Unity Scriptable Object创建疑问:能否用new?及代码解析
Unity Scriptable Object 常见问题与代码解析
核心疑问
- 能否使用
new关键字创建Scriptable Object? - 若不能,正确用法是什么?
- 为什么自己写的用
new的代码能运行? - 如何正确使用
CreateInstance?
玩家触碰检测脚本(Player)
public class Player : MonoBehaviour { public Inventory inventory; private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Item")) { int itemId = collision.GetComponent<ItemWorld>().item.id; string itemName = collision.GetComponent<ItemWorld>().item.name; int itemAmount = collision.GetComponent<ItemWorld>().item.amount; inventory.AddItem(itemId,itemName,itemAmount); Destroy(collision.gameObject); } } }
物品属性存储脚本(Item,继承ScriptableObject)
[CreateAssetMenu(fileName = "Item", menuName ="ItemSystem/BasicItem")] public class Item : ScriptableObject { public int id; public string name; public int amount; public Item(int _id,string _name,int _amount) { this.id = _id; this.name = _name; this.amount = _amount; } }
物品添加逻辑脚本(Inventory)
public class Inventory : MonoBehaviour { public List<Item> InventoryList = new List<Item>(); public void AddItem(int _itemId,string _itemName, int _itemAmount) { Item newItem = new Item(_itemId,_itemName,_itemAmount); InventoryList.Add(newItem); } }
问题解答
1. 能不能用new创建Scriptable Object?
绝对不能。Scriptable Object是Unity专属的资源对象,依赖Unity的序列化系统、资源管理体系运行。用new创建的只是一个继承了ScriptableObject的普通C#类实例,完全不具备Scriptable Object的核心特性。
2. 你的代码为什么能运行?
C#语法允许调用自定义构造函数创建类实例,哪怕这个类继承自ScriptableObject。但这种对象本质是纯C#对象,和Unity的Scriptable Object没有关系:
- 不会被Unity序列化,修改的值无法持久保存
- 无法在Inspector面板查看或编辑
- 不支持多对象共享同一个资源实例的特性
- 退出Play模式后实例会直接消失
3. Scriptable Object的正确用法
方式一:编辑器创建.asset资源
利用你代码里的[CreateAssetMenu]特性,在Unity编辑器右键菜单中选择ItemSystem→BasicItem,生成对应的Item资源文件,直接在Inspector中设置id、name、amount属性,然后在代码中直接引用这些资源实例。
方式二:运行时用CreateInstance创建
必须使用ScriptableObject.CreateInstance<T>()方法,才能创建Unity认可的Scriptable Object实例:
public void AddItem(int _itemId, string _itemName, int _itemAmount) { Item newItem = ScriptableObject.CreateInstance<Item>(); newItem.id = _itemId; newItem.name = _itemName; newItem.amount = _itemAmount; InventoryList.Add(newItem); }
注意:
- 运行时创建的Scriptable Object默认是临时对象,退出Play模式就会消失;如果需要永久保存,编辑器模式下可以用
AssetDatabase.CreateAsset,运行时则需要自己序列化到文件。 - 不要给Scriptable Object写自定义构造函数!Unity创建实例时只会调用无参构造,自定义构造函数会干扰Unity的初始化流程,且
CreateInstance不会触发自定义构造。
4. 代码优化示例
修正后的Inventory脚本
public class Inventory : MonoBehaviour { public List<Item> InventoryList = new List<Item>(); public void AddItem(Item itemToAdd) { // 创建独立的Item实例,避免修改原资源 Item newItemInstance = ScriptableObject.CreateInstance<Item>(); newItemInstance.id = itemToAdd.id; newItemInstance.name = itemToAdd.name; newItemInstance.amount = itemToAdd.amount; InventoryList.Add(newItemInstance); } }
修正后的Player脚本
public class Player : MonoBehaviour { public Inventory inventory; private void OnTriggerEnter2D(Collider2D collision) { if (collision.CompareTag("Item")) { ItemWorld itemWorld = collision.GetComponent<ItemWorld>(); if (itemWorld != null) { inventory.AddItem(itemWorld.item); Destroy(collision.gameObject); } } } }
移除Item脚本的自定义构造函数
[CreateAssetMenu(fileName = "Item", menuName ="ItemSystem/BasicItem")] public class Item : ScriptableObject { public int id; public string name; public int amount; }
内容的提问来源于stack exchange,提问作者erdem.61
相关产品推荐
相关产品推荐

