如何正确使用Unity VR Toolkit的Raycast功能实现物体移动
问题1:移动穿模解决方案
- 直接修改
transform.position属于坐标瞬移,会跳过物理引擎的碰撞检测逻辑,是穿模的核心原因。将位置赋值逻辑替换为刚体的Rigidbody.MovePosition()方法,该方法会在物理更新阶段自动触发碰撞检测,避免穿模。 - 拖拽过程中临时修改刚体属性:选中物体时将
useGravity设为false,isKinematic设为false,退出选中时恢复原有属性,保证物理交互正常。 - 可选增强:在更新位置前调用
Physics.OverlapBox检测目标位置是否存在其他碰撞体,若存在则沿碰撞法线偏移到合法位置,进一步降低穿模概率。
问题2:无法拉远物体解决方案
- 当前逻辑仅在射线命中场景碰撞体时更新落点,当射线前方无障碍物时无法更新落点位置,导致不能拉远物体。修改逻辑为:选中物体时记录控制器到物体的初始距离,当射线未命中场景碰撞体时,用「控制器位置 + 射线正方向 × 初始距离」作为目标落点,即可实现无障碍物时的自由推拉。
- 可选增强:绑定控制器摇杆输入动态调整这个距离,适配不同操作习惯。
修改后核心代码示例
DragObject 脚本修改
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; public class DragObject : XRGrabInteractable { [SerializeField] XRRayInteractor ray; private Rigidbody rb; private Vector3 mOffset; private float initialDistance; private bool originalUseGravity; private bool originalIsKinematic; private ControllerPosition controllerPosition = null; protected override void Awake() { base.Awake(); rb = GetComponent<Rigidbody>(); } protected override void OnSelectEntered(SelectEnterEventArgs args) { base.OnSelectEntered(args); controllerPosition = args.interactor.GetComponent<ControllerPosition>(); // 记录初始属性和距离 originalUseGravity = rb.useGravity; originalIsKinematic = rb.isKinematic; rb.useGravity = false; rb.isKinematic = false; initialDistance = Vector3.Distance(controllerPosition.transform.position, transform.position); mOffset = gameObject.transform.position - controllerPosition.rayHitPosition; } protected override void OnSelectExited(SelectExitEventArgs args) { base.OnSelectExited(args); // 恢复原有刚体属性 rb.useGravity = originalUseGravity; rb.isKinematic = originalIsKinematic; controllerPosition = null; } public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase updatePhase) { base.ProcessInteractable(updatePhase); if (isSelected && updatePhase == XRInteractionUpdateOrder.UpdatePhase.Fixed) { UpdateObjectPosition(); } } private void UpdateObjectPosition() { if (controllerPosition == null) return; Vector3 targetHitPos; // 判断射线是否命中场景碰撞体,可新增Layer判断过滤自身碰撞体 if (ray.TryGetHitInfo(out var hitPos, out var hitNormal, out _, out _)) { targetHitPos = hitPos; // 命中时更新距离,保证推拉逻辑连贯 initialDistance = Vector3.Distance(controllerPosition.transform.position, hitPos); } else { // 未命中时用初始距离计算落点 targetHitPos = controllerPosition.transform.position + ray.rayDirection * initialDistance; } // 用MovePosition代替直接改position避免穿模 rb.MovePosition(targetHitPos + mOffset); } }
内容的提问来源于stack exchange,提问作者harrY
相关产品推荐
相关产品推荐

