Unity OnControllerColliderHit检测到碰撞但角色瞬移失效问题
Unity CharacterController碰撞触发瞬移失效问题
问题表现
- 开发需求:实现角色触碰指定立方体时触发瞬移功能
- 异常现象:控制台可正常输出碰撞检测命中的日志,但角色位置未按代码逻辑更新,瞬移完全失效
- 已验证情况:近似逻辑在其他场景可正常运行;将瞬移逻辑拆分到独立脚本挂载后问题仍复现
- 对照测试结果:相近逻辑脚本中,命中
trigger对象时可正常修改位置、打印日志并退出应用;命中entrance对象时仅打印瞬移日志,角色位置同样不更新
复现代码
瞬移功能测试脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class othercollision : MonoBehaviour { void OnControllerColliderHit(ControllerColliderHit hit) { if (hit.gameObject.name == "teleportcube") { gameObject.transform.position = new Vector3(-3184.53f, 20.35f, -171.585f); Debug.Log("Collision detected"); } } }
对照参考脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; public class detectcollision : MonoBehaviour { public GameObject tplocation1; public GameObject player; public GameObject entranceloc; void OnControllerColliderHit(ControllerColliderHit hit) { // 该分支可正常修改位置、打印日志后退出 if (hit.gameObject.name == "trigger") { Debug.Log("triggered"); gameObject.transform.position = tplocation1.transform.position; Thread.Sleep(7); Application.Quit(); } // 该分支仅打印日志,位置不更新 if (hit.gameObject.name == "entrance") { gameObject.transform.position = new Vector3(-3184.53f, 20.35f, -171.585f); Debug.Log("teleporting..."); } } }
问题根因
OnControllerColliderHit是CharacterController执行Move/SimpleMove移动逻辑过程中触发的回调,在回调内直接修改transform.position后,CharacterController当帧未执行完的移动解算、碰撞贴靠修正逻辑会直接覆盖你手动设置的坐标,最终表现为瞬移失效。
参考脚本里trigger分支看似正常,本质是设置完坐标后立刻调用Application.Quit()终止了程序,CharacterController没机会执行后续的位置覆盖步骤,不是写法本身正确。
修复方案
不要在OnControllerColliderHit回调内直接修改坐标,用布尔值标记需要瞬移的状态,等到LateUpdate阶段(所有物理计算、CharacterController移动逻辑全部执行完成后)再更新位置,示例代码如下:
using UnityEngine; public class TeleportFix : MonoBehaviour { private bool _needTeleport = false; private Vector3 _targetPos; private CharacterController _controller; public GameObject tplocation1; void Start() { _controller = GetComponent<CharacterController>(); } void OnControllerColliderHit(ControllerColliderHit hit) { if (hit.gameObject.name == "teleportcube" || hit.gameObject.name == "entrance") { _needTeleport = true; _targetPos = new Vector3(-3184.53f, 20.35f, -171.585f); Debug.Log("Hit teleport trigger"); } if (hit.gameObject.name == "trigger") { Debug.Log("triggered"); _needTeleport = true; _targetPos = tplocation1.transform.position; // 不要在主线程用Thread.Sleep,会卡死程序 Invoke(nameof(QuitGame), 0.01f); } } void LateUpdate() { if (_needTeleport) { // 瞬移前临时关闭Controller避免位置被覆盖 _controller.enabled = false; transform.position = _targetPos; _controller.enabled = true; _needTeleport = false; } } void QuitGame() { Application.Quit(); } }
额外注意:不要在Unity主线程调用Thread.Sleep(),会直接阻塞整个游戏进程导致卡顿。
内容的提问来源于stack exchange,提问作者dbubbis
相关产品推荐
相关产品推荐

