Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何避免ScriptableObject数据结构更改时的数据丢失_C#_Unity3d - Fatal编程技术网

C# 如何避免ScriptableObject数据结构更改时的数据丢失

C# 如何避免ScriptableObject数据结构更改时的数据丢失,c#,unity3d,C#,Unity3d,我正在使用ScriptableObject资产为我的多语言应用程序项目存储数据。除了我想更改现有数据结构的情况外,这工作得非常好 在我的例子中,数据结构如下所示: [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/I18NData", order = 1)] public class I18NData : ScriptableObject { public I18NSpriteData[] Sprites;

我正在使用
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;
}
%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!
现在我在Unity编辑器中添加了数据,一切正常。但是,一旦我更改了现有数据结构上的某些内容,编辑器中的所有数据都将丢失。ScriptableObject似乎已重置为空状态

<> P>这是特别恼人的,因为我们处于发展的中期,数据结构的变化是不可避免的,在这个阶段…

为了避免这种情况,你会怎么做?您是否有基于其他文件(如JSON)生成ScriptableObject的脚本?还是有一个简单的方法可以从统一内部实现


谢谢你的提示

对结构的某些更改总是会导致数据丢失,因此,在确定数据的最终布局之前,您不应该使用数据填充它,至少对于主要人员来说,unity序列化应该支持添加新字段,但无论如何,我都不会依赖它


您可以使用几种技术来改进工作流程

  • 创建数据资产的层次结构。例如,将单个翻译实例作为单独的可编写脚本的对象来封装结构(顺便说一句,对值对象(如“I18NSpriteData”或“I18NTextData”)使用结构而不是类),然后链接“I18NData”中的所有资产
  • 使用外部数据存储,如JSON或数据库。这意味着要为编辑器开发一个导入/导出工具,但在许多情况下,这样做是值得的,特别是当您考虑将来实现动态翻译时(例如,通过从服务器下载)。您还可以考虑一种更具扩展性的语言方法,而不是将它们硬编码为结构字段,每个元素可能有一个翻译数组,例如:
  • [System.Serializable]
    公共结构I18NTextData
    {
    公共字符串标签;
    公共I18NTextDataTranslation[]翻译;
    }
    [系统可序列化]
    公共结构I18NTextDataTranslation
    {
    公共字符串语言;
    公共字符串内容;
    }
    
    您更改数据结构的方式可能会使Unity无法以所需的方式解压缩序列化数据。因此缺少数据

    如果选择了
    强制文本
    序列化模式,则所有ScriptableObject都将序列化为人类可读的格式YAML

    在文本编辑器中读取时,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;
    }
    
    %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后,您可以手动更改数据以适应新的数据结构。

    如果您想重命名序列化字段,但保留数据,Unity有一个名为的属性,该属性将保留旧的序列化字段名

    [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/I18NData", order = 1)]
    public class I18NData : ScriptableObject
    {
    
        [FormerlySerializedAs("Sprites")]
        public I18NSpriteData[] Sprites2;
        public I18NTextData[] Texts;
    }
    
    另一个选项是可以将字段设置为私有并使用属性。然后,您可以随时间自由更改访问或修改字段的属性,而不影响序列化:

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

    您可能想考虑的另一件事是使用接口对统一的序列化过程有更多的控制。它提供了两个方法,OnBeforeSerialize和OnAfterDeserialize,它们在Unity的序列化和反序列化过程中被调用


    当序列化Unity通常无法处理的复杂结构(如字典)时,这可能更有用,但根据所做更改的类型,这可能会有所帮助。您可能会使用它在修订之间迁移数据。

    您所说的“更改现有数据结构上的某些内容”是什么意思?您的意思是从类中添加/删除/重命名字段吗?或者你的意思是修改Unity Inspector中的数据?还是别的什么?另外,您是使用此ScriptableObject的自定义编辑器,还是直接在标准Unity inspector中进行编辑?@JasonCG例如,当我在上面的I18NData类中将“Sprites”重命名为“Sprites2”时,就会发生这种情况。然后,“精灵”/“精灵2”的Unity编辑器中的数据消失。虽然在.asset文件中,我仍然可以看到一些文件/GUID引用。但是手工编辑并不实用…谢谢你的回复。是的,你说得对。我的.asset文件被序列化为YAML,我看到了与您描述的类似的内容。但是,这对我来说似乎很不容易。你真的手工编辑这些东西吗?我还考虑了你在第二章中提出的建议。需要付出很多额外的努力,但可能是值得的。但这在实践中是如何实现的呢?我仍然看到一些问题:例如,我有一些PNG文件,我必须为每种语言生成,因为其中包含一些图形文本。如何在JSON文件中正确引用图像?我想JSON文件应该被处理并生成成某种.asset文件(ScriptableObject),对吗?或者这种方法是否在运行时使用Resource.Load语句?再次感谢您的想法。小PNG可以是base64编码的字符串,或者您可以将它们单独存储为文件,并在json中仅保留对这些文件的引用,文件也可以在服务器上和/或本地缓存。你可以在需要的地方动态加载这些文件。谢谢你的回复。这真的很有帮助。