Unity中的序列化與反序列化

Unity的數(shù)據(jù)存儲(chǔ),本地類 PlayerPrefs, Inspector,以及Prefab等都使用了序列化與反序列化的知識.
循序漸進(jìn),讓我們一步步了解Unity中的序列化和反序列化的知識;

流與格式化器

序列化: 將對象轉(zhuǎn)換為字節(jié)流.
反序列化: 將字節(jié)流轉(zhuǎn)換為對象.
直接講概念太抽象,我們先來看代碼;

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class Test : MonoBehaviour
{
    private void Start()
    {
        Hero hero_ins = new Hero();
        hero_ins.id = 100;
        hero_ins.attack = 99f;
        hero_ins.defence = 99f;
        hero_ins.name = "Calabash";

        Stream st = FormatInstanceToMemory(hero_ins);
        st.Position = 0;
        hero_ins = null;

        hero_ins = MemoryToInstance(st) as Hero;
        Debug.Log(hero_ins.id.ToString());
        Debug.Log(hero_ins.attack.ToString());
        Debug.Log(hero_ins.defence.ToString());
        Debug.Log(hero_ins.name);
    }

    //序列化 把實(shí)例對象寫入流中
    private static MemoryStream FormatInstanceToMemory(object instance)
    {
        //創(chuàng)建一個(gè)流
        MemoryStream ms = new MemoryStream();
        //創(chuàng)建格式化器
        BinaryFormatter bf = new BinaryFormatter();
        //序列化為二進(jìn)制流
        bf.Serialize(ms, instance);

        return ms;
    }
    //反序列化, 從流中讀出實(shí)例對象
    private static object MemoryToInstance(Stream st)
    {
        //創(chuàng)建格式化器
        BinaryFormatter bf = new BinaryFormatter();
        //把二進(jìn)制流反序列化為指定的對象
        return bf.Deserialize(st);
    }

}

關(guān)于Hero類的定義如下:

[Serializable] //注意這個(gè)關(guān)鍵字
public class Hero
{
    public int id;
    public float attack;
    public float defence;
    public string name;
}

代碼中的注釋已經(jīng)寫得很清楚了,通過代碼我們要解釋三個(gè)概念;

  • 流(Stream): Unity中的二進(jìn)制數(shù)據(jù)流,有 MemoryStream, FileStream 等子類來處理不同場景的數(shù)據(jù)流,但我們這里不討論每種流的用法,只需要讓大家理解 Stream提供了一個(gè)用來容納經(jīng)過序列化之后的字節(jié)塊的容器
    更多的Stream知識可以查閱這里: Unity的Stream流
  • 格式化器: 使用序列化和反序列的工具,代碼中只是使用到了 BinaryFormatter 這種格式化器,其實(shí)還有 SoapFormatter (需要導(dǎo)入對應(yīng)的.dll文件),需要注意的是進(jìn)行序列化和反序列的操作必須是相同的格式化器,否則可能會(huì)拋出System.Runtime.Serialization.SerializationException異常.
  • [Serializable]特性: 默認(rèn)自定義的類型是無法被序列化的,需要使用 [Serializable] 特性來實(shí)現(xiàn)序列化與反序列化,關(guān)于此特性更多的內(nèi)容見下節(jié);

通過上面的示例,我們往流中寫入了一個(gè)對象,那么可以寫入兩個(gè),甚至多個(gè)不同的對象么?答案是肯定的,我們還是用代碼測試一下;

using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class Test : MonoBehaviour
{
    private void Start()
    {
        Hero hero_ins = new Hero();
        hero_ins.id = 100;
        hero_ins.attack = 99f;
        hero_ins.defence = 99f;
        hero_ins.name = "Calabash";

        Soldier soldier_ins = new Soldier();
        soldier_ins.life = 50;
        soldier_ins.weapon = "hammer";

        //創(chuàng)建一個(gè)流
        MemoryStream ms = new MemoryStream();
        //創(chuàng)建格式化器
        BinaryFormatter bf = new BinaryFormatter();
        //序列化為二進(jìn)制流
        bf.Serialize(ms, hero_ins);
        bf.Serialize(ms, soldier_ins);

        ms.Position = 0;
        hero_ins = null;
        soldier_ins = null;

        //從數(shù)據(jù)流中讀出數(shù)據(jù)
        //讀出的順序不能顛倒,因?yàn)槭菑膍s的開端讀取,因此要按寫入的順序讀取
        hero_ins = bf.Deserialize(ms) as Hero;
        soldier_ins = bf.Deserialize(ms) as Soldier;


        Debug.Log("hero: " + hero_ins.id.ToString());
        Debug.Log("hero: " + hero_ins.attack.ToString());
        Debug.Log("hero: " + hero_ins.defence.ToString());
        Debug.Log("hero: " + hero_ins.name);
        Debug.Log("soldier: " + soldier_ins.life.ToString());
        Debug.Log("soldier: " + soldier_ins.weapon);
    }

[Serializable]與[NonSerialized]的繼承

1. [Serializable]

該特性只能用于以下類型:

  • 引用類型(class)
  • 值類型(struct)
  • 枚舉類型(enum)
  • 委托類型(delegate)

該特性不會(huì)被派生的子類繼承;

[Serializable] //注意這個(gè)關(guān)鍵字
public class Hero
{
    public int id;
    public float attack;
    public float defence;
    public string name;
}


public class GirlHero : Hero
{
    public int girlAge;
}
public class Test : MonoBehaviour
{

    private void Start()
    {
        Hero hero_ins = new Hero();
        hero_ins.id = 100;
        hero_ins.attack = 99f;
        hero_ins.defence = 99f;
        hero_ins.name = "Calabash";

        GirlHero girl_ins = new GirlHero();
        girl_ins.girlAge = 18;

        //創(chuàng)建一個(gè)流
        MemoryStream ms = new MemoryStream();
        //創(chuàng)建格式化器
        BinaryFormatter bf = new BinaryFormatter();
        //序列化為二進(jìn)制流
        bf.Serialize(ms, hero_ins);
        bf.Serialize(ms, girl_ins);

        ms.Position = 0;
        hero_ins = null;
        girl_ins = null;

        //從數(shù)據(jù)流中讀出數(shù)據(jù)
        //讀出的順序不能顛倒,因?yàn)槭菑膍s的開端讀取,因此要按寫入的順序讀取
        hero_ins = bf.Deserialize(ms) as Hero;
        girl_ins = bf.Deserialize(ms) as GirlHero;


        Debug.Log("hero: " + hero_ins.id.ToString());
        Debug.Log("hero: " + hero_ins.attack.ToString());
        Debug.Log("hero: " + hero_ins.defence.ToString());
        Debug.Log("hero: " + hero_ins.name);
        Debug.Log("girl: " + girl_ins.girlAge.ToString());

    }
}

點(diǎn)擊運(yùn)行后,果不其然會(huì)報(bào) SerializationException 的一個(gè)錯(cuò)誤:

SerializationException: Type 'GirlHero' in Assembly 'Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

錯(cuò)誤信息很明顯,我們的 GirlHero 類沒有標(biāo)記 Serializable 特性,當(dāng)我們給這個(gè)類也標(biāo)記上該特性后,結(jié)果可以正常被打印;

我們在來看另外一種情況,只有派生類使用特性,基類不使用:

public class Hero
{
    public int id;
    public float attack;
    public float defence;
    public string name;
}

[Serializable]
public class GirlHero : Hero
{
    public int girlAge;
}

運(yùn)行后報(bào)錯(cuò)如下:

SerializationException: Type 'Hero' in Assembly 'Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

通過這個(gè)測試,我們可以知道:

  1. Serializable特性不會(huì)被繼承,每個(gè)派生類如果要想被序列化,需要單獨(dú)添加此特性字段.
  2. 派生類添加了 Serializable 特性,而基類不使用,那么從基類派生的任何類都無法被序列化.
    可以這么理解,基類如果無法被序列化,那么它的字段無法被序列化,派生類同樣包含該基類的字段,那么自然也是無法被序列化的.C#中的所有類都是繼承自 System.Object 類,這個(gè)類已經(jīng)應(yīng)用了 Serializable 特性.

2. [NonSerialized]

在默認(rèn)情況下,序列化會(huì)讀取對象的所有字段,無論這些字段生命的訪問權(quán)限是 public 還是 private, 如果我們有些敏感字段或者計(jì)算屬性不想被序列化,有沒有辦法呢?
在不想被序列化的字段上面使用 NonSerialized 屬性即可;

[Serializable] //注意這個(gè)關(guān)鍵字
public class Hero
{
    public int id;
    [NonSerialized]
    public float attack;
    public float defence;
    public string name;
    
}

使用上面Test的腳本,打印結(jié)果如下:

NonSerialized特性的使用

我們可以看到反序列化后,被標(biāo)記為 NonSerialized 特性的字段值變?yōu)榱?,這是由于
attack 字段不能被序列化,它的值99并不會(huì)寫入到流中,因此被反序列化后,其余字段都能夠被正常賦值,該字段由于從流中讀取不到對應(yīng)的值,只能設(shè)置為0;
那么能不能在反序列化的時(shí)候,把正確的值賦值回去呢?答案是肯定的,我們下節(jié)再來解決這個(gè)問題,我們繼續(xù)查看 NonSerialized 的繼承特點(diǎn);

[Serializable] //注意這個(gè)關(guān)鍵字
public class Hero
{
    public int id;
    [NonSerialized]
    public float attack;
    public float defence;
    public string name;
    
}

[Serializable]
public class GirlHero : Hero
{
    public int girlAge;
}
public class Test : MonoBehaviour
{

    private void Start()
    {
        GirlHero girl_ins = new GirlHero();
        girl_ins.id = 100;
        girl_ins.attack = 99f;
        girl_ins.defence = 99f;
        girl_ins.name = "Calabash";
        girl_ins.girlAge = 18;

        //創(chuàng)建一個(gè)流
        MemoryStream ms = new MemoryStream();
        //創(chuàng)建格式化器
        BinaryFormatter bf = new BinaryFormatter();
        //序列化為二進(jìn)制流
        bf.Serialize(ms, girl_ins);

        ms.Position = 0;
        girl_ins = null;

        //從數(shù)據(jù)流中讀出數(shù)據(jù)
        girl_ins = bf.Deserialize(ms) as GirlHero;

        Debug.Log("girl_id: " + girl_ins.id.ToString());
        Debug.Log("girl_attack: " + girl_ins.attack.ToString());
        Debug.Log("girl_defence: " + girl_ins.defence.ToString());
        Debug.Log("girl_name: " + girl_ins.name);
        Debug.Log("girl_age: " + girl_ins.girlAge.ToString());

    }
}

打印結(jié)果如下:

NonSerialized特性可以被繼承

通過上面的測試可以得知: [NonSerialized] 特性可以被派生類繼承;


控制序列化和反序列化的流程

在上一節(jié)提出的問題,對于 NonSerialized 修飾的字段,在反序列化的時(shí)候應(yīng)該如何賦值,以及如果我們想在序列化和反序列化之前和之后做些操作,應(yīng)該怎么實(shí)現(xiàn)?

[Serializable]
public class GirlHero : Hero
{
    public int girlAge;

    [OnDeserialized]
    private void CaculateAttack(StreamingContext context)
    {
        this.attack = 1000;
    }
}

在上一節(jié)的代碼基礎(chǔ)上,我們對 GirlHero 做了上面的改動(dòng),增加了一個(gè) CaculateAttack 方法,并且使用了 [OnDeserialized] 特性,我們再來看打印結(jié)果:

控制反序列化

通過這樣的方法和特性我們對 attack 字段在反序列化的時(shí)候進(jìn)行了賦值;
從特性的名字可以看出,是在反序列化過程完成后調(diào)用所修飾的方法,還有其他三個(gè)相關(guān)特性我們一起來看看;

1. 序列化與反序列化過程的方法特性

  • OnSerializing :格式化器在序列化對象字段之前,調(diào)用該特性修飾的方法.
  • OnSerialized :格式化器在序列化對象字段之后,調(diào)用該特性修飾的方法.
  • OnDeserializing :格式化器在反序列化對象字段之前,調(diào)用該特性修飾的方法.
  • OnDeserialized ::格式化器在反序列化對象字段之后,調(diào)用該特性修飾的方法.

這幾個(gè)特性是在 System.Runtime.Serialization 命名空間下,共同點(diǎn)是用來修飾類型中定義的方法;注意他們的調(diào)用時(shí)機(jī).

2. StreamingContext

在上面的實(shí)例代碼中,可以看到方法參數(shù)是一個(gè) StreamingContext 類,這個(gè)類是序列化與反序列化時(shí)流的上下文,我們通過程序集可以看到,這個(gè)類型是一個(gè)值類型.

public struct StreamingContext
{
    //調(diào)用方定義的附加上下文引用,一般為空
    public object Context {
        get;
    }
    //用來標(biāo)記序列化和反序列對象的來源和目的地 
    public StreamingContextStates State {
        get;
    }
    //構(gòu)造方法
    public StreamingContext (StreamingContextStates state);

    public StreamingContext (StreamingContextStates state, object additional);
    //重載System.Object方法
    public override bool Equals (object obj);

    public override int GetHashCode ();
}

通過State的屬性我們可以查看序列化和反序列化時(shí)對應(yīng)的來源和目的地,更多的信息請查閱這里:StreamingContextStates枚舉
我們在上面序列化時(shí)使用的格式化器的 Context 屬性就是 StreamingContext, 它的 State 屬性默認(rèn)是All,我們也可以在創(chuàng)建格式化器的時(shí)候手動(dòng)指定 State 的類型來滿足不同的需求,比如:

//指定state類型,深度克隆一個(gè)對象
BinaryFormatter bf = new BinaryFormatter();
bf.Context = new StreamingContext(StreamingContextStates.Clone);

Unity的Inspector

在屬性監(jiān)視板中可以看到游戲腳本中某個(gè)對象的信息,這些字段和值并不是Unity調(diào)用游戲腳本中的C#接口獲取的,而是通過顯示對象的反序列化得到這些屬性數(shù)值,然后在面板中展示出來;


Unity的Prefab

Prefab是Unity中很重要的一種資源類型,真正實(shí)現(xiàn)了游戲?qū)ο蟮目寺?預(yù)制體是游戲?qū)ο蠛徒M件經(jīng)過序列化后得到的文件,它的格式可以是二進(jìn)制的也可以是文本文件,可以通過下面的選項(xiàng)來設(shè)置:

資源格式設(shè)置

它的特點(diǎn)如下:

  • 可以被放入多個(gè)場景中,也可以在一個(gè)場景中放入多個(gè)
  • 在場景中增加一個(gè)Prefab,就實(shí)例化了一個(gè)該P(yáng)refab的實(shí)例
  • 所有的Prefab實(shí)例都是Prefab的克隆,因此在運(yùn)行中生成Prefab實(shí)例的話可以看到這些實(shí)例會(huì)帶有(Clone)的標(biāo)記
  • 只要Prefab的原型發(fā)生了變化,場景中所有的prefab實(shí)例都會(huì)發(fā)生變化

腳本創(chuàng)建Prefab實(shí)例我們都是通過Instantiate方法:

public static Object Instantiate (Object original, Vector3 position, Quaternion rotation)

在該方法內(nèi)部,會(huì)首先將參數(shù)original所引用的游戲?qū)ο笮蛄谢?得到序列化流后,再使用反序列化機(jī)制將這個(gè)序列化流生成一個(gè)新的游戲?qū)ο?可以說是對象的克隆操作;


Unity在System.Runtime.Serialization命名空間下定義了一個(gè)FormatterServices的類型,只包含一些靜態(tài)方法,用來輔助序列化與反序列化的過程;

序列化過程

  1. 調(diào)用FormatterServices的 GetSerializableMembers ;
//兩個(gè)重載版本
//type: 正在序列化或克隆的類型
//context: 發(fā)生序列化的上下文
//MemberInfo[]: 返回類型對象的數(shù)組,每一個(gè)元素都對應(yīng)一個(gè)可以成員字段的名稱
public static MemberInfo[] GetSerializableMembers(Type type, StreamingContext context)
public static MemberInfo[] GetSerializableMembers(Type type)
  1. 調(diào)用FormatterServices的 GetObjectData ;
//obj: 表示要寫入序列化程序的對象實(shí)例
//members: 代表的是第一步提取的成員字段的名稱
//Object[]: 返回的是對應(yīng)members中每個(gè)元素表示的字段對應(yīng)的值,理解為Value的集合
public static Object[] GetObjectData(Object obj, MemberInfo[] members)
  1. 經(jīng)過前兩個(gè)步驟獲取了對象的成員和其對應(yīng)的值,這一步先把程序集標(biāo)識以及類型的完整名稱寫入流中.
  2. 格式化器遍歷第一步與第二步得到的數(shù)組獲取成員名稱和其對應(yīng)的值,將這些信息寫入流中.

反序列化過程

  1. 格式化器從流中讀取程序集標(biāo)識和完整的類型名稱,然后調(diào)用FormatterServices的 GetTypeFromAssembly ;
//assem: 讀取到的程序集標(biāo)識
//name: 完整的類型名稱
//Type: 返回值便是反序列化對象的實(shí)際類型
public static Type GetTypeFromAssembly(Assembly assem, string name)
  1. 獲取了對象的類型后,接下來就是要在為新的對象分配一塊內(nèi)存空間,調(diào)用FormatterServices的 GetUninitializedObject ;
//為指定類型分配內(nèi)存空間
public static Object GetUninitializedObject(Type type)

需要注意的是,此時(shí)還沒有調(diào)用構(gòu)造函數(shù),對象的所有字節(jié)都被初始化為 null 或者 0 ;

  1. 分配好內(nèi)存空間后,還是調(diào)用FormatterServices的 GetSerializableMembers 構(gòu)造并初始化一個(gè)新的 MemberInfo 數(shù)組;
    這個(gè)方法的說明見序列化過程的第一步;
    調(diào)用方法后獲取該類型的所有成員字段名稱的集合 MemberInfo[] members ;
  2. 獲取到字段信息后,這一步就要獲取字段對應(yīng)數(shù)組的信息;格式化器會(huì)根據(jù)流中包含的數(shù)據(jù)創(chuàng)建一個(gè) Object 數(shù)組,對其進(jìn)行初始化;
    到了這一步,你就有了一個(gè)未初始化的對象,一個(gè)成員變量集合和對應(yīng)數(shù)值的集合;
  3. 這一步就要調(diào)用FormatterServices的 PopulateObjectMembers 方法對實(shí)例對象初始化;
//obj: 表示剛才創(chuàng)建要被初始化的對象實(shí)例
//members: 對象需要被填充的成員或者屬性
//data: 對象需要被填充的成員或者屬性對應(yīng)的數(shù)值
//Object: 返回一個(gè)初始化好的實(shí)例對象
public static Object PopulateObjectMembers(Object obj, MemberInfo[] members, Object[] data)

參考文章: <<Unity3D腳本編程>> 陳嘉棟

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市群发,隨后出現(xiàn)的幾起案子结执,更是在濱河造成了極大的恐慌管嬉,老刑警劉巖膜蠢,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忿等,死亡現(xiàn)場離奇詭異棺滞,居然都是意外死亡纷铣,警方通過查閱死者的電腦和手機(jī)将饺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門贡避,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人予弧,你說我怎么就攤上這事刮吧。” “怎么了掖蛤?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵皇筛,是天一觀的道長。 經(jīng)常有香客問我坠七,道長水醋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任彪置,我火速辦了婚禮拄踪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拳魁。我一直安慰自己惶桐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著姚糊,像睡著了一般贿衍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上救恨,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天贸辈,我揣著相機(jī)與錄音,去河邊找鬼肠槽。 笑死擎淤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秸仙。 我是一名探鬼主播嘴拢,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寂纪!你這毒婦竟也來了席吴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤捞蛋,失蹤者是張志新(化名)和其女友劉穎抢腐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體襟交,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年伤靠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捣域。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡宴合,死狀恐怖焕梅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卦洽,我是刑警寧澤贞言,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站阀蒂,受9級特大地震影響该窗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚤霞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一酗失、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昧绣,春花似錦规肴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽删壮。三九已至,卻和暖如春兑牡,著一層夾襖步出監(jiān)牢的瞬間央碟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工发绢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硬耍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓边酒,卻偏偏與公主長得像经柴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子墩朦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容

  • JAVA序列化機(jī)制的深入研究 對象序列化的最主要的用處就是在傳遞,和保存對象(object)的時(shí)候,保證對象的完整...
    時(shí)待吾閱讀 10,878評論 0 24
  • 官方文檔理解 要使類的成員變量可以序列化和反序列化坯认,必須實(shí)現(xiàn)Serializable接口。任何可序列化類的子類都是...
    獅_子歌歌閱讀 2,414評論 1 3
  • 前言 Prefab氓涣,也就是大家熟知的預(yù)制件(本文中牛哺,我們依然使用它的英文名字——Prefab),它是Unity中一...
    windknife閱讀 21,071評論 9 22
  • 一劳吠、序列化 1引润、序列化的作用 Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下痒玩,只有當(dāng)JVM處于...
    慕凌峰閱讀 4,071評論 0 8
  • 【小小陪伴】20170609學(xué)習(xí)力踐行記錄D25:早上淳附,我去醫(yī)院照顧姥爺之前,把妮妮送到姥姥那蠢古,她問我“媽媽奴曙,姥爺...
    睿依show閱讀 138評論 0 0