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中的序列化和反序列化的主要内容,如果未能解决你的问题,请参考以下文章

Unity中的序列化和反序列化

unity json序列化和反序列化

unity Restsharp Api Json 序列化和反序列化

unity含枚举类型json数据的序列化和反序列化

unity含枚举类型json数据的序列化和反序列化

如何在 Unity 中序列化和反序列化包含 Gameobject 和 Vector2 的字典