赞
踩
序列化是将对象转换为二进制流的过程,反序列化是将二进制流转换为对象的过程。序列化主要解决对象的传输问题。Unity对Unity有自己的序列化机制(方法),但没有开放成API。Unity 的一些内置功能(保存和加载、Inspector 窗口、实例化和预制件)的实现需要使用序列化。
int
、float
、double
、bool
、string
等)Serializable
属性的自定义结构体Vector2
、Vector3
、Vector4
、Rect
、Quaternion
、Matrix4x4
、Color
、Color32
、LayerMask
、AnimationCurve
、Gradient
、RectOffset
、GUIStyle
我们可以在Inspector窗口查看或修改脚本中字段的值;在游戏运行时,我们常常发现在Inspector窗口中字段的值会代替脚本中字段的值;修改这个值我们会立马在Play窗口看到效果;停止游戏时,这个值会变回原来的值,而不是最后修改的值。
能够查看是因为这个字段被序列化了,例如常见的public string name就会自动序列化了,所以我们能在Inspector窗口查看或修改name的值,具体可以看这个例子。
常用的属性有SerializeField,HideInInspector,NonSerialized,Serializable
在Inspector窗口修改字段的值时,Unity 会序列化此数据,然后反序列化在 Inspector 窗口中显示数据。这个数据存储在native层。
unity其实是两层,C++层与unity控制层,因为unity是用c++编写的,但是我们自己编写的脚本是c#,就会有一个交互。当我们点击运行按钮时,先是把所有的序列化数据在内部创建,然后把他们存在c++这一层;然后清除unity控制层这边所有的内存和消息;然后加载我们编写的脚本;最后再把c++层中存储的序列化数据反序列化到unity控制层中去。
在运行时修改字段的值只是更改unity控制层上的数据,游戏运行过程中会读取这个数据,但不会保存在C++层(Native层)。
游戏停止后,会再次反序列化Native层中的数据,显示运行前没更改的那个数值,显示时不会与Unity Scripting API 通信。
Prefab是一个或多个游戏对象和组件的序列化数据,包含对所有源物体的引用和相应的修改列表。任何派生自UnityEngine.Object的对象都可被实例化,当使用Instantiate实例化一个Prefab时,会先创建一个GameObject,随后根据引用找到源物体,反序列化得到源物体,在根据修改列表修改相应的值,最后得到实例化的物体。
Unity不能序列化属性、树结构、泛型、字典、高维数组、委托等,最直观的表现是在Inspector窗口中看不到。
Unity不支持空引用。遇到空引用时,Unity会自动构造一个对象来填补这个引用,如果这个引用的类型就是自身,例如在树结构中每个节点为Node类型,还有两个Node类型的子节点,那么会无限循环的构造对象。尽管Unity对这个循环的次数做了限制(7次),但会影响运行时的性能。解决方法是自己创建一个不用的对象。
Unity不支持多态。如果具有一个Animal基类,在某个脚本中有字段public Animal[] animals,随后放入的是继承Animal类的Dog、Cat、Fish类的实例,那么在序列化后得到Animal实例,不能识别出派生类。解决方法是基类要继承UnityEngine.Object或Monobehaviour。
对于高维数组,将其低维化。,即底层采用一维数组来替代。
对于字典,key和value各自存储成List,运行时用字典,序列化时用数组。
对于泛型类,用一个新类将其封装并用 [Serializable]
修饰新类。
对于不带返回值的委托,可以用 UnityEvent
来序列化。
对于更复杂的情况,例如树结构,需实现ISerializationCallbackReceiver。
- using UnityEngine;
- using System.Collections.Generic;
- using System;
-
- //直接序列化将导致性能问题
- public class VerySlowBehaviourDoNotDoThis : MonoBehaviour {
- [Serializable]
- public class Node {
- public string interestingValue = "value";
- //下面的字段使序列化数据变得巨大,
- //因为它引入了"类周期"。
- public List<Node> children = new List<Node>();
- }
- //这将经过序列化
- 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 ();
- }
- }
- using System.Collections.Generic;
- using System;
- //避免直接序列化,先处理成Unity支持的形式
- public class BehaviourWithTree : MonoBehaviour, ISerializationCallbackReceiver {
- // 在运行时使用的 Node 类。
- //此类位于 BehaviourWithTree 类的内部,不会被序列化。
- public class Node {
- public string interestingValue ="value";
- public List<Node> children = new List<Node>();
- }
- // 我们将用于序列化的 Node 类。
- [Serializable]
- public struct SerializableNode {
- public string interestingValue;
- public int childCount;
- public int indexOfFirstChild;
- }
- //用于运行时树表示的根节点。不序列化。
- Node root = new Node();
- //这是我们提供给 Unity 进行序列化的字段。
- public List<SerializableNode> serializedNodes;
- public void OnBeforeSerialize() {
- //Unity 即将读取 serializedNodes 字段的内容。
- // 现在必须"及时"将正确的数据写入该字段。
- if (serializedNodes == null) serializedNodes = new List<SerializableNode>();
- if (root == null) root = new Node ();
- serializedNodes.Clear();
- AddNodeToSerializedNodes(root);
- // 现在 Unity 可自由地序列化这个字段,我们应该在稍后反序列化时
- // 找回预期的数据。
- }
- 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 刚刚将新数据写入 serializedNodes 字段。
- //让我们用这些新值填充我们的实际运行时数据。
- if (serializedNodes.Count > 0) {
- ReadNodeFromSerializedNodes (0, out root);
- } else
- root = new Node ();
- }
- int ReadNodeFromSerializedNodes(int index, out Node node) {
- var serializedNode = serializedNodes [index];
- //将反序列化的数据传输到内部 Node 类
- Node newNode = new Node() {
- interestingValue = serializedNode.interestingValue,
- children = new List<Node> ()
- }
- ;
- // 以深度优先的方式读取树,因为这正是我们写入树的方式。
- for (int i = 0; i != serializedNode.childCount; i++) {
- Node childNode;
- index = ReadNodeFromSerializedNodes (++index, out childNode);
- newNode.children.Add (childNode);
- }
- node = newNode;
- return index;
- }
- // 此 OnGUI 在 Game 视图中绘制出节点树,其中包含用于添加新节点作为子项的按钮。
- void OnGUI() {
- if (root != null)
- 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 ();
- }
- }
还可以采用Json格式对类进行序列化,使用JsonUtility 类可在 Unity 对象与 JSON格式之间来回转换。基准测试表明,JsonUtility比流行的 .NET JSON 解决方案要快得多。使用方式参考下面的代码。
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class test : MonoBehaviour
- {
-
-
- // Start is called before the first frame update
- void Start()
- {
- Data date = new Data();
- date.num = 1;
- date.home = "sdfew";
- date.name = "enternalstar";
- string json = JsonUtility.ToJson(date);//将对象Json序列化
- Data getData = JsonUtility.FromJson<Data>(json);//反序列化再得到对象
- Data newData = new Data();
- newData.name = "saff";
- newData.num = 3;
- newData.home = "safjoi";
- JsonUtility.FromJsonOverwrite(json, newData);//将其他Data对象的数据覆盖到newData上
-
- }
-
- [Serializable]//自定义一个需要序列化的类
- public class Data
- {
- public int num;
- public string home;
- public string name;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。