Unity3D问题:物品已在Inventory中找到,但GetItem()仍返回null
问题原因与解决方案
核心问题
你的Item类继承了MonoBehaviour,但在拾取物品时用new Item(itemName, itemPrefab)创建实例——这是完全错误的。Unity中的MonoBehaviour组件不能通过new关键字实例化,这样创建的对象没有绑定到任何GameObject,属于Unity生命周期外的无效实例。虽然它的getName()方法能返回正确值,但后续引用时会被判定为null,这就是你看到日志同时输出“Apple found in inventory”和“Apple is null”的原因。
另外,Inventory类的Update方法每帧都订阅InventoryUpdated事件,会导致事件被重复订阅,每次触发时UpdateInventoryDisplay会执行多次,也需要修复。
分步解决方案
1. 将Item改为普通数据类
把Item从MonoBehaviour组件改为纯数据类,因为物品的基础数据不需要依赖Unity的组件生命周期:
public class Item { public string itemName; public GameObject itemPrefab; public Item(string name, GameObject prefab) { itemName = name; itemPrefab = prefab; } public override bool Equals(object obj) { if (obj is Item other) { return itemName.Equals(other.itemName); } return false; } public override int GetHashCode() { return itemName.GetHashCode(); } public string getName() { return itemName; } }
2. 创建单独的拾取逻辑组件
新建一个ItemPickup类,负责处理物品的显示、旋转和拾取逻辑,挂载在物品的GameObject上:
public class ItemPickup : MonoBehaviour { public Item itemData; // 在Inspector面板中赋值对应的Item数据对象 void Update() { transform.Rotate(0, 0.25f, 0, Space.Self); } private void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) { Inventory inventory = other.GetComponent<Inventory>(); if (inventory != null) { // 传入有效的Item实例 inventory.Add(itemData); Destroy(gameObject); } } } }
3. 修复Inventory的事件订阅问题
把InventoryUpdated事件的订阅移到Start方法中,避免每帧重复订阅:
public class Inventory : MonoBehaviour { public Dictionary<Item, int> inventory = new Dictionary<Item, int>(); public TextMeshProUGUI inventoryText; public static event Action InventoryUpdated; void Start() { // 仅订阅一次事件 InventoryUpdated += UpdateInventoryDisplay; } void Update() { UpdateInventoryDisplay(); } private void UpdateInventoryDisplay() { inventoryText.text = ""; foreach (var item in inventory) { inventoryText.text += $"{item.Key.getName()}: {item.Value}\n"; } } public void Add(Item item){ if (inventory.ContainsKey(item)){ inventory[item] += 1; } else { inventory.Add(item, 1); } InventoryUpdated?.Invoke(); } public void Use(Item item, int amount){ if (inventory.ContainsKey(item)){ inventory[item] -= amount; if (inventory[item] <= 0){ inventory.Remove(item); } } InventoryUpdated?.Invoke(); } public Item GetItem(string key) { foreach (var kvp in inventory) { Item item = kvp.Key; if (item.getName().Equals(key)){ Debug.Log(item.getName() + " found in inventory"); return item; } } Debug.Log(key + " not found"); return null; } }
4. 调整CookingSystem(无需修改代码)
现在GetItem方法返回的是有效的Item实例,CookingSystem中的判断会正常工作,不会再输出“Apple is null”。
验证方法
- 在Unity中创建一个普通的Item对象,赋值给
ItemPickup的itemData字段。 - 运行游戏,拾取苹果后,日志只会输出“Apple found in inventory”,不会再出现“Apple is null”的错误。
内容的提问来源于stack exchange,提问作者user29031318
相关产品推荐
相关产品推荐

