Unity垃圾分类关卡问题:拾取物品后无法触发垃圾桶销毁逻辑
垃圾分类关卡碰撞销毁问题排查请求
我正在制作Unity垃圾分类关卡,玩家通过方向键移动、E键拾取垃圾,需将垃圾送至对应垃圾桶完成销毁。目前仅能正常拾取物品,但角色碰撞对应垃圾桶时,无法触发物品销毁操作,调试无果,请求协助排查。
附脚本代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; public class PickupItem : MonoBehaviour { public float pickupRange = 2f; public LayerMask pickupLayer; public AudioClip pickupSound; public string[] pickupTags; public AudioClip wrongBinSound; public string[] trashBinTags; public TextMeshProUGUI itemNameText; private AudioSource audioSource; private GameObject currentObject; private bool holdingItem = false; private void Start() { audioSource = GetComponent<AudioSource>(); } private void Update() { RaycastHit hit; if (Physics.Raycast(transform.position, transform.forward, out hit, pickupRange, pickupLayer)) { foreach (string tag in pickupTags) { if (hit.collider.tag == tag && !holdingItem) { currentObject = hit.collider.gameObject; if (Input.GetKeyDown(KeyCode.E)) { StartCoroutine(Pickup()); } break; } } } else { currentObject = null; } } private void OnCollisionEnter(Collision collision) { foreach (string trashBinTag in trashBinTags) { if (collision.gameObject.tag == trashBinTag) { switch (currentObject.tag) { case "paper": if (trashBinTag == "TrashbinPa") { Debug.Log("paper in vuilbak"); audioSource.PlayOneShot(pickupSound); itemNameText.text = ""; holdingItem = false; } break; case "glass": if (trashBinTag == "TrashbinG") { Debug.Log("glass in vuilbak"); audioSource.PlayOneShot(pickupSound); Destroy(currentObject); itemNameText.text = ""; holdingItem = false; } break; case "metal": if (trashBinTag == "TrashbinM") { Debug.Log("metal in vuilbak"); audioSource.PlayOneShot(pickupSound); Destroy(currentObject); itemNameText.text = ""; holdingItem = false; } break; case "plastic": if (trashBinTag == "TrashbinP") { Debug.Log("plastic in vuilbak"); audioSource.PlayOneShot(pickupSound); Destroy(currentObject); itemNameText.text = ""; holdingItem = false; } break; default: audioSource.PlayOneShot(wrongBinSound); break; } break; } } } IEnumerator Pickup() { if (currentObject != null) { Debug.Log("Object picked up"); yield return new WaitForSeconds(1); audioSource.PlayOneShot(pickupSound); currentObject.SetActive(false); itemNameText.text = "Inventory: " + currentObject.name; holdingItem = true; } } }
场景对象设置
- 角色设置:带有Character Controller组件,碰撞体已启用
- 玻璃垃圾桶设置:带有Box Collider(非触发器),标签为
TrashbinG - 酒瓶垃圾设置:带有Mesh Collider,标签为
glass,属于可拾取Layer
核心问题排查方向
currentObject引用丢失
拾取物品后,Update方法中的Raycast因物品被SetActive(false)无法检测到,会将currentObject设为null,导致碰撞垃圾桶时无有效引用。
修复:新增private GameObject heldItem变量单独存储持有物品,拾取时将currentObject赋值给heldItem,碰撞逻辑改用heldItem判断。碰撞触发逻辑错误
角色使用Character Controller时,OnCollisionEnter不会被触发,需替换为OnControllerColliderHit;或给角色添加额外的非触发器Collider并勾选Is Kinematic。标签匹配与状态判断缺失
- 检查垃圾桶标签是否与代码中
TrashbinG完全一致(Unity标签大小写敏感) - 在
OnCollisionEnter开头添加if (!holdingItem) return;,避免无物品时触发无效逻辑
- 检查垃圾桶标签是否与代码中
协程执行顺序问题
Pickup协程的1秒延迟期间,若玩家移动导致Raycast丢失,currentObject会被清空。可将currentObject的引用保存逻辑移到协程内部,避免外部修改。
修改后关键代码示例
private GameObject heldItem; // 新增:单独存储持有的物品 private void Update() { RaycastHit hit; if (Physics.Raycast(transform.position, transform.forward, out hit, pickupRange, pickupLayer) && !holdingItem) { foreach (string tag in pickupTags) { if (hit.collider.tag == tag) { currentObject = hit.collider.gameObject; if (Input.GetKeyDown(KeyCode.E)) { StartCoroutine(Pickup()); } break; } } } // 移除else分支的currentObject = null,避免拾取后丢失引用 } IEnumerator Pickup() { if (currentObject != null) { Debug.Log("Object picked up"); yield return new WaitForSeconds(1); audioSource.PlayOneShot(pickupSound); currentObject.SetActive(false); heldItem = currentObject; // 保存持有物品引用 itemNameText.text = "Inventory: " + currentObject.name; holdingItem = true; currentObject = null; // 清空临时变量 } } // 替换OnCollisionEnter为OnControllerColliderHit适配Character Controller private void OnControllerColliderHit(ControllerColliderHit hit) { if (!holdingItem || heldItem == null) return; foreach (string trashBinTag in trashBinTags) { if (hit.gameObject.tag == trashBinTag) { switch (heldItem.tag) { case "glass": if (trashBinTag == "TrashbinG") { Debug.Log("glass in vuilbak"); audioSource.PlayOneShot(pickupSound); Destroy(heldItem); itemNameText.text = ""; holdingItem = false; heldItem = null; } else { audioSource.PlayOneShot(wrongBinSound); } break; // 其他垃圾类型case同理修改 default: audioSource.PlayOneShot(wrongBinSound); break; } break; } } }
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

