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

如何避免玩家拾取脚下可交互物体时出现浮空问题?

物体拾取时玩家被顶起的问题解决

问题背景

我实现的物体拾取功能,是让被拾取物体悬浮在屏幕中央、镜头前方的指定位置,核心代码如下:

raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.Raycast(raycastPos, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
    lookObject = hit.collider.transform.gameObject;
    reticle.color = new Color(1, 0, 0, 0.75f);

}
else
{
    lookObject = null;
    reticle.color = new Color(1, 1, 1, 0.75f);
}
if (PickingUp)
{
    if (currentlyPickedUpObject == null)
    {
        if (lookObject != null)
        {
            if (hit.distance >= minDistance)
            {
                PickUpObject();
            } 
         }
     }
}
public void PickUpObject()
{
    physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();

    currentlyPickedUpObject = lookObject;

    // currPicked = true;  

    pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();

    pickupRB.constraints = RigidbodyConstraints.FreezeRotation;

    physicsObject.playerInteractions = this;
}

当前遇到的异常:当玩家站在可拾取物体上方并拾取时,玩家会被物体顶起浮空,场景中可明显看到该问题。

解决方法

问题根源是拾取后物体的碰撞体仍与玩家碰撞体产生物理交互,导致物体位置更新时推开玩家。以下是几种直接可行的解决方案:

方案1:临时禁用物体碰撞体

拾取时禁用物体的Collider,放下时恢复,避免物理交互:

private Collider pickupCollider; // 新增变量存储碰撞体

public void PickUpObject()
{
    // 原有代码保留
    physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
    currentlyPickedUpObject = lookObject;
    pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
    pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
    physicsObject.playerInteractions = this;

    // 新增禁用碰撞体逻辑
    pickupCollider = currentlyPickedUpObject.GetComponent<Collider>();
    if(pickupCollider != null)
        pickupCollider.enabled = false;
}

// 对应放下物体的方法中添加恢复逻辑
public void DropObject()
{
    // 原有放下逻辑(比如清空引用、解除刚体约束等)
    currentlyPickedUpObject = null;
    if(pickupRB != null)
        pickupRB.constraints = RigidbodyConstraints.None;
    
    // 恢复碰撞体
    if(pickupCollider != null)
        pickupCollider.enabled = true;
}

方案2:切换物体碰撞层

创建一个与玩家层无碰撞交互的专属层,拾取时切换物体到该层,放下时切回原层:

  1. 在Unity编辑器中新增PickedUp层,然后在Edit > Project Settings > Physics中设置该层与玩家层的碰撞为忽略。
  2. 修改代码:
private int originalObjectLayer; // 存储物体原层

public void PickUpObject()
{
    // 原有代码保留
    physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
    currentlyPickedUpObject = lookObject;
    pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
    pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
    physicsObject.playerInteractions = this;

    // 切换碰撞层
    originalObjectLayer = currentlyPickedUpObject.layer;
    currentlyPickedUpObject.layer = LayerMask.NameToLayer("PickedUp");
}

public void DropObject()
{
    // 原有放下逻辑
    currentlyPickedUpObject = null;
    if(pickupRB != null)
        pickupRB.constraints = RigidbodyConstraints.None;
    
    // 恢复原层
    if(currentlyPickedUpObject != null)
        currentlyPickedUpObject.layer = originalObjectLayer;
}

方案3:设置刚体为运动学(Kinematic)

拾取时将物体的Rigidbody设为Kinematic,使其脱离物理引擎控制,不再产生物理碰撞:

public void PickUpObject()
{
    // 原有代码保留
    physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
    currentlyPickedUpObject = lookObject;
    pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
    pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
    physicsObject.playerInteractions = this;

    // 设置为运动学刚体
    pickupRB.isKinematic = true;
}

public void DropObject()
{
    // 原有放下逻辑
    currentlyPickedUpObject = null;
    if(pickupRB != null)
    {
        pickupRB.constraints = RigidbodyConstraints.None;
        // 恢复物理刚体
        pickupRB.isKinematic = false;
    }
}

内容的提问来源于stack exchange,提问作者Okashi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:57:16