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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:40:18