Unity中的序列化和反序列化
Posted Hello Bug.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity中的序列化和反序列化相关的知识,希望对你有一定的参考价值。
一:前言
序列化是指把对象转换为字节序列的过程,而反序列化是指把字节序列恢复为对象的过程。序列化最主要的用途就是传递对象和保存对象
在Unity中保存和加载、prefab、scene、Inspector窗口、实例化预制体等都使用了序列化与反序列化
二:可序列化类型
——自定义的具有Serializable特性的非抽象、非泛型类(所有继承UnityEngine.Object的类都具有Serializable特性,如MonoBehaviour)
——自定义的具有Serializable特性的结构体(Unity内置结构体类型都都具有Serializable特性)
——所有基本数据类型,如int、string等(必须为public
或具有SerializeField特性且不能为static、const、readonly)
——可序列化类型的数组和列表(如int、string列表),栈、队列、字典等都不能被序列化
——枚举类型
三:Unity中的序列化和反序列化
最直观的就是在Unity中的检视面板可以看到字段就是被成功序列化了的参数,与序列化相关的常用关键字有SerializeField,HideInInspector,NonSerialized,Serializable
——SerializeField : 表示变量可被序列化,SerializeField与private,protected结合使用可以达到让脚本的变量在检视面板里可视化编辑,同时保持它的私有性的目的
——HideInInspector : 将原本显示在检视面板上的序列化值隐藏起来
——NonSerialized :将一个公有变量不序列化并且不显示在检视面板中
——Serializable:用在类的前面,表示该类可被序列化,Serializable不会被派生类所继承,每个类想要被序列化需要单独加Serializable特性
四:通过二进制序列化与反序列化
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using System.IO;
using System;
using System.Runtime.Serialization.Formatters.Binary;
public class Test : MonoBehaviour
[MenuItem("Tools/BinarySerialize")]
private static void BinarySerialize()
People p = new People();
Name name = new Name();
name.name1 = "l";
name.name2 = "hw";
p.name = name;
p.age = 26;
string path = Application.dataPath + "/Data/PeopleData.bytes";
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(fs, p);
fs.Close();
AssetDatabase.Refresh();
[MenuItem("Tools/BinaryDeserialize")]
private static void BinaryDeserialize()
TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>("Assets/Data/PeopleData.bytes");
MemoryStream ms = new MemoryStream(textAsset.bytes);
BinaryFormatter binaryFormatter = new BinaryFormatter();
People p = (People)binaryFormatter.Deserialize(ms);
ms.Close();
Debug.Log(p.name.name1);
Debug.Log(p.name.name2);
Debug.Log(p.age);
[Serializable]
public class People
public Name name;
public int age;
[Serializable]
public class Name
public string name1;
public string name2;
使用二进制序列化时,每个类都需要Serializable标识
只有通过二进制进行序列化和反序列化时才能调用到OnSerializing、OnSerialized、OnDeserializing、OnDeserialized这四个特性
五:通过Json序列化与反序列化
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using System.IO;
using System;
public class Test : MonoBehaviour
[MenuItem("Tools/JsonSerialize")]
private static void JsonSerialize()
People p = new People();
Name name = new Name();
name.name1 = "l";
name.name2 = "hw";
p.name = name;
p.age = 26;
string jsonString = JsonUtility.ToJson(p);
string path = Application.dataPath + "/Data/PeopleData.json";
File.WriteAllText(path, jsonString);
AssetDatabase.Refresh();
[MenuItem("Tools/JsonDeserialize")]
private static void JsonDeserialize()
string path = Application.dataPath + "/Data/PeopleData.json";
StreamReader sr = File.OpenText(path);
string jsonString = sr.ReadToEnd();
sr.Close();
People p = JsonUtility.FromJson<People>(jsonString);
Debug.Log(p.name.name1);
Debug.Log(p.name.name2);
Debug.Log(p.age);
public class People
public Name name;
public int age;
[Serializable]
public class Name
public string name1;
public string name2;
使用Json序列化时,第一层类不需要Serializable标识,其他类都需要Serializable标识
六:通过XML序列化与反序列化
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using System.IO;
using System.Xml.Serialization;
public class Test : MonoBehaviour
[MenuItem("Tools/XmlSerialize")]
private static void XmlSerialize()
People p = new People();
Name name = new Name();
name.name1 = "l";
name.name2 = "hw";
p.name = name;
p.age = 26;
string path = Application.dataPath + "/Data/PeopleData.xml";
FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8);
XmlSerializer xmlSerializer = new XmlSerializer(p.GetType());
xmlSerializer.Serialize(streamWriter, p);
streamWriter.Close();
fileStream.Close();
AssetDatabase.Refresh();
[MenuItem("Tools/XmlDeserialize")]
private static void XmlDeserialize()
string path = Application.dataPath + "/Data/PeopleData.xml";
FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(People));
People p = (People)xmlSerializer.Deserialize(fileStream);
fileStream.Close();
Debug.Log(p.name.name1);
Debug.Log(p.name.name2);
Debug.Log(p.age);
public class People
public Name name;
public int age;
public class Name
public string name1;
public string name2;
使用XML序列化时,类不需要Serializable标识,不能序列化私有变量,就算加上NonSerialized标识也无效
七:通过ScriptableObject序列化与反序列化
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using System;
public class Test : MonoBehaviour
[MenuItem("Tools/ScriptableObjectSerialize")]
private static void ScriptableObjectSerialize()
string path = "Assets/Data/PeopleData.asset";
People p = ScriptableObject.CreateInstance<People>();
Name name = new Name();
name.name1 = "l";
name.name2 = "hw";
p.name = name;
p.age = 26;
AssetDatabase.CreateAsset(p, path);
AssetDatabase.Refresh();
[MenuItem("Tools/ScriptableObjectDeserialize")]
private static void ScriptableObjectDeserialize()
People p = AssetDatabase.LoadAssetAtPath<People>("Assets/Data/PeopleData.asset");
Debug.Log(p.name.name1);
Debug.Log(p.name.name2);
Debug.Log(p.age);
public class People : ScriptableObject
public Name name;
public int age;
[Serializable]
public class Name
public string name1;
public string name2;
使用ScriptableObject序列化时,第一层类不需要Serializable标识,其他类都需要Serializable标识
八:序列化接口—ISerializationCallbackReceiver
——OnBeforeSerialize:序列化前
——OnAfterDeserialize:反序列化后
Unity中Inspector序列化显示Dictionary的方法
九:序列化特性
——OnDeserialized:序列化后
——OnDeserializing:序列化前
——OnSerialized:反序列化后
——OnSerializing:反序列化前
只有通过二进制进行序列化和反序列化时才能调用到OnSerializing、OnSerialized、OnDeserializing、OnDeserialized这四个特性
[Serializable]
public class People
public Name name;
public int age;
[OnSerializing]
virtual protected void OnSerializing(StreamingContext context)
Debug.Log("OnSerializing");
[OnSerialized]
virtual protected void OnSerialized(StreamingContext context)
Debug.Log("OnSerialized");
[OnDeserializing]
virtual protected void OnDeserializing(StreamingContext context)
Debug.Log("OnDeserializing");
[OnDeserialized]
virtual protected void OnDeserialized(StreamingContext context)
Debug.Log("OnDeserialized");
十:Unity中的Prefab
Unity中的Prefab就是游戏对象和组件经过序列化后得到的文件,当你创建一个空物体并制作成预制体后,他会序列化成一个xxx.prefab的文件,这个文件的格式可以是二进制的也可以是文本文件,通过下面的选项可以设置
为什么在运行时修改预制体上的值不会保存?
Unity其实是两层,C++层与Unity控制层,因为Unity是用C++编写的,但是我们自己编写的脚本是C#,就会有一个交互。当我们点击运行按钮时,先是把所有的序列化数据在内部创建,然后把他们存在C++这一层,然后清除Unity控制层这边所有的内存和消息,然后加载我们编写的脚本,最后再把C++层中存储的序列化数据反序列化到Unity控制层中去
在运行时修改字段的值只是更改Unity控制层上的数据,游戏运行过程中会读取这个数据,但不会保存在C++层(Native层),游戏停止后,会再次反序列化Native层中的数据,显示运行前没更改的那个数值
为什么在脚本中定义了变量值,在属性面板上改了之后,使用的是面板上的值而不是脚本中定义的值?
属性面板上的值并不是Unity调用脚本中的C#接口获取的,而是通过对象的反序列化得到的,当修改了属性面板上值后就进行了序列化操作,将值保存到了文件中,面板显示时通过反序列化将文件中的值赋给变量
实例化预制体Instantiate方法的内部过程是首先将参数original所引用的游戏对象序列化,得到序列化流后,再使用反序列化机制将这个序列化流生成一个新的游戏对象,可以说是对象的克隆操作,因此在运行中生成Prefab实例的话可以看到这些实例会带有(Clone)的标记
以上是关于Unity中的序列化和反序列化的主要内容,如果未能解决你的问题,请参考以下文章