You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 10:06:02