Как избежать потери данных при изменении структуры данных ScriptableObject - PullRequest
0 голосов
/ 06 февраля 2019

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

В моем случае структура данных выглядит следующим образом:

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/I18NData", order = 1)]
public class I18NData : ScriptableObject
{
    public I18NSpriteData[] Sprites;
    public I18NTextData[] Texts;
}

[System.Serializable]
public class I18NSpriteData
{
    public string Label;
    public Sprite SpriteEN;
    public Sprite SpriteFR;
    public Sprite SpriteSG;
    public Sprite SpriteES;
    public Sprite SpriteDE;
    public Sprite SpriteIT;
}

[System.Serializable]
public class I18NTextData
{
    public string Label;

    [TextArea]
    public string TextEN;

    [TextArea]
    public string TextFR;

    [TextArea]
    public string TextSG;

    [TextArea]
    public string TextES;

    [TextArea]
    public string TextDE;

    [TextArea]
    public string TextIT;
}

Теперь я добавляю данные вредактор единства и все работает и нормально.Но как только я что-то изменяю в существующей структуре данных, все данные в редакторе теряются.Кажется, ScriptableObject сбрасывается в пустое состояние.

Это особенно раздражает, поскольку мы находимся в процессе разработки, и изменения структуры данных на данном этапе неизбежны ...

Что вы делаете, чтобы избежать таких ситуаций?Есть ли у вас сценарии, которые генерируют объекты ScriptableObject на основе других файлов, таких как JSON?Или есть простой способ изнутри Unity?

Спасибо за любую подсказку!

Ответы [ 3 ]

0 голосов
/ 06 февраля 2019

Вы, вероятно, изменяете структуру данных таким образом, что Unity не может распаковать сериализованные данные желаемым способом.Отсюда и отсутствующие данные.

Если выбран режим сериализации Force Text, все объекты ScriptableObject будут сериализованы в читаемый человеком формат YAML.

При чтении в текстовом редакторе объект ScriptableObject будет выглядетьчто-то вроде этого:

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInternal: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 9228bfb9e457c5341920079380c382ba, type: 3}
  m_Name: Data
  m_EditorClassIdentifier:
  Sprites:
  - Label:
    SpriteEN: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0}
    SpriteFR: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
    SpriteSG: {fileID: 10915, guid: 0000000000000000f000000000000000, type: 0}
    SpriteES: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0}
    SpriteDE: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0}
    SpriteIT: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
  Texts:
  - Label:
    TextEN: "Hello\t"
    TextFR: Salut!
    TextSG: ????
    TextES: Holla!
    TextDE: Bratwurst
    TextIT: Pizza!

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

0 голосов
/ 12 февраля 2019

Если вы хотите переименовать сериализованное поле, но сохранить данные, Unity имеет атрибут FormerlySerializedAs , который будет поддерживать старое сериализованное имя поля.

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/I18NData", order = 1)]
public class I18NData : ScriptableObject
{

    [FormerlySerializedAs("Sprites")]
    public I18NSpriteData[] Sprites2;
    public I18NTextData[] Texts;
}

Другой вариантесли вы можете сделать поля приватными и использовать атрибут SerializeField .Затем вы можете свободно изменять свойства, которые обращаются к полям или изменяют их с течением времени, не влияя на сериализацию:

[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/I18NData", order = 1)]
public class I18NData : ScriptableObject
{
    [SerializeField]
    private I18NSpriteData[] Sprites;
    public I18NSpriteData[] Sprites2 {
            get { return Sprites; }
            set { Sprites = value; }
    }

    [SerializeField]
    private I18NTextData[] Texts;
    public I18NTextData[] Texts2 {
            get { return Texts; }
            set { Texts= value; }
    }
}

Еще одна вещь, которую вы можете рассмотреть, - это использование интерфейса ISerializationCallbackReceiver для большего контроля надПроцесс сериализации Unity.Он предоставляет два метода, OnBeforeSerialize и OnAfterDeserialize, которые вызываются в процессе сериализации и десериализации Unity.

Это, вероятно, более полезно при сериализации сложных структур, которые Unity обычно не может обрабатывать, например, словари, но в зависимости от типов вносимых вами изменений это может оказаться полезным.Вы можете использовать его для переноса данных между ревизиями.

0 голосов
/ 06 февраля 2019

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


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

  1. Создание иерархии данныхактивы.Например, создайте отдельные экземпляры перевода в виде отдельного объекта с возможностью написания сценариев для инкапсуляции ваших структур (кстати, используйте структуры вместо классов для объектов-значений, таких как «I18NSpriteData» или «I18NTextData»), а затем свяжите все активы внутри «I18NData».
  2. Используйте внешнее хранилище данных, например, JSON или базу данных.Это подразумевает разработку инструмента импорта / экспорта для редактора, но во многих случаях это стоит сделать, особенно когда вы планируете реализовать динамические переводы в будущем (например, путем загрузки с сервера).Вы также можете подумать о более расширяемом подходе к языкам, и вместо жесткого кодирования их как структурного поля, для каждого элемента может быть массив переводов, например:
[System.Serializable]
public struct I18NTextData
{
    public string Label;
    public I18NTextDataTranslation[] translations;
}
[System.Serializable]
public struct I18NTextDataTranslation
{
    public string lang;
    public string content;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...