Unity按空格键实现玩家与虚拟人偶位置交换偶现执行异常问题
玩家与人偶位置交换功能异常修复方案
异常根本原因
- 执行顺序逻辑错位:原代码在
Update中按键检测前就每帧执行SwapTransforms(),完全偏离了「记录交换前位置快照」的设计目的。本该在触发交换的瞬间、交换动作执行前记录两个对象的原始位置,实际实现却每帧用实时位置覆盖快照值,一旦物理引擎因重叠产生推挤位移,错误的推挤后位置会被写入快照,直接导致后续交换目标错乱。 - 赋值时序触发物理冲突:交换时逐对象设置位置的间隙,物理引擎会检测到刚体重叠,立刻施加推力将两个对象分离,直接复现了「人偶先瞬移到玩家位置、玩家再移动到目标点、最终位置重叠互相推挤」的异常表现。
- 快照逻辑无隔离:原实现每帧根据实时位置刷新记录值,没有和「交换触发动作」做绑定,只要两个对象位置因任何原因发生变动,就会被写入记录位,无法保证交换时获取的是无推挤污染的原始位置,因此会出现偶发异常。
修复后可运行代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerSwap : MonoBehaviour { public GameObject player; public GameObject dummy; public GameObject cameraAnchor; public bool canSwap; public float swapRadius; public LayerMask dummyInRadius; void Update() { canSwap = Physics.CheckSphere(player.transform.position, swapRadius, dummyInRadius); if (canSwap && Input.GetKeyDown(KeyCode.Space)) { SwapPosition(); } else if (!canSwap) { Debug.Log("Can't Swap"); } cameraAnchor.transform.position = player.transform.position; } private void SwapPosition() { // 触发交换瞬间存储两个对象的原始位置快照,完全隔离后续物理推挤的影响 Vector3 playerOriginalPos = player.transform.position; Vector3 dummyOriginalPos = dummy.transform.position; // 临时禁用碰撞体,避免赋值过程触发重叠推挤 Collider playerCol = player.GetComponent<Collider>(); Collider dummyCol = dummy.GetComponent<Collider>(); if (playerCol) playerCol.enabled = false; if (dummyCol) dummyCol.enabled = false; // 基于快照直接赋值,不再依赖外部挂载的记录位Transform player.transform.position = dummyOriginalPos; dummy.transform.position = playerOriginalPos; // 赋值完成后恢复碰撞 if (playerCol) playerCol.enabled = true; if (dummyCol) dummyCol.enabled = true; } public void OnDrawGizmos() { Gizmos.color = Color.white; Gizmos.DrawWireSphere(player.transform.position, swapRadius); } }
修复后效果
- 每次按下空格键时,会一次性读取两个对象的当前真实位置生成快照,后续所有赋值操作都基于这个快照完成,不会被中途的物理推挤干扰,从根源上避免偶发的位置错乱。
- 赋值过程中临时关闭碰撞体,完全杜绝逐对象赋值时物理引擎检测重叠推挤的问题,不会出现位置重叠、互相推动的异常表现。
- 移除了原代码中冗余的
haveSwapped状态标记、每帧无效调用的SwapTransforms()、需要手动拖拽绑定的记录位Transform,逻辑链路更短,状态同步无延迟,每次按键都能稳定实现两者位置来回切换的需求。
内容的提问来源于stack exchange,提问作者Somiaz
相关产品推荐
相关产品推荐

