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

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

核心问题排查方向

  1. currentObject引用丢失
    拾取物品后,Update方法中的Raycast因物品被SetActive(false)无法检测到,会将currentObject设为null,导致碰撞垃圾桶时无有效引用。
    修复:新增private GameObject heldItem变量单独存储持有物品,拾取时将currentObject赋值给heldItem,碰撞逻辑改用heldItem判断。

  2. 碰撞触发逻辑错误
    角色使用Character Controller时,OnCollisionEnter不会被触发,需替换为OnControllerColliderHit;或给角色添加额外的非触发器Collider并勾选Is Kinematic。

  3. 标签匹配与状态判断缺失

    • 检查垃圾桶标签是否与代码中TrashbinG完全一致(Unity标签大小写敏感)
    • 在OnCollisionEnter开头添加if (!holdingItem) return;,避免无物品时触发无效逻辑
  4. 协程执行顺序问题
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:30:30