如何在有障碍物遮挡时阻止Raycast拾取物体?
Raycast拾取物体穿透障碍物的修复方案
问题现状
使用Raycast实现物体拾取功能时,玩家直视可拾取物体准星变红表示可拾取,但存在穿透障碍物拾取的问题:即使Raycast与可拾取物体之间有遮挡(如关闭的抽屉),物体仍能被触发拾取,不符合"仅直接命中暴露物体才可拾取"的预期。
问题根源
- Raycast未限制检测层,可能穿透了障碍物的碰撞体
- 未验证Raycast命中的第一个对象是否为可拾取物体,只要射线能触碰到可拾取物体(哪怕中间有遮挡)就触发拾取
修复步骤
1. 配置Raycast层掩码
在Unity编辑器中创建并分配对应层(如Pickable层放可拾取物体,Obstacle层放障碍物),然后在代码中定义层掩码,让Raycast只与这些层的对象交互。
2. 修改Raycast检测逻辑
确保只有当Raycast命中的第一个对象带有canPickUp_组件时,才触发拾取逻辑;若命中障碍物(无该组件),则不执行拾取并恢复准星状态。
3. 检查障碍物碰撞体设置
确保障碍物的碰撞体处于启用状态,若使用Mesh Collider需勾选Convex,否则Raycast无法正确检测到遮挡。
修改后的代码示例
// 在类中添加层掩码变量,需在Unity编辑器中配置 public LayerMask pickableAndObstacleLayers; public float pickUpRange; public Transform holdPos; public GameObject player; public Image reticle; // 准星UI private GameObject heldObj; private Rigidbody heldObjRb; private List<GameObject> targetObjectsList = new List<GameObject>(); if (PickingUp) { if (heldObj == null) // 当前未持有物体 { RaycastHit hit; // 发射射线,仅检测指定层的对象 if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, pickUpRange, pickableAndObstacleLayers)) { var pickUpComponent = hit.transform.GetComponent<canPickUp_>(); if (pickUpComponent != null) { Debug.Log("准星应变红"); // 准星变红逻辑 reticle.color = Color.red; if (hit.transform.CompareTag("TargetObj") && !targetObjectsList.Contains(hit.transform.gameObject)) { targetObjectsList.Add(hit.transform.gameObject); } PickUpObject(hit.transform.gameObject); } else { // 命中障碍物,恢复准星默认颜色 reticle.color = new Color(1, 1, 1, 0.75f); } } else { // 未命中任何对象,恢复准星默认颜色 reticle.color = new Color(1, 1, 1, 0.75f); } } else { if (canDrop == true) { StopClipping(); // 防止物体穿墙 DropObject(); } } } private void PickUpObject(GameObject pickUpObj) { if (pickUpObj.GetComponent<Rigidbody>()) { heldObj = pickUpObj; heldObjRb = pickUpObj.GetComponent<Rigidbody>(); heldObjRb.isKinematic = true; heldObjRb.transform.parent = holdPos.transform; heldObj.layer = LayerNumber; Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), true); reticle.enabled = false; } } void DropObject() { Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), false); heldObj.layer = 0; heldObjRb.isKinematic = false; heldObj.transform.parent = null; heldObj = null; reticle.color = new Color(1, 1, 1, 0.75f); reticle.enabled = true; }
关键说明
- 层掩码
pickableAndObstacleLayers需在编辑器中勾选Pickable和Obstacle层,确保Raycast不会穿透无关对象 - 修改后的逻辑优先检测第一个命中对象,彻底避免了"射线穿透障碍物触发拾取"的问题
- 补充了准星颜色的状态切换,确保只有命中可拾取物体时才变红
内容的提问来源于stack exchange,提问作者SSS
相关产品推荐
相关产品推荐

