简体   繁体   English

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

[英]ScriptableObject generic list makes all derived instances to base type when serializing in unity

I'm trying to make a stat system library to use in as many games as possible in the future, but unity's serialization got in the way. 我正在尝试制作一个统计信息系统库,以便将来在尽可能多的游戏中使用,但是unity的序列化妨碍了这一工作。

Knowing how awful is the serialization in unity and that derived instances get transformed to base type during serialization, I decided to make the base class derive from ScriptableObject, but it does not work. 知道序列化的统一性是多么糟糕,并且派生的实例在序列化过程中被转换为基类型,因此我决定使基类派生自ScriptableObject,但它不起作用。 My code was becoming a bit messy at this point so I decided to make a much simpler test version from scratch, by the book, as explained here: 此时,我的代码变得有些混乱,因此我决定从书开始重新编写一个简单得多的测试版本,如下所述:

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

But that didn't work either. 但这也不起作用。

The base class: 基类:

[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;
    }
}

The derived class: 派生类:

[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;
    }
}

And finally the collection class: 最后是集合类:

[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>(); }
    }
}

I am creating instances and checking the list like this: 我正在创建实例并检查列表,如下所示:

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);
    }
}

The custom editor I am using: 我正在使用的自定义编辑器:

[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();
            }
        }
    }
}

If I press Play and add a new derived instance to the list, it is recognized properly until I press Play again, when the derived instance gets moved to base type. 如果我按Play并将一个新的派生实例添加到列表中,它将被正确识别,直到当派生实例被移到基本类型时再次按Play时。

I tried a lot of things, but none seem to work so thank you for trying to help me. 我尝试了很多事情,但似乎没有任何效果,因此感谢您尝试帮助我。

OK I figured it out. 好,我知道了。 I was not creating assets out of the 2 classes. 我不是在2类中创建资产。 ScriptableObjects as assets can't reference non asset / prefab instances without loosing references apparently, so the data of my list of BaseClass got lost. 作为资产的ScriptableObjects不能在没有明显丢失参考的情况下引用非资产/预制实例,因此我的BaseClass列表数据丢失了。

Conclusion: it's not enough to make a ScriptableObject wrapper for ScriptableObject fields that you make an asset out of, but you need to make assets out of the fields too so the reference doesn't get lost. 结论:仅对要使用资产的ScriptableObject字段创建ScriptableObject包装器是不够的,但是您也需要在这些字段之外使用资产,以使引用不会丢失。

@derHugo thank you for your time. @derHugo谢谢您的时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 派生类型的通用基类 - Derived type of generic base class 仅知道通用基类的类型时,如何查找通用基类的派生类? - How to find a Derived Class of a Generic Base Class when you only know the type of the Generic Base Class? 将基本类型转换/转换为派生的泛型类型 - convert/cast base type to derived generic type 将通用基础对象转换为派生类型 - Casting Generic base object to derived type Unity:按特定顺序解析所有类型的已注册实例的列表 - Unity: Resolve list of all registered instances of type in specific order 是否可以将 Unity GameObject 转换为 ScriptableObject 类类型? - Is it possible to cast Unity GameObject to ScriptableObject class type? Unity3D控制台警告:不是从MonoBehaviour或ScriptableObject派生的 - Unity3D Console Warning: Not derived from MonoBehaviour or ScriptableObject 从基本泛型类型转换为派生泛型类型,其中派生类型具有附加类型约束 - Cast from base generic type to derived generic type where the derived type has additional type constraints 在WPF中设置派生类型的所有实例的样式 - Styling all instances of a derived type in WPF 将具有基本类型的列表序列化为派生类型json - Serialize list with base type into derived type json
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM