Unity C# 带碰撞体的鼠标3D指针自身碰撞导致位置异常如何解决
3D指针跳变问题修复方案
问题根源
跳变问题本质是每帧从相机发射的射线优先命中了指针自身的碰撞体,导致指针位置反复在自身表面和目标物体表面来回切换。
可选修复方案
方案1:层级过滤(最推荐,性能最优)
该方案通过层级掩码让射线检测直接忽略指针所属层级,改动最小且运行效率最高:
- 在Unity编辑器中新建单独层级,命名为
3D_Pointer,将指针物体的层级设置为该层级 - 修改原有代码,在
Physics.Raycast调用时传入排除该层级的掩码
public class MousePosition3D : MonoBehaviour { [SerializeField] private Camera mainCamera; // 可在Inspector面板直接选择你新建的3D_Pointer层级 [SerializeField] private LayerMask pointerLayer; [SerializeField] private float defaultPointerDistance = 5f; private void Update() { // 若需要对齐屏幕中心,替换Input.mousePosition为下方注释的屏幕中心坐标 // Vector3 screenCenter = new Vector3(Screen.width/2f, Screen.height/2f, 0f); Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); // 对层级掩码取反,即检测除指针层外的所有层级 if (Physics.Raycast(ray, out RaycastHit raycastHit, float.MaxValue, ~pointerLayer)) { transform.position = raycastHit.point; } // 可选优化:无命中物体时将指针放在相机前方固定距离位置,避免指针消失 else { transform.position = mainCamera.transform.position + mainCamera.transform.forward * defaultPointerDistance; } } }
方案2:代码过滤自身碰撞体(无需新增层级)
如果不想额外新增层级,可以直接在射线命中结果中过滤掉自身的碰撞体:
public class MousePosition3D : MonoBehaviour { [SerializeField] private Camera mainCamera; [SerializeField] private float defaultPointerDistance = 5f; private void Update() { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); RaycastHit[] allHits = Physics.RaycastAll(ray); // 按命中距离从小到大排序 System.Array.Sort(allHits, (a, b) => a.distance.CompareTo(b.distance)); foreach (var hit in allHits) { // 跳过指针自身的碰撞体,取第一个有效命中点 if(hit.collider.gameObject == gameObject) continue; transform.position = hit.point; return; } // 无有效命中时使用默认位置 transform.position = mainCamera.transform.position + mainCamera.transform.forward * defaultPointerDistance; } }
屏幕中心对齐适配
如果需要实现对齐屏幕中心的3D指针,只需要将射线生成的输入参数从Input.mousePosition替换为屏幕中心坐标即可:
Vector3 screenCenter = new Vector3(Screen.width/2f, Screen.height/2f, 0f); Ray ray = mainCamera.ScreenPointToRay(screenCenter);
内容的提问来源于stack exchange,提问作者Rafał Gruba
相关产品推荐
相关产品推荐

