Сохранение List в ScriptableObject

Программирование на Юнити.

Сохранение List в ScriptableObject

Сообщение misereli 03 авг 2020, 19:25

Всем привет!
Делаю эникей-интерфейс (окошко с настройками проекта) с помощью EditorWindow, данные сохраняю с помощью ScriptableObject.

Код самого ScriptableObject (SpritesGroup)
Синтаксис:
Используется csharp
using System.Collections.Generic;
using UnityEngine;

namespace Editor
{
        [CreateAssetMenu(fileName = "SpritesGroup", menuName = "Scriptable Objects/Sprites Group")]
        public class SpritesGroup : ScriptableObject
        {
                [SerializeField] private List<SpriteInfo> sprites;

                public SpritesGroup()
                {
                        sprites = new List<SpriteInfo>();
                }

                public List<SpriteInfo> Sprites => sprites;

                public void AddSpriteInfo(SpriteInfo spriteInfo)
                {
                        sprites.Add(spriteInfo);
                }
        }
}


Класс SpriteInfo
Синтаксис:
Используется csharp
using UnityEngine;

namespace Editor
{
    [System.Serializable]
    public class SpriteInfo
    {
        [SerializeField] private string name;
        [SerializeField] private Sprite sprite;

        public SpriteInfo()
        {
            name = "new sprite";
            sprite = null;
        }

        public string Name
        {
            get => name;
            set => name = value;
        }

        public Sprite Sprite
        {
            get => sprite;
            set => sprite = value;
        }
    }
}


Для добавления нового элемента в список использую метод AddSpriteInfo(SpriteInfo spriteInfo) в SpritesGroup.
И вроде бы всё работает, в ScriptableObject список пополняется
Изображение
НО после перезапуска Unity содержимое списка исчезает.
Изображение

Казалось бы схема дохлая, это так не работает, но если вручную в окне Inspector задать размерность списка, то всё ок! Все созданные элементы после перезапуска Unity на месте.

Собственно вопрос в чем разница, между Юнити-решением изменения размерности сериализованного списка и моим?
И соответственно, как сделать так, чтобы я мог через метод AddSpriteInfo добавлять элементы в список и он сохранялся?
Unity3d.ru :) Discord worldadmin#5845
Аватара пользователя
misereli
UNITрон
 
Сообщения: 165
Зарегистрирован: 05 мар 2012, 14:13
Откуда: Tver

Re: Сохранение List в ScriptableObject

Сообщение 1max1 03 авг 2020, 19:54

Добавляя через AddSpriteInfo не происходит сериализации. Покажи скрипт где вызывается метод.
https://docs.unity3d.com/ScriptReferenc ... bject.html
https://docs.unity3d.com/ScriptReferenc ... perty.html
https://docs.unity3d.com/ScriptReferenc ... rties.html
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Сохранение List в ScriptableObject

Сообщение samana 03 авг 2020, 19:59

Не получилось воссоздать такую проблему (unity personal 2019.2.8f1), после перезапуска все данные остались на месте.

Кстати, в вашем свойстве
Синтаксис:
Используется csharp
public List<SpriteInfo> Sprites => sprites;

разве есть смысл? Ведь идёт возврат приватного массива, который можно запросто изменить извне.

Ваш код для теста немного переделал (самую малость).
Синтаксис:
Используется csharp
[CreateAssetMenu(fileName = "SpritesGroup", menuName = "Scriptable Objects/Sprites Group")]
public class SpritesGroup : ScriptableObject
{
    [SerializeField] private List<SpriteInfo> _sprites;

    public SpritesGroup()
    {
        _sprites = new List<SpriteInfo>();
    }
       
    // добавил этот атрибут, чтобы в инспекторе можно было вызвать этот метод для тестирования (выбрав "add" в выпадающем списке, после клика по иконке шестерёнки инспектора этого скриптблОбжекта).
    [ContextMenu("add")]
    public void AddSpriteInfo()
    {
        _sprites.Add(new SpriteInfo());
    }
}
Аватара пользователя
samana
Адепт
 
Сообщения: 4738
Зарегистрирован: 21 фев 2015, 13:00
Откуда: Днепропетровск

Re: Сохранение List в ScriptableObject

Сообщение misereli 03 авг 2020, 22:21

Спасибо. Помогли продвинуться

samana писал(а):Кстати, в вашем свойстве
Синтаксис:
Используется csharp
public List<SpriteInfo> Sprites => sprites;

разве есть смысл? Ведь идёт возврат приватного массива, который можно запросто изменить извне.

Метод был добавлен для наглядности. )

1max1 писал(а):Покажи скрипт где вызывается метод.

Действительно ошибка оказалась в месте вызова метода.
Есть две кнопки:
1. "Add new sprite (NOT WORK)" добавляет данные в ScriptableObject, но после перезагрузки Unity данные не сохраняются.
2. "Add new sprite (WORK)" добавляет данные в ScriptableObject и сохраняет данные. С ней всё отлично.
//Постарался максимально закомментировать, чтобы быстро разобраться.
Синтаксис:
Используется csharp
private void LoadSpritesGroup()
        {
            EditorGUILayout.BeginVertical("Box");
           
            EditorGUILayout.LabelField("Sprite groups", EditorStyles.boldLabel);
           
            //Загружаем ассеты, если их количество поменялось
            //Так же добавляем ссылки на ассеты в Dictionary<SpritesGroup, bool> _spritesGroup,
            //где bool - состояние (открыт/закрыт) спойлера (Foldout) у SpritesGroup
            List<SpritesGroup> groups;
            int currentCount;
            if((currentCount = (groups = Extensions.Assets.GetAssets<SpritesGroup>("Assets/Data/Editor/")).Count) != _numberOfSpritesGroup)
            {
                groups.ForEach(group => _spritesGroup.Add(group, false));
                _numberOfSpritesGroup = currentCount;
            }
           
            //отображаем спойлер (Foldout) и его содержимое
            foreach (var key in _spritesGroup.Keys.ToList().Where(key => _spritesGroup[key] = EditorGUILayout.Foldout(_spritesGroup[key], key.name)))
            {
                key.Sprites.ToList().ForEach(sprite =>
                {
                    EditorGUILayout.BeginHorizontal();
                    sprite.Name = EditorGUILayout.TextField(sprite.Name);
                    sprite.Sprite = (Sprite)EditorGUILayout.ObjectField(sprite.Sprite, typeof(Sprite), true);
                    if (GUILayout.Button("Delete"))
                    {
                        key.Sprites.Remove(sprite);
                    }
                    EditorGUILayout.EndHorizontal();
                });

                //Данная конструкция не сериализует данные
                if (GUILayout.Button("Add new sprite (NOT WORK)"))
                {
                    key.Sprites.Add(new SpriteInfo());
                }
            }
           
            //При прямом обращении все работает
            if (GUILayout.Button("Add new sprite (WORK)"))
            {
                groups[0].Sprites.Add(new SpriteInfo());
            }
           
            EditorGUILayout.EndVertical();
        }


Сам интерфейс выглядит вот так
Изображение

Вот и не пойму, при присвоении в Dictionary _spritesGroup значения из groups присваивается же ссылка на объект. К тому же в Unity выглядит все хорошо - добавляются элементы в список нужного ScriptableObject. Только нет сохранения.
Unity3d.ru :) Discord worldadmin#5845
Аватара пользователя
misereli
UNITрон
 
Сообщения: 165
Зарегистрирован: 05 мар 2012, 14:13
Откуда: Tver

Re: Сохранение List в ScriptableObject

Сообщение misereli 06 авг 2020, 00:43

Всем спасибо за помощь!
Проблема была в том, что после изменения размерности списка надо было дописать EditorUtility.SetDirty(scriptableObject);

Синтаксис:
Используется csharp
if (GUILayout.Button("Add new sprite"))
{
   key.Sprites.Add(new SpriteInfo());
   EditorUtility.SetDirty(key);
}
Unity3d.ru :) Discord worldadmin#5845
Аватара пользователя
misereli
UNITрон
 
Сообщения: 165
Зарегистрирован: 05 мар 2012, 14:13
Откуда: Tver


Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot] и гости: 5