日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity学习笔记 - Assets, Objects and Serialization

發(fā)布時間:2025/7/14 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity学习笔记 - Assets, Objects and Serialization 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Assets和Objects

? ? Asset是存儲在硬盤上的文件,保存在Unity項目的Assets文件夾內(nèi)。比如:紋理貼圖、材質(zhì)和FBX都是Assets。一些Assets以Unity原生格式保存數(shù)據(jù),例如材質(zhì)。另一些Assets需要通過處理轉(zhuǎn)換到原生格式,例如FBX。

? ? Object是一系列序列化數(shù)據(jù),這些數(shù)據(jù)描述了具體的資源實例,這可以是Unity使用的任意類型的資源,例如mesh,sprite,audio clip或animation clip。所有的Objects都是UnityEngine.Object的子類。

? ? 大部分Object類型都是Unity內(nèi)置的,但有兩個特殊類型:

? ? 1. ScriptableObject允許開發(fā)者定義他們自己的數(shù)據(jù)類型。這些類型能夠由Unity序列化和反序列化,并且在編輯器的Inspector窗口中進(jìn)行操作。

? ? 2. MonoBehaviour提供了鏈接到MonoScript的封裝。MonoScript是Unity的內(nèi)部數(shù)據(jù)類型,其中保存了指向在具體的程序集和命名空間中的具體腳本類的引用。MonoScripte不包含任何實際可執(zhí)行的代碼。

? ? Assets和Objects之間存在一對多的關(guān)系:也就是說,Asset文件內(nèi)能夠包含一個或多個Objects。

?

內(nèi)部對象引用

? ? 所有的UnityEngine.Objects都可以引用其他的UnityEngine.Objects,被引用的Objects可以和引用的Objects位于同一個Asset文件內(nèi),也可以是由其他Asset文件導(dǎo)入的。例如,材質(zhì)對象通常有一個或多個紋理對象的引用,這些紋理對象通常都是從紋理資源文件導(dǎo)入的(例如PNG或JPG)。

? ? 當(dāng)序列化的時候,這些對象由兩部分分離的數(shù)據(jù)組成:文件的GUID和Local ID。文件的GUID標(biāo)記了存儲資源的Asset文件。Local ID是局部唯一的(也就是說,在每個Asset文件中,Local ID都是唯一的),標(biāo)記了Asset文件中的每個Object。

? ? 文件的GUID存儲在.meta文件中。這些.meta文件是Unity第一次導(dǎo)入Assets時生成的,并且和Asset存儲在同一個目錄中。下圖展示了Diffuse材質(zhì)及其.meta文件:

? ? .meta文件中包含了GUID:

? ? 打開材質(zhì)文件本身,可以看到Local ID:

? ? 如果在場景中有對象使用該材質(zhì)進(jìn)行渲染,那么打開場景文件后,就會發(fā)現(xiàn)該材質(zhì)對象由GUID以及Local ID來標(biāo)記:

?

為什么使用GUID和Local ID?

? ? GUID的功能是提供文件路徑的抽象表示。只要使用GUID來關(guān)聯(lián)具體的文件,那么文件在磁盤上的位置就無關(guān)緊要了。因此可以隨意移動文件而不需要更新引用該文件的Objects(因為這些Objects存儲的都是文件的GUID)。

? ? 由于一個Asset文件可能包含多個UnityEngine.Object資源,因此需要用Local ID來明確的標(biāo)記每個不同的Object。

? ? 如果一個Asset文件關(guān)聯(lián)的GUID丟失的話,那么所有對該Asset文件中的Objects的引用都將丟失。當(dāng).meta文件丟失時,Unity會重新生成。

? ? Unity維護(hù)了具體文件路徑與GUID的映射關(guān)系。當(dāng)一個Asset被加載或?qū)霑r,就會新增一個映射項,該映射項將Asset的文件路徑和Asset文件的GUID連接在一起。如果一個Asset的.meta文件丟失但其文件路徑?jīng)]有發(fā)生變化的話,Unity能確保重新生成的.meta中記錄的GUID是保持不變的。

? ? 如果.meta文件在Unity關(guān)閉時丟失,或者Asset文件的路徑發(fā)生了變化,但.meta文件沒有跟著一起移動的話,那么所有對該Asset文件中的Objects的引用都將丟失。舉個例子,場景中的Cube使用了我創(chuàng)建的材質(zhì)Diffuse:

Diffuse材質(zhì)及其.meta文件存儲在Assets目錄下,如果現(xiàn)在在外部移動Diffuse材質(zhì)到Assets/Temp目錄下,由于沒有同時移動其.meta文件,因此Cube對其引用就會丟失:

?

資源及其導(dǎo)入

? ? 非Unity原生資源必須導(dǎo)入進(jìn)Unity中才能使用,這是通過asset importer完成的。這些improter在資源導(dǎo)入時會被自動調(diào)用,同時你也可以用AssetImporter及其子類的API來通過代碼調(diào)整資源導(dǎo)入過程。

? ? 資源導(dǎo)入的結(jié)果是一個或多個UnityEngine.Objects。在Unity中你可以看到一個父對象包含多個子對象,例如sprite atlas:。這些對象都共享同一個GUID,因為他們的源數(shù)據(jù)來自于同一個Asset文件。Unity使用Local ID來區(qū)分他們:

? ? 資源導(dǎo)入過程包含了十分耗時的操作,例如紋理壓縮。所以如果每次打開Unity都需要執(zhí)行一遍資源導(dǎo)入過程的話將會十分低效,因此,Unity將資源導(dǎo)入的結(jié)果緩存在Library文件夾中:。具體來說,存儲在以Asset文件的GUID前兩個數(shù)字命名的文件夾中,這些文件夾位于目錄Library/metadata:

實際上即使是Unity原生資源,也會將導(dǎo)入結(jié)果存儲在對應(yīng)文件中。但是原生資源不需要很長的轉(zhuǎn)換時間或重新序列化時間。?

? ?

實例ID

? ? 盡管GUID和Local ID健壯耐用,但是GUID的比較很耗時,而在運(yùn)行時我們需要有個十分高效的系統(tǒng)。因此Unity在內(nèi)部會維護(hù)一份緩存,這份緩存將GUID和Local ID轉(zhuǎn)換成獨(dú)一無二的整數(shù),這些整數(shù)被稱為Instance ID,每當(dāng)有新的Objects添加到緩存中時,Instance ID以簡單的單調(diào)遞增的方式進(jìn)行賦值。緩存維護(hù)了Instance ID,GUID和Local ID(這兩個定義了Object的源數(shù)據(jù)在磁盤上的位置)以及Object在內(nèi)存中的實例(如果Object已經(jīng)被加載到內(nèi)存中的話)之間的映射關(guān)系。這樣UnityEngine.Objects就可以維護(hù)相互之間的引用關(guān)系。通過Instance ID可以快速找到對應(yīng)的已經(jīng)加載的Object,如果對應(yīng)的Object還沒有加載,那么就可以通過GUID和Local ID來找到Object的源數(shù)據(jù),然后加載相應(yīng)的Object。

? ? 應(yīng)用程序啟動時,項目內(nèi)置對象(比如場景中使用的對象)的數(shù)據(jù)以及在Resources文件夾中的對象的數(shù)據(jù)將被初始化到Instance ID緩存中。當(dāng)運(yùn)行時有新的資源被導(dǎo)入(比如通過腳本創(chuàng)建的Texture2D對象),以及當(dāng)從AssetBundle中加載對象時,就會在緩存中添加Instance ID項。Instance ID只有在被認(rèn)為已經(jīng)過時的情況下才會從緩存中刪除,這種情況發(fā)生在一個AssetBundle被卸載時。當(dāng)一個AssetBundle被卸載時,除了會導(dǎo)致對應(yīng)的Instance ID被認(rèn)為已經(jīng)過時,Instance ID和GUID以及Local ID之間的映射數(shù)據(jù)也會被從內(nèi)存中刪除。如果AssetBundle被重新加載的話,那么從該AssetBundle中加載的每一個對象都會創(chuàng)建一個新的Instance ID。

? ? 需要注意的是在具體平臺上的一些特定事件會導(dǎo)致Objects從內(nèi)存中被刪除。比如當(dāng)iOS上的應(yīng)用程序被掛起時,圖形資源可能會從顯存中被刪除,如果這些資源是來自一個已經(jīng)被卸載的AssetBundle,那么Unity就無法重新加載這些資源了,任何對這些資源的引用也將變得無效(例如出現(xiàn)不可見的模型(missing)使用粉色的材質(zhì)(missing)來渲染)。

?

MonoScript

? ? 一個MonoBehaviour包含了一個對MonoScript的引用,而MonoScript僅僅包含了用于定位到一個具體腳本類所需的信息,他們都不包含腳本類的可執(zhí)行代碼。

? ? 一個MonoScript中包含了三個字符串:一個程序集名,一個類名以及一個命名空間名。

? ? 當(dāng)Unity構(gòu)建項目時,會將Assets文件夾下的所有腳本文件編譯到Mono程序集中。具體來說,Unity會為在Assets文件夾中使用的每種不同的編程語言編譯一個程序集,并且會將在Assets/Plugins文件夾中的腳本單獨(dú)編譯到一個程序集中。在Assets/Plugins文件夾外的C#腳本會被編譯到Assetmbly-CSharp.dll中,在Assets/Plugins文件夾外的Java腳本會被編譯到Assembly-UnityScript.dll中,Assets/Plugins中的腳本會被編譯到Assembly-CSharp-firstpass.dll中。

? ? 這些程序集(再加上預(yù)編譯的程序集)都會被包含在最終的應(yīng)用程序中:

這些程序集就是MonoScript引用的程序集。和其他資源不同,所有程序集在應(yīng)用程序第一次啟動時會被全部加載進(jìn)來。這種方式也是為什么一個AssetBundle(或者一個Scene、一個Prefab)中不包含掛載的MonoBehaviour組件中的可執(zhí)行代碼。這種方式使得不同的MonoBehaviour可以引用共同的具體類。

?

資源生命周期

? ? 有兩種加載UnityEngine.Objects的方式:自動加載和顯示的手動加載。當(dāng)一個Instance ID被解引用,其對應(yīng)的Object當(dāng)前沒有加載到內(nèi)存中,并且Object的源數(shù)據(jù)能夠被定位到時,Object會被自動加載。Objects還能夠顯示的在腳本中手動加載,例如新建一個Texture2D或通過AssetBundle.LoadAsset方式加載一個Object。

? ? 如果一個文件GUID和Local ID沒有對應(yīng)的Instance ID,或者一個Instance ID對應(yīng)的Object沒有被加載,并且其對應(yīng)的GUID和Local ID是無效的話,那么Object就不會被加載,但是引用關(guān)系仍舊會被保留,此時在Unity編輯器中就會出現(xiàn)"(Missing)"。

? ? Objects在下面三種具體的情況下會被卸載:?

? ? 1. 當(dāng)清理未被引用的Asset時,未被引用的Objects會被自動卸載。當(dāng)場景切換時或當(dāng)調(diào)用Resources.UnloadUnusedAssets函數(shù)時會觸發(fā)清理未被引用的Asset。

? ? 2. 來自Resources文件夾的Objects在調(diào)用Resources.UnloadAsset函數(shù)時會被銷毀。但是Instance ID會被保留,所以如果在Object被銷毀后,有任何先前對該對象的引用被解引用時,Unity會重新通過Instance ID找到GUID和Local ID,然后將該對象再次加載進(jìn)來。

? ? 3. 來自AssetBundle的Objects在調(diào)用AssetBundle.Unload(true)函數(shù)時會被立即銷毀,同時也會使得Instance ID,GUID和Local ID變得無效,任何對該對象的引用也會變成"(Missing)"。之后在C#中任何對該對象的訪問都會引發(fā)"NullReferenceException"異常。如果調(diào)用AssetBundle.Unload(false),從AssetBundle加載的Objects不會被銷毀,但是Instance ID對應(yīng)的GUID和Local ID會變得無效,因此如果這些對象被從內(nèi)存中釋放的話,Unity將無法再次加載他們。

?

加載大層級對象

? ? 當(dāng)序列化Unity GameObjects(例如Prefabs)時,要記住整個層級都會被序列化。也就是說,層級中每個GameObject及其組件在序列化數(shù)據(jù)中都會被獨(dú)立的表示。因此,加載和實例化具有大層級的GameObjects時會有性能影響。

? ? 當(dāng)實例化GameObjects時,實例化一個具有大層級的GameObject和實例化多個小層級的GameObjects然后將這些GameObjects組合在一起相比,需要耗費(fèi)更多的CPU時間。盡管實例化一個大層級的GameObject不需要組合GameObjects(不需要trampolining和SendTransformChanged回調(diào))的CPU時間,但這些節(jié)約的CPU時間遠(yuǎn)遠(yuǎn)比不過讀取和反實例化大層級數(shù)據(jù)的時間。

? ? 之前提到,序列化GameObjects時,整個層級中的GameObject及其組件數(shù)據(jù)都會被序列化 --- 即使這些數(shù)據(jù)是重復(fù)的。比如一個UI中有30個一樣的Button,那么Button數(shù)據(jù)會被序列化30次。在加載時,這些數(shù)據(jù)都需要從磁盤上進(jìn)行讀取,在加載大層級的GameObjects時,文件讀取時間會消耗大量CPU時間。因此,可以把重復(fù)對象從整個層級中移出來,再單獨(dú)實例化后再組合到整個層級中。

? ??

轉(zhuǎn)載于:https://www.cnblogs.com/twjcnblog/p/5673309.html

總結(jié)

以上是生活随笔為你收集整理的Unity学习笔记 - Assets, Objects and Serialization的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。