Общий список ScriptableObject превращает все производные экземпляры в базовый тип при сериализации в единстве - PullRequest
1 голос
/ 07 июня 2019

Я пытаюсь создать библиотеку систем статистики, чтобы в будущем можно было использовать как можно больше игр, но мешает сериализация Unity.

Зная, насколько ужасна сериализация в 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 и добавляюновый производный экземпляр в списке, он распознается должным образом до тех пор, пока я снова не нажму «Воспроизвести», когда производный экземпляр будет перемещен в базовый тип.

Я пробовал много вещей, но ни одна из них, похоже, не работает, поэтому спасибо за попыткучтобы помочь мне.

1 Ответ

0 голосов
/ 08 июня 2019

ОК, я понял это. Я не создавал активы из 2 классов. ScriptableObjects как объекты не могут ссылаться на экземпляры non-asset / prefab без потери ссылок, поэтому данные из моего списка BaseClass потерялись.

Вывод: недостаточно создать оболочку ScriptableObject для полей ScriptableObject, из которых вы создаете актив, но вам также необходимо сделать активы из полей, чтобы ссылка не терялась.

@ derHugo спасибо за ваше время.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...