What is it? 序列化的概念
长篇大论起来,序列化就是一个将对象的状态信息转换为可以存储或传输到内存,数据库或者文件的过程。简而言之,就是如何存储对象以便后续调用的过程。
假设我们需要定义和存储一个向量,那这个向量的哪些字段状态我们需要保存起来?很明显是向量的三个分量X.Y,Z啦。您可以考虑将重要的数据进行序列化操作,以便在后续进行反序列化的操作。在这里我们再次简明概括一下序列化以及反序列化。
序列化: 将数据结构或对象转换成二进制串的过程
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
正如我们所分析那样,序列化经常用于存储或者转化数据。假设我们在我们的服务器上部署了一个商城,并且正常运行了数据库管理应用。那么,将内存数据((内存数据断电即丢失))写入硬盘的操作过程就是将对象序列化成数据表的实例。其他平台的数据存储操作,也属于序列化的范畴。
How Unity Does Unity的序列化工作原理
Unity作为一个游戏引擎,除了需要从外部(磁盘)加载一大堆资源,如脚本,预设以及场景等到内存,还需要将数据从原生C++端转移到C#端进行托管。序列化操作不仅限于底层数据的加载和存储,它在前端界面也有所应用,如属性窗口,编辑器重定义以及场景对象实例化等。鉴于个人能力有限,您可以在本帖子的延伸阅读(最下方)找寻其他作者关于Unity序列化的博文。我相信处女座的同学会立马谷歌一下。
已被序列化的UnityEngine.Object类会提供相应的序列化资源 :如MonoBehaviour,ScriptableObject(稍后会提到),Shader,Sprite以及Unity的基础内置资源。一般上述资源在游戏开发过程中的调用不可见,除了在脚本编写的环节才有所体现。毕竟在脚本编码过程中,您需要关注哪些字段需要被序列化。如果要对字段进行序列化,您必须遵循以下规则:
将该字段定义为public,或者为其添加【SerializeField】的属性
该字段不能使用static关键字
该字段不能使用const关键字
该字段不能使用readonly关键字
字段的数据类型必须能序列化
通过查询unity文档可知,以下类型可被序列化:
带有[Serializable]属性的自定义非抽象类
带有[Serializable]属性的自定义结构体
继承自UntiyEngine.Object的对象引用
基本数据类型(int,float,double,bool,string等)
能序列化的字段数组
能序列化的线性表
需要重点关注的是,自定义的字典类型数据是不能序列化的,就算定义其为公有或附带[SerializeField]属性。鉴于上述原因,在游戏开发过程中,应避免使用字典进行序列化。
说了那么多,让我们来实战一下。
Examples 案例
可复制以下代码到C#文件进行调试:
using UnityEngine;public class MyBehaviour : MonoBehaviour
{
[SerializeField] private int x = 3;
[SerializeField] private float y = 5.6f;
public float pi = 3.1415f;
private int mySecret = 42;
public static int myStatic = 10;
public const int myConst = 22;
public readonly int myReadOnly = 99;
public int MyProperty { get { return 100; } }
}
根据序列化原则可知,当字段含有static, const, readonly关键字或该字段定义为非公有时,不能被序列化,综上所述仅”x,y和pi”能被序列化。这些能被序列化的字段都可在脚本的属性面板进行查看。
通过对象克隆的方式,可输出并调试非序列化的字段。以下案例将阐述序列化过程中调用Instantiate方法的过程。
private void Start()
{
Debug.Log(“Pi: “ + pi + “. MySecret: “ + x+ “. MyStatic: “ + myStatic);// 译者注:MySecret的值引用的x,不知道是否错误,但我改为mySecrte时,2次输出同为42
}private void Update()
{
if (Input.GetMouseButtonDown(0))
{
pi = –4;
mySecret = –11;
myStatic = 13;
GameObject.Instantiate(gameObject);
}
}
运行上述程序并点击鼠标左键时,会在场景中引用当前对象并创建第二个对象。由于第二个对象是第一个对象的拷贝,我们是否可以认为第二个对象的值与第一个对象相同?参照输出列表发现,MySecret的值保持不变(没被序列化),而Pi的值改变了(序列化了)。
鉴于mySecret字段为private且没附带【SerializeField】属性,所以它不能被序列化。(某些场合私有字段可被序列化)。
某些场合私有字段可被序列化,这种特殊情况会在【Problems】章节中进行描述。
细心的读者肯定会发现,字段myStatic的值也发生了改变,那是否意味着它也被序列化了。答案是否定的。虽然控制台所输出的值会让我们认为该字段被序列化,但事实上仅仅因为它是静态字段,而静态字段仅属于类,而不属于类实例。如果我们新建一个空的物体【GameObject > Create Empty】并赋予它该脚本时,运行上述程序时,myStatic的值一样为13(没被序列化)。需要注意的是,Pi的值变为原来的值。
序列化的意思是说再次读取Unity时序列化的变量是有值的,不需要你再次去赋值,因为它已经被保存下来。
Unity序列化
在Unity中,在检视面板中可以看到的,就是被成功序列化了的参数。与序列化相关的常用的关键字有SerializeField,HideInInspector,NonSerialized,Serializable并可以组合使用。
- SerializeField : 表示变量可被序列化。众所周知,公有变量可以在检视面板中看到并编辑,而私有和保护变量不行。SerializeField与private,protected结合使用可以达到让脚本的变量在检视面板里可视化编辑,同时保持它的私有性的目的。
- HideInInspector : 将原本显示在检视面板上的序列化值隐藏起来。
- NonSerialized :通过此方法可以将一个公有变量不序列化并且不显示在检视面板中。
- Serializable:用在类的前面,表示该类可被序列化。
public class Test :Monobehavior
{
public int a; //序列化,显示
private int b; //不序列化,不显示
[SerializeField ] int c; //序列化,显示
[HideInInspector] public int d; //序列化,不显示
[NonSerialized ] public int e; //不序列化,不显示
public Test2 test2; //序列化,显示(可序列化的部分)
}
[Serializable ]
public class Test2
{
public int aa;
private int bb;
}
7 comments
😎
要是什么时候有人和我抢沙发就好了 💡
能不能说说具体如何用于保存数据的
沙发 😛
沙发 😛
淦
😛