统一序列化时,ScriptableObject通用列表使所有派生实例都成为基本类型

时间:2019-06-07 07:19:25

标签: c# unity3d

我正在尝试建立一个统计系统库,以在将来尽可能多的游戏中使用,但是unity的序列化阻碍了这一工作。

知道序列化的统一性是多么糟糕,并且派生的实例在序列化过程中被转换为基类型,所以我决定使基类从ScriptableObject派生,但是它不起作用。 此时我的代码变得有些混乱,因此我决定从书开始重新编写一个简单得多的测试版本,如下所述:

  

https://forum.unity.com/threads/serialization-best-practices-megapost.155352/

但是那也不起作用。

基类:

[System.Serializable]
public class BaseClass : ScriptableObject
{
    [SerializeField]
    private string m_Name;

    [SerializeField]
    public string Name { get => m_Name; set => m_Name = value; }

    public static BaseClass NewInstance()
    {
        BaseClass b = CreateInstance<BaseClass>();
        b.Name = string.Empty;
        return b;
    }
    public static BaseClass NewInstance(string name)
    {
        BaseClass b = CreateInstance<BaseClass>();
        b.Name = name;
        return b;
    }
}

派生类:

[System.Serializable]
public class DerivedClass : BaseClass
{
    [SerializeField]
    private string m_Value;

    [SerializeField]
    public string Value { get => m_Value; set => m_Value = value; }

    public new static DerivedClass NewInstance()
    {
        DerivedClass d = CreateInstance<DerivedClass>();
        d.Name = string.Empty;
        d.Value = string.Empty;
        return d;
    }
    public static DerivedClass NewInstance(string name, string value)
    {
        DerivedClass d = CreateInstance<DerivedClass>();
        d.Name = name;
        d.Value = value;
        return d;
    }
}

最后是集合类:

[System.Serializable] [CreateAssetMenu(menuName = "CollectionA")]
public class CollectionA : ScriptableObject
{
    [SerializeField]
    private List<BaseClass> m_TestList;

    [SerializeField]
    public List<BaseClass> TestList { get => m_TestList; set => m_TestList = value; }

    public static CollectionA NewInstance()
    {
        CollectionA c = CreateInstance<CollectionA>();
        c.TestList = new List<BaseClass>();
        return c;
    }

    public List<T> GetAllWithType<T>()
    {
        try { return TestList.OfType<T>().ToList<T>(); }
        catch { return new List<T>(); }
    }
}

我正在创建实例并像这样检查列表:

private void Update()
{
    if (Input.GetKeyDown("k"))
    {
        print("Derived stats:");
        List<DerivedClass> derived = collection.GetAllWithType<DerivedClass>();
        foreach (DerivedClass t in derived)
        { print(t.Name + " | " + t.Value); }
    }
    if (Input.GetKeyDown("p"))
    {
        DerivedClass d = DerivedClass.NewInstance("Hey", "Hello");
        collection.TestList.Add(d);
    }
}

我正在使用的自定义编辑器:

[CustomEditor(typeof(CollectionA))]
public class CollectionAEditor : Editor
{
    private CollectionA collection;
    private List<DerivedClass> derived;
    struct derivedValues
    {
        public string name, value;
    }

    derivedValues addDerived = new derivedValues();

    public override void OnInspectorGUI()
    {
        if (target is CollectionA)
            collection = (CollectionA)target;
        if (collection != null)
        {
            DrawInspector();
        }
    }

    private void DrawInspector()
    {
        derived = collection.GetAllWithType<DerivedClass>();

        // title
        EditorGUILayout.Space();
        GUILayout.Label("CLASS LIST", EditorStyles.largeLabel);

        // title
        EditorGUILayout.Space();
        GUILayout.Label("Derived classes:", EditorStyles.boldLabel);

        // layout labels
        GUILayout.BeginHorizontal();
        GUILayout.Label("Name", GUILayout.MinWidth(35));
        GUILayout.Label("Value", GUILayout.MinWidth(35));
        GUILayout.EndHorizontal();

        // derived classes list
        if (derived.Count <= 0)
            GUILayout.Label("Class list empty", EditorStyles.centeredGreyMiniLabel);
        else
        {
            foreach (DerivedClass x in derived)
            {
                GUILayout.BeginHorizontal();
                x.Name = GUILayout.TextField(x.Name, GUILayout.MinWidth(35));
                x.Value = GUILayout.TextField(x.Value, GUILayout.MinWidth(35));
                GUILayout.EndHorizontal();
            }
        }

        // add derived stat
        EditorGUILayout.Space();
        GUILayout.BeginHorizontal();
        GUILayout.Label("Values:", GUILayout.Width(50f));
        addDerived.name = GUILayout.TextField(addDerived.name, GUILayout.MinWidth(35));
        addDerived.value = GUILayout.TextField(addDerived.value, GUILayout.MinWidth(35));
        GUILayout.EndHorizontal();
        if (GUILayout.Button("Add derived class"))
        {
            collection.TestList.Add(
                DerivedClass.NewInstance(addDerived.name, addDerived.value));
            addDerived = new derivedValues();
        }

        // default stats title
        EditorGUILayout.Space();
        GUILayout.BeginHorizontal();
        GUILayout.Label("All classes as default:", EditorStyles.boldLabel);
        if (collection.TestList == null) Debug.Log("NULL");
        GUILayout.Label(collection.TestList.Count.ToString());
        GUILayout.EndHorizontal();

        EditorGUILayout.Space();
        if (GUILayout.Button("Delete all instances"))
            collection.TestList.Clear();

        GUILayout.BeginHorizontal();
        GUILayout.Label("Name", GUILayout.MinWidth(35));
        GUILayout.EndHorizontal();

        // all classes list
        if (collection.TestList.Count <= 0)
            GUILayout.Label("Class list empty", EditorStyles.centeredGreyMiniLabel);
        else
        {
            foreach (BaseClass x in collection.TestList)
            {
                GUILayout.BeginHorizontal();
                x.name = GUILayout.TextField(x.name, GUILayout.MinWidth(35));
                GUILayout.EndHorizontal();
            }
        }
    }
}

如果我按Play并将一个新的派生实例添加到列表中,它将被正确识别,直到当派生实例被移至基本类型时再次按下Play为止。

我尝试了很多事情,但是似乎都没有用,所以感谢您尝试帮助我。

1 个答案:

答案 0 :(得分:0)

好,我知道了。我不是在2类中创建资产。 ScriptableObjects作为资产不能在没有明显丢失引用的情况下引用非资产/预制实例,因此我的BaseClass列表中的数据丢失了。

结论:仅仅为您使用资产的ScriptableObject字段创建ScriptableObject包装器是不够的,但是您也需要从这些字段中制作资产,以免引用丢失。

@derHugo谢谢您的宝贵时间。