如何避免玩家拾取脚下可交互物体时出现浮空问题?
物体拾取时玩家被顶起的问题解决
问题背景
我实现的物体拾取功能,是让被拾取物体悬浮在屏幕中央、镜头前方的指定位置,核心代码如下:
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:切换物体碰撞层
创建一个与玩家层无碰撞交互的专属层,拾取时切换物体到该层,放下时切回原层:
- 在Unity编辑器中新增
PickedUp层,然后在Edit > Project Settings > Physics中设置该层与玩家层的碰撞为忽略。 - 修改代码:
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
相关产品推荐
相关产品推荐

