Unity中如何实现可存储int、string、Sprite的物品清单?
问题分析与解决方案
核心问题
- 初始代码直接存储
ItemData(ScriptableObject)实例,所有背包条目共享同一份SO数据,修改任一条目数量会影响原SO,且Inspector仅显示SO引用,无法直观查看每个条目的具体数据。 - 修改后的代码错误地使用
new ItemData()创建ScriptableObject实例(Unity禁止直接new SO,必须通过ScriptableObject.CreateInstance),且创建的物品未添加到items列表,导致数据丢失。
正确实现方案
我们需要区分物品模板和背包物品实例:用ItemData SO存储物品的基础属性(名称、图标),再创建一个可序列化的普通类来存储背包中每个物品的实例数据(包含SO模板引用+数量),这样既复用SO的配置,又能独立管理每个背包条目的数量,同时让Inspector正常显示所有数据。
1. 定义物品模板与背包条目类
using System.Collections.Generic; using UnityEngine; // 物品模板:用ScriptableObject存储基础属性,可在Project窗口创建配置文件 [CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Item")] public class ItemData : ScriptableObject { public string itemName; public Sprite itemIcon; // 移除amountOfItem,因为数量是背包实例的属性,不是模板属性 } // 背包条目:可序列化的普通类,存储每个物品的实例数据 [System.Serializable] public class InventoryItem { public ItemData itemTemplate; // 引用物品模板 public int quantity; // 当前背包中该物品的数量 }
2. 修改Inventory脚本实现拾取逻辑
public class Inventory : MonoBehaviour { public List<InventoryItem> items = new List<InventoryItem>(); private void OnTriggerEnter(Collider other) { if (other.CompareTag("Item")) { // 从拾取物上获取物品配置 PickupItem pickup = other.GetComponent<PickupItem>(); if (pickup != null && pickup.itemTemplate != null) { AddItemToInventory(pickup.itemTemplate, pickup.quantity); Destroy(other.gameObject); } } } // 添加物品到背包:存在同类型则叠加数量,否则新增条目 private void AddItemToInventory(ItemData itemTemplate, int addQuantity) { // 检查背包中是否已有该物品 InventoryItem existingItem = items.Find(item => item.itemTemplate == itemTemplate); if (existingItem != null) { existingItem.quantity += addQuantity; } else { // 创建新的背包条目并添加到列表 InventoryItem newItem = new InventoryItem(); newItem.itemTemplate = itemTemplate; newItem.quantity = addQuantity; items.Add(newItem); } } }
3. 新增PickupItem脚本挂载到拾取物上
using UnityEngine; // 挂载在场景中的拾取物上,配置要拾取的物品模板和数量 public class PickupItem : MonoBehaviour { public ItemData itemTemplate; public int quantity = 1; // 拾取该物品时获得的数量,默认为1 }
方案优势
- 数据解耦:物品模板(SO)和背包实例数据分离,修改背包数量不会影响原SO配置。
- Inspector可视化:
List<InventoryItem>会在Inspector中展开显示每个条目的物品模板和数量,方便调试和编辑。 - 扩展性强:后续添加物品消耗、堆叠上限等功能时,直接修改
InventoryItem和AddItemToInventory逻辑即可。
为什么之前的代码出错
- 直接存储SO实例:所有背包条目共享同一份SO数据,修改数量会同步到Project窗口中的SO文件,导致数据混乱。
- 用
new ItemData()创建SO:Unity中ScriptableObject必须通过ScriptableObject.CreateInstance<ItemData>()创建,直接new会导致对象无法被Unity正确管理,出现参数异常。
内容的提问来源于stack exchange,提问作者BeeCode
相关产品推荐
相关产品推荐

