Unity修改库存ScriptableObject数据时同步改动源资产的问题排查
问题原因
你的判断完全正确,核心问题就是库存列表直接存储了原始ScriptableObject资产的引用,没有做数据隔离。
- ScriptableObject是引用类型的项目资源,你传入
AddItem的参数本身就是指向磁盘上.asset文件的直接引用,不是独立的数据副本。 - 后续
RemoveItem里修改searchedItem.amount时,改动直接作用在原始资源上,Unity触发资源序列化后,改动就会永久写入资产文件,就出现了你看到的源文件数值被改动的现象。 - 顺带提一句你现有
AddItem的堆叠逻辑有bug:遍历库存列表时,只要当前遍历项和新物品不重名就会直接插入新条目,会导致同一个可堆叠物品在库存里重复生成多条记录。
修复方案
核心思路是把静态配置数据和运行时库存数据做隔离,不要直接修改原始资产,推荐两种实现:
方案1:拆分配置类和运行时库存条目(推荐)
这是Unity项目里的标准实现方式,结构清晰也不容易出资源修改的问题:
- 把原有
Item类改成纯静态配置类,删掉所有运行时会变动的字段(比如amount),只保留物品名、类型、描述、是否可堆叠、图标这类固定不变的配置属性。 - 新建普通C#类作为运行时库存条目,专门存动态变动的数据:
[System.Serializable] public class InventoryEntry { public Item config; // 只读,指向静态配置资产,永远不修改 public int count; // 运行时变动的数量存在这里 }
- 把库存列表类型改为
List<InventoryEntry>,重写增删逻辑:
private List<InventoryEntry> itemList = new List<InventoryEntry>(); public void AddItem(Item targetItem, int addCount = 1) { if (targetItem.isStackable) { InventoryEntry existEntry = itemList.Find(entry => entry.config.name == targetItem.name); if (existEntry != null) { existEntry.count += addCount; return; } } // 没有可堆叠的现有条目就新建运行时实例 itemList.Add(new InventoryEntry { config = targetItem, count = addCount }); } public void RemoveItem(Item targetItem, int removeCount = 1) { InventoryEntry existEntry = itemList.Find(entry => entry.config.name == targetItem.name); if (existEntry == null) return; existEntry.count -= removeCount; if (existEntry.count <= 0) { itemList.Remove(existEntry); } }
这种实现下所有动态修改都发生在运行时生成的普通类实例上,完全不会触碰原始资产文件,也不用额外处理资源销毁的问题。
方案2:运行时复制ScriptableObject资产(快速修复)
如果不想改动现有数据结构,可以在物品入库存时调用Instantiate创建一份原始资产的运行时副本,后续所有修改都在副本上操作,和原始资产解耦:
public void AddItem(Item item) { // 创建运行时副本,脱离原始资产引用 Item runtimeItem = Instantiate(item); if (!runtimeItem.isStackable || itemList.Count == 0) { itemList.Add(runtimeItem); return; } Item existItem = itemList.Find(i => i.name == runtimeItem.name); if (existItem != null) { existItem.amount += runtimeItem.amount; Destroy(runtimeItem); // 销毁临时副本避免内存泄漏 return; } itemList.Add(runtimeItem); }
注意这种方式要在物品移除、场景卸载时手动Destroy掉运行时生成的ScriptableObject副本,否则会造成内存残留,适合小项目快速修复问题用。
内容的提问来源于stack exchange,提问作者Suspense
相关产品推荐
相关产品推荐

