unity序列化和反序列化_Unity中的序列化

unity序列化和反序列化

In the spirit of sharing more of the tech behind the scenes, and reasons why some things are the way they are, this post contains an overview of Unity’s serialization system. Understanding this system very well can have a big impact on the effectiveness of your development, and the performance of the things you make.  Here we go.

本着分享更多幕后技术的精神,以及某些事物按其原样进行的原因,本文包含Unity序列化系统的概述。 很好地了解此系统可能会对开发效率和所做工作的性能产生重大影响。 开始了。

Serialization of “things” is at the very core of Unity. Many of our features build ontop of the serialization system:

“事物”的序列化是Unity的核心。 我们的许多功能都建立在序列化系统之上:

  • Storing data stored in your scripts. This one most people are probably somewhat familiar with.

    存储脚本中存储的数据。 大多数人可能对此有点熟悉。

  • Prefabs. Internally, a prefab is the serialized data stream of one (or more) game objects and components. A prefab instance is a list of modifications that should be made on the serialized data for this instance. The concept prefab actually only exists at editor time. The prefab modifications get baked into a normal serialization stream when Unity makes a build, and when that gets instantiated, the instantiated gameobjects have no idea they were a prefab when they lived in the editor.

    预制件。 在内部,预制件是一个(或多个)游戏对象和组件的序列化数据流。 预制实例是应对此实例的序列化数据进行的修改的列表。 预制概念实际上仅在编辑时存在。 当Unity进行构建时,将预制的修改放入常规的序列化流中,并在实例化该实例时,实例化的游戏对象不知道它们是否存在于编辑器中是预制的。

  • Instantiation. When you call Instantiate() on either a prefab, or a gameobject that lives in the scene, or on anything else for that matter (everything that derives from UnityEngine.Object can be serialized), we serialize the object, then create a new object, and then we “deserialize” the data onto the new object. (We then run the same serialization code again in a different variant, where we use it to report which other UnityEngine.Object’s are being referenced. We then check for all referenced UnityEngine.Object’s if they are part of the data being Instantiated(). If the reference is pointing to something “external” (like a texture) we keep that reference as it is, if it is pointing to something “internal” (like a child gameobject), we patch the reference to the corresponding copy).

    实例化。 当您在预制件,场景中的游戏对象或与此相关的任何其他对象上调用Instantiate()时(对于从UnityEngine.Object派生的所有对象都可以序列化),我们将序列化该对象,然后创建一个新对象,然后将数据“反序列化”到新对象上。 (然后,我们在另一个变体中再次运行相同的序列化代码,在该变体中,我们使用该代码来报告正在引用哪些其他UnityEngine.Object。然后检查所有被引用的UnityEngine.Object是否属于Instantiated()数据。如果引用指向“外部”(例如纹理),则将其保持原样;如果引用指向“内部”(例如子游戏对象),则将引用修补到相应的副本。

  • Saving. If you open a .unity scene file with a text editor, and have set unity to “force text serialization”, we run the serializer with a yaml backend.

    保存。 如果您使用文本编辑器打开.unity场景文件,并将unity设置为“强制文本序列化”,则我们将使用yaml后端运行序列化程序。

  • Loading. Might not seem surprising, but backwards compatible loading is a system that is built on top of serialization as well. In-editor yaml loading uses the serialization system, as well as the runtime loading of scenes and assets. Assetbundles also make use of the serialization system.

    正在加载。 可能并不令人惊讶,但是向后兼容的加载也是基于序列化的系统。 编辑器中的Yaml加载使用序列化系统以及场景和资产的运行时加载。 Assetbundle还利用序列化系统。

  • Hot reloading of editor code. When you change an editor script, we serialize all editor windows (they derive from UnityEngine.Object!), we then destroy all the windows, unload the old c# code, load the new c# code, recreate the windows, and finally deserialize the datastreams of the windows back onto the new windows.

    热重载编辑器代码。 当您更改编辑器脚本时,我们序列化所有编辑器窗口(它们从UnityEngine.Object派生!),然后销毁所有窗口,卸载旧的c#代码,加载新的c#代码,重新创建窗口,最后反序列化数据流的窗口重新回到新窗口。

  • Resource.GarbageCollectSharedAssets(). This is our native garbage collector and is different to the C# garbage collector. It is the thing that we run after you load a scene to figure out which things from the previous scene are no longer referenced, so we can unload them. The native garbage collector runs the serializer in a mode where we use it to have objects report all references to external UnityEngine.Objects. This is what makes textures that were used by scene1, get unloaded when you load scene2.

    Resource.GarbageCollectSharedAssets()。 这是我们的本机垃圾收集器,与C#垃圾收集器不同。 这是我们在加载场景后执行的操作,以确定不再引用先前场景中的哪些内容,因此我们可以将其卸载。 本机垃圾收集器以一种模式运行序列化程序,在该模式下,我们使用序列化程序让对象报告对外部UnityEngine.Objects的所有引用。 这就是使Scene1使用的纹理在加载Scene2时被卸载的原因。

The serialization system is written in C++, we use it for all our internal object types (Textures, AnimationClip, Camera, etc). Serialization happens at the UnityEngine.Object level, each UnityEngine.Object is always serialized as a whole. They can contain references to other UnityEngine.Objects and those references get serialized properly.

序列化系统是用C ++编写的,我们将其用于所有内部对象类型(纹理,AnimationClip,Camera等)。 序列化发生在UnityEngine.Object级别,每个UnityEngine.Object始终作为一个整体序列化。 它们可以包含对其他UnityEngine.Objects的引用,并且这些引用会正确序列化。

Now you may say that none of this concerns you very much, you’re just happy that it works and want to get on with actually creating some content. However, this will concern you, as we use this same serializer to serialize MonoBehaviour components, which are backed by your scripts. Because of the very high performance requirements that the serializer has, it does not in all cases behave exactly like what a C# developer would expect from a serializer. Here we’ll describe how the serializer works and some best practices on how to make the best use of it.

现在您可能会说,这些都不是您非常关心的事情,您只是很高兴它能起作用,并且希望继续实际创建一些内容。 但是,这会引起您的关注,因为我们使用相同的序列化程序来序列化由脚本支持的MonoBehaviour组件。 由于串行器具有非常高的性能要求,因此它在所有情况下的行为都不会完全像C#开发人员对串行器的期望。 在这里,我们将描述序列化器的工作方式以及一些如何最佳利用序列化的最佳实践。

What does a field of my script need to be in order to be serialized?

我的脚本的字段需要什么才能被序列化?

    Which fieldtypes can we serialize?

    我们可以序列化哪些字段类型?

      So far so good. So what are these situations where the serializer behaves differently from what I expect?

      到目前为止,一切都很好。 那么,在哪些情况下串行化器的行为与我期望的不同?

      Custom classes behave like structs

      自定义类的行为类似于结构

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      [Serializable]
      class Animal
      {
          public string name;
      }
      class MyScript : MonoBehaviour
      {
          public Animal[] animals;
      }

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      [ Serializable ]
      class Animal
      {
           public string name ;
      }
      class MyScript : MonoBehaviour
      {
           public Animal [ ] animals ;
      }

      If you populate the animals array with three references to a single Animal object, in the serializationstream you will find 3 objects. When it’s deserialized, there are now three different objects. If you need to serialize a complex object graph with references, you cannot rely on Unity’s serializer doing that all automagically for you, and have to do some work to get that object graph serialized yourself. See the example below on how to serialize things Unity doesn’t serialize by itself.

      如果用三个对单个Animal对象的引用填充animals数组,则在序列化流中将找到3个对象。 反序列化后,现在有三个不同的对象。 如果您需要使用引用序列化一个复杂的对象图,则不能依靠Unity的序列化器为您自动完成所有任务,而必须做一些工作才能使该对象图自行序列化。 请参阅以下示例,了解如何序列化Unity本身不会序列化的内容。

      Note that this is only true for custom classes, as they are serialized “inline” because their data becomes part of the complete serializationdata for the MonoBehaviour they are used in. When you have fields that have a reference to something that is a UnityEngine.Object derived class, like a “public Camera myCamera”, the data from that camera are not serialized inline, and an actual reference to the camera UnityEngine.Object is serialized.

      请注意,这仅适用于自定义类,因为它们是“内联”序列化的,因为它们的数据成为其使用的MonoBehaviour的完整序列化数据的一部分。当您具有引用了UnityEngine.Object的字段时派生类,例如“ public Camera myCamera”,来自该摄像机的数据不会内联序列化,并且对摄像机UnityEngine.Object的实际引用也已序列化。

      No support for null for custom classes

      自定义类不支持null

      Pop quiz. How many allocations are made when deserializing a MonoBehaviour that uses this script:

      突击测验。 反序列化使用此脚本的MonoBehaviour时会进行多少分配:

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      class Test : MonoBehaviour
      {
          public Trouble t;
      }
      [Serializable]
      class Trouble
      {
          public Trouble t1;
          public Trouble t2;
          public Trouble t3;
      }

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      class Test : MonoBehaviour
      {
           public Trouble t ;
      }
      [ Serializable ]
      class Trouble
      {
           public Trouble t1 ;
           public Trouble t2 ;
           public Trouble t3 ;
      }

      It wouldn’t be strange to expect 1 allocation, that of the Test object. It also wouldn’t be strange to expect 2 allocations, one for the Test object and one for a Trouble object. The correct answer is 729. The serializer does not support null. If it serializes an object and a field is null, we just instantiate a new object of that type and serialize that. Obviously this could lead to infinite cycles, so we have a relatively magical depth limit of 7 levels. At that point we just stop serializing fields that have types of custom classes/structs and lists and arrays. [1]

      期望分配1,即Test对象的分配,这并不奇怪。 期望有2个分配,一个分配给Test对象,另一个分配给Trouble对象也并不奇怪。 正确的答案是729。序列化器不支持null。 如果它序列化了一个对象并且一个字段为空,那么我们只需实例化该类型的新对象并将其序列化即可。 显然,这可能会导致无限循环,因此我们具有7个级别的相对神奇的深度限制。 到那时,我们只是停止序列化具有自定义类/结构以及列表和数组类型的字段。 [1]

      Since so many of our subsystems build on top of the serialization system, this unexpectedly big serializationstream for the Test monobehaviour will cause all these subsystems to perform more slowly than necessary. When we investigate performance problems in customer projects, we almost always find this problem and we added a warning for this situation in Unity 4.5.  We actually messed up the warning implementation in such a way that it gives you so many warnings, you have no other option but to fix them right away. We’ll soon ship a fix for this in a patch release, the warning is not gone, but you will only get one per “entering playmode”, so you don’t get spammed crazy. You’d still want to fix your code, but you should be able to do it at a time where it suits you.

      由于我们的许多子系统都建立在序列化系统之上,因此测试单行为的意外大序列化流将导致所有这些子系统的执行速度比必要的慢。 当我们调查客户项目中的性能问题时,我们几乎总是会发现此问题,并在Unity 4.5中针对这种情况添加了警告。 实际上,我们以某种方式弄乱了警告实现,它为您提供了很多警告,您别无选择,只能立即对其进行修复。 我们很快将在补丁程序版本中发布此修复程序,该警告没有消失,但是您在每个“进入播放模式”下只会得到一个警告,因此您不会发垃圾邮件。 您仍然想修复您的代码,但是您应该能够在适合自己的时候进行修改。

      No support for polymorphism

      不支持多态

      If you have a

      如果你有一个

      1

      public Animal[] animals

      1

      public Animal [ ] animals

      and you put in an instance of a dog, a cat and a giraffe, after serialization, you will have three instances of Animal.

      并放入了一只狗,一只猫和一只长颈鹿的实例,序列化后,您将拥有三个Animal实例。

      One way to deal with this limitation is to realize that it only applies to “custom classes”, which get serialized inline. References to other UnityEngine.Object’s get serialized as actual references and for those, polymorphism does actually work. You’d make a ScriptableObject derived class or another MonoBehaviour derived class, and reference that. The downside of doing this, is that you need to store that monobehaviour or scriptable object somewhere and cannot serialize it inline nicely.

      解决此限制的一种方法是认识到它仅适用于内联序列化的“自定义类”。 对其他UnityEngine.Object的引用被序列化为实际引用,对于这些引用,多态性确实有效。 您将创建一个ScriptableObject派生类或另一个MonoBehaviour派生类,并对其进行引用。 这样做的缺点是,您需要将该单一行为或可编写脚本的对象存储在某个位置,并且不能很好地内联序列化它。

      The reason for these limitations is that one of the core foundations of the serialization system is that the layout of the datastream for an object is known ahead of time, and depends on the types of the fields of the class, instead of what happens to be stored inside the fields.

      这些限制的原因是,序列化系统的核心基础之一是对象的数据流的布局事先已知,并且取决于类字段的类型,而不是实际发生的情况。存储在字段中。

      I want to serialize something that Unity’s serializer doesn’t support. What do I do?

      我要序列化Unity的序列化器不支持的内容。 我该怎么办?

      In many cases the best approach is to use serialization callbacks. They allow you to be notified before the serializer reads data from your fields and after it is done writing to them. You can use this to have a different representation of your hard-to-serialize data at runtime than when you actually serialize. You’d use these to transform your data into something Unity understands right before Unity wants to serialize it, you also use it to transform the serialized form back into the form you’d like to have your data in at runtime, right after Unity has written the data to your fields.

      在许多情况下,最好的方法是使用序列化回调。 它们使您可以在序列化程序从您的字段读取数据之前以及完成对它们的写入操作之后得到通知。 与实际序列化时相比,您可以使用它在运行时以不同的方式表示难以序列化的数据。 您将使用它们将数据转换为Unity想要序列化之前Unity可以理解的东西,还可以使用它将序列化的表单转换回您希望在运行时将数据存入的表单,将数据写入您的字段。

      Let’s say you want to have a tree datastructure. If you let Unity directly serialize the data structure, the “no support for null” limitation would cause your datastream to become very big, leading to performance degradations in many systems:

      假设您想要一个树数据结构。 如果让Unity直接序列化数据结构,则“不支持空值”限制将导致您的数据流变得非常大,从而导致许多系统的性能下降:

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      using UnityEngine;
      using System.Collections.Generic;
      using System;
      public class VerySlowBehaviourDoNotDoThis : MonoBehaviour
      {
          [Serializable]
          public class Node
          {
              public string interestingValue = "value";
             //The field below is what makes the serialization data become huge because
             //it introduces a 'class cycle'.
             public List<Node> children = new List<Node>();
          }
          //this gets serialized
          public Node root = new Node();
          void OnGUI()
          {
              Display (root);
          }
          void Display(Node node)
          {
              GUILayout.Label ("Value: ");
              node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));
              GUILayout.BeginHorizontal ();
              GUILayout.Space (20);
              GUILayout.BeginVertical ();
              foreach (var child in node.children)
                  Display (child);
              if (GUILayout.Button ("Add child"))
                  node.children.Add (new Node ());
              GUILayout.EndVertical ();
              GUILayout.EndHorizontal ();
          }
      }

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      using UnityEngine ;
      using System . Collections . Generic ;
      using System ;
      public class VerySlowBehaviourDoNotDoThis : MonoBehaviour
      {
           [ Serializable ]
           public class Node
           {
               public string interestingValue = "value" ;
             //The field below is what makes the serialization data become huge because
             //it introduces a 'class cycle'.
             public List & lt ; Node & gt ; children = new List & lt ; Node & gt ; ( ) ;
           }
           //this gets serialized
           public Node root = new Node ( ) ;
           void OnGUI ( )
           {
               Display ( root ) ;
           }
           void Display ( Node node )
           {
               GUILayout . Label ( "Value: " ) ;
               node . interestingValue = GUILayout . TextField ( node . interestingValue , GUILayout . Width ( 200 ) ) ;
               GUILayout . BeginHorizontal ( ) ;
               GUILayout . Space ( 20 ) ;
               GUILayout . BeginVertical ( ) ;
               foreach ( var child in node . children )
                   Display ( child ) ;
               if ( GUILayout . Button ( "Add child" ) )
                   node . children . Add ( new Node ( ) ) ;
               GUILayout . EndVertical ( ) ;
               GUILayout . EndHorizontal ( ) ;
           }
      }

      Instead, you tell Unity not to serialize the tree directly, and you make a seperate field to store the tree in a serialized format, suited for Unity’s serializer:

      相反,您告诉Unity不要直接序列化该树,并创建一个单独的字段以适合Unity的序列化器的序列化格式存储树:

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      using UnityEngine;
      using System.Collections.Generic;
      using System;
      public class BehaviourWithTree : MonoBehaviour, ISerializationCallbackReceiver
      {
          //node class that is used at runtime
          public class Node
          {
              public string interestingValue = "value";
              public List<Node> children = new List<Node>();
          }
          //node class that we will use for serialization
          [Serializable]
          public struct SerializableNode
          {
              public string interestingValue;
              public int childCount;
              public int indexOfFirstChild;
          }
          //the root of what we use at runtime. not serialized.
          Node root = new Node();
          //the field we give unity to serialize.
          public List<SerializableNode> serializedNodes;
          public void OnBeforeSerialize()
          {
              //unity is about to read the serializedNodes field's contents. lets make sure
              //we write out the correct data into that field "just in time".
              serializedNodes.Clear();
              AddNodeToSerializedNodes(root);
          }
          void AddNodeToSerializedNodes(Node n)
          {
              var serializedNode = new SerializableNode () {
                  interestingValue = n.interestingValue,
                  childCount = n.children.Count,
                  indexOfFirstChild = serializedNodes.Count+1
              };
              serializedNodes.Add (serializedNode);
              foreach (var child in n.children)
                  AddNodeToSerializedNodes (child);
          }
          public void OnAfterDeserialize()
          {
              //Unity has just written new data into the serializedNodes field.
              //let's populate our actual runtime data with those new values.
              if (serializedNodes.Count > 0)
                  root = ReadNodeFromSerializedNodes (0);
              else
                  root = new Node ();
          }
          Node ReadNodeFromSerializedNodes(int index)
          {
              var serializedNode = serializedNodes [index];
              var children = new List<Node> ();
              for(int i=0; i!= serializedNode.childCount; i++)
                  children.Add(ReadNodeFromSerializedNodes(serializedNode.indexOfFirstChild + i));
              return new Node() {
                  interestingValue = serializedNode.interestingValue,
                  children = children
              };
          }
          void OnGUI()
          {
              Display (root);
          }
          void Display(Node node)
          {
              GUILayout.Label ("Value: ");
              node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));
              GUILayout.BeginHorizontal ();
              GUILayout.Space (20);
              GUILayout.BeginVertical ();
              foreach (var child in node.children)
                  Display (child);
              if (GUILayout.Button ("Add child"))
                  node.children.Add (new Node ());
              GUILayout.EndVertical ();
              GUILayout.EndHorizontal ();
          }
      }

      1

      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      using UnityEngine ;
      using System . Collections . Generic ;
      using System ;
      public class BehaviourWithTree : MonoBehaviour , ISerializationCallbackReceiver
      {
           //node class that is used at runtime
           public class Node
           {
               public string interestingValue = "value" ;
               public List & lt ; Node & gt ; children = new List & lt ; Node & gt ; ( ) ;
           }
           //node class that we will use for serialization
           [ Serializable ]
           public struct SerializableNode
           {
               public string interestingValue ;
               public int childCount ;
               public int indexOfFirstChild ;
           }
           //the root of what we use at runtime. not serialized.
           Node root = new Node ( ) ;
           //the field we give unity to serialize.
           public List & lt ; SerializableNode & gt ; serializedNodes ;
           public void OnBeforeSerialize ( )
           {
               //unity is about to read the serializedNodes field's contents. lets make sure
               //we write out the correct data into that field "just in time".
               serializedNodes . Clear ( ) ;
               AddNodeToSerializedNodes ( root ) ;
           }
           void AddNodeToSerializedNodes ( Node n )
           {
               var serializedNode = new SerializableNode ( ) {
                   interestingValue = n . interestingValue ,
                   childCount = n . children . Count ,
                   indexOfFirstChild = serializedNodes . Count + 1
               } ;
               serializedNodes . Add ( serializedNode ) ;
               foreach ( var child in n . children )
                   AddNodeToSerializedNodes ( child ) ;
           }
           public void OnAfterDeserialize ( )
           {
               //Unity has just written new data into the serializedNodes field.
               //let's populate our actual runtime data with those new values.
               if ( serializedNodes . Count & gt ; 0 )
                   root = ReadNodeFromSerializedNodes ( 0 ) ;
               else
                   root = new Node ( ) ;
           }
           Node ReadNodeFromSerializedNodes ( int index )
           {
               var serializedNode = serializedNodes [ index ] ;
               var children = new List & lt ; Node & gt ; ( ) ;
               for ( int i = 0 ; i != serializedNode . childCount ; i ++ )
                   children . Add ( ReadNodeFromSerializedNodes ( serializedNode . indexOfFirstChild + i ) ) ;
               return new Node ( ) {
                   interestingValue = serializedNode . interestingValue ,
                   children = children
               } ;
           }
           void OnGUI ( )
           {
               Display ( root ) ;
           }
           void Display ( Node node )
           {
               GUILayout . Label ( "Value: " ) ;
               node . interestingValue = GUILayout . TextField ( node . interestingValue , GUILayout . Width ( 200 ) ) ;
               GUILayout . BeginHorizontal ( ) ;
               GUILayout . Space ( 20 ) ;
               GUILayout . BeginVertical ( ) ;
               foreach ( var child in node . children )
                   Display ( child ) ;
               if ( GUILayout . Button ( "Add child" ) )
                   node . children . Add ( new Node ( ) ) ;
               GUILayout . EndVertical ( ) ;
               GUILayout . EndHorizontal ( ) ;
           }
      }

      Beware that the serializer, including these callbacks coming from the serializer, usually do not run on the main thread, so you are very limited in what you can do in terms of invoking Unity API. (Serialization happening as part of loading a scene happens on a loading thread. Serialization happening as part of you invoking Instantiate() from script happens on the main thread). You can however do the necessary data transformations do get your data from a non-unity-serializer-friendly format to a unity-serializer-friendly-format.

      请注意,序列化程序(包括来自序列化程序的这些回调)通常不会在主线程上运行,因此就调用Unity API而言您只能做些有限的事情。 (序列化是在加载场景时发生的,它发生在加载线程上。序列化是在您从脚本调用Instantiate()的过程中发生的,发生在主线程上)。 但是,您可以进行必要的数据转换,以将您的数据从非统一化对串行化器友好的格式转换为统一化对串行化器友好的格式。

      You made it to the end!

      你做到了!

      Thanks for reading this far, hope you can put some of this information to good use in your projects.

      感谢您阅读本文,希望您可以在项目中充分利用这些信息。

      Bye, Lucas. (@lucasmeijer)

      再见,卢卡斯。 ( @lucasmeijer )

      PS: We’ll add all this information to the documentation as well.

      PS:我们还将所有这些信息添加到文档中。

      [1] I lied, the correct answer isn’t actually 729. This is because in the very very old days before we had this 7 level depth limit, Unity would just endless loop, and then run out of memory if you created a script like the Trouble one I just wrote. Our very first fix for that 5 years ago was to just not serialize fieldtypes that were of the same type as the class itself. Obviously, this was not the most robust fix, as it’s easy to create a cycle using Trouble1->Trouble2->Trouble1->Trouble2 class. So shortly afterwards we actually implemented the 7 level depth limit to catch those cases too. For the point I’m trying to make however it doesn’t matter, what matters is that you realize that if there is a cycle you are in trouble.

      [1]我撒谎了,正确的答案实际上不是729。这是因为在我们拥有7级深度限制的非常古老的日子里,Unity会无休止地循环,如果您创建了脚本,则会耗尽内存就像我刚写的《麻烦》。 对于5年前,我们的第一个解决方法是不对与类本身相同类型的字段类型进行序列化。 显然,这并不是最可靠的解决方案,因为使用Trouble1-> Trouble2-> Trouble1-> Trouble2类很容易创建一个循环。 因此不久之后,我们实际上也实施了7级深度限制来捕获这些情况。 对于我要提出的要点,但这并不重要,重要的是,您意识到如果有一个周期,您会遇到麻烦。

      翻译自: https://blogs.unity3d.com/2014/06/24/serialization-in-unity/

      unity序列化和反序列化

      • 0
        点赞
      • 3
        收藏
        觉得还不错? 一键收藏
      • 0
        评论

      “相关推荐”对你有帮助么?

      • 非常没帮助
      • 没帮助
      • 一般
      • 有帮助
      • 非常有帮助
      提交
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值