Unity文件引用、meta文件詳解


引用自:

https://www.cnblogs.com/CodeGize/p/8697227.html

 https://www.jianshu.com/p/2a7c4a48aaee

 

----------------------------------  帖子 1 ------------------------------------------

在unity3d中一般存在這么幾種文件

  • 資源文件(Imported Asset)
  • 代碼文件
  • 序列化文件(Native Asset)
  • 文本文檔
  • 非序列化文件
  • meta文件

 

資源文件(ImportedAsset)

資源文件指一些創建好的,並且不再修改的文件。這樣的文件一般是美術設計師,音頻視頻設計師創造的文件,比如FBX文件,貼圖文件,音頻文件,視頻文件,動畫文件(雖然動畫文件可以被認為是配置文件,不過在由於一般不會去做修改,所以也認為是資源文件)。像這類文件,unity中都會在導入時進行轉化。每一個類型都對應一個AssetImporter,比如AudioImporter,TextureImporter,ModelImport等等。在unity中點擊這樣的資源,在Inspector面板會出現設置界面,如下圖所示是一個FBX的設置界面:

 

 

 

代碼文件

代碼文件包括所有的代碼文件,代碼庫文件,shader文件等,在導入時,unity會進行一次編譯。

序列化文件(Native Asset):

序列化文件通常是指unity能夠序列化的文件,一般是unity自身的一些類型。比如prefab(預制體),unity3d(場景)文件,asset(ScriptableObject)文件.mat文件(材質球),這些文件能夠在運行時直接反序列化為對應類的一個實例。

 

文本文檔

文本文檔比較特殊,它不是序列化文件,但是unity可以識別為TextAsset。很像資源文件,但是又不需要資源文件那樣進行設置和轉化。比如txt、xml文件等等。

非序列化文件

非序列文件是Unity無法識別的文件,比如一個文件夾也會被認為是一個文件,但是無法識別。

 

 

 

Meta文件

meta文件在unity中的作用非常關鍵,它有2個作用

  • 定義在它同目錄下,同名的非meta文件的唯一ID:GUID。而對於unity的序列化文件來說,引用的對象用的就是這個GUID。所以一旦meta中的GUID變更了,就要注意,它很可能引起一場引用丟失的災難
  • 存儲資源文件的ImportSetting數據。在上文中資源文件是有ImportSetting數據的,這個數據正數存儲在meta文件中。ImportSetting中專門有存儲Assetbundle相關的數據。這些數據幫助編輯器去搜集所有需要打包的文件並分門別類。所以每一次修改配置都會修改meta文件。

GUID 和 MD5 是完全不同的兩個東西,首先 guid 的概念只存在於 unity 體系,而 MD5 則是擴大到整個文件系統。

guid 用來識別不同的文件,它是根據 Path 路徑來生成的;

而 MD5 是根據文件內容進行的算法,同一個文件 被修改前后 ,其 MD5 的值是不同 的,所以 MD5被稱為“文件指紋”。

總結來說,guid 的因變量是 文件路徑(當然也包括文件名),而MD5 的因變量是文件的內容(改文件名不會影響md5的值)。

 

Meta文件詳解——Unity GUID/LocalID系統

 

meta文件實質上是一個文本文檔,只是采用的是一種叫做 YAML 的格式來寫的(https://docs.unity3d.com/Manual/FormatDescription.html)。

unity中的序列化文件都是用這個格式類寫的,比如prefab,場景等。

我們使用nodepad++打開一個meta文件,然后在菜單中將語言設置為YAML,如下圖所示:

 

 

這是一個asset文件(ScriptableObject)的meta文件。

 

GUID

guid是meta中最最最重要的數據。這個guid代表了這個文件,無論這個文件是什么類型(甚至是文件夾)。換句話說,通過GUID就可以找到工程中的這個文件,無論它在項目的什么位置。在編輯器中使用 AssetDatabase.GUIDToAssetPathAssetDatabase.AssetPathToGUID進行互轉。

所以在每次svn提交時如果發現有meta文件變更,一定要打開看一下。看看這個guid是否被更改。理論上是不需要更改的。

 

ImportSetting數據

后面比較重要的數據是ImportSetting數據。根據不同的文件類型,它的數據是不同的ImportSetting數據,比如上面的NativeFormatImporter,ModelImporter,AudioImporter等等。只要對照Inspector面板中的條目,都可以看懂每一行的意義。

所以知道這個之后,我們可以發現,假如我們把一個文件和這個文件的meta文件從一個Unity工程復制到另一個Unity工程中,它的配置是不會變的。(以前在2個工程手動裁剪同一個模型的20個動畫真是傻到家了,直接將這個fbx和它的meta文件拷貝過去就行!)

FileID(LocalID)

有一個問題是,如果是一個圖集,下面有若干個圖片,那么,這個GUID怎么對應一個文件呢?是的,對於一個文件下有多個文件的情況,就需要另外一個ID來表示,這就是LocalID。更習慣用meta文件中的名字FileID。

FileID存儲方式有2種

  • 對於資源文件,非序列化文件,由於一般不會去更改源文件,所以FileID存儲在meta文件中。以下是一個fbx文件的meta文件

 

 

  • 對於序列化文件,自身數據里面會存儲自身的FileID,也會記錄所有子文件的FileID(更多關於序列化文件的數據,見下文)。比如對於這樣的AnimatorController。它本身的數據如下,除了本身的FileID為9100000外,記錄了4個AnimatorClip的FileID。而meta文件中只有自身的FileID

 

 

 

 

 

 

回到本節一開始的問題,如果是圖集,因為是圖片本身是資源文件,所以會有FileID存儲在對應的meta文件中。打開任意一個圖集,比如以下這個圖集對應的meta文件

 

 

 

 

至此就是整個Unity的GUID/LocalID系統的基礎了。通過GUID找到任何一個文件,通過FileID找到其中的某個子文件。

序列化文件詳解——Unity文件引用系統

上文已經提到,對於所有的序列化文件,unity采用的是YAML來書寫。所以對於一個unity3d(場景)文件,prefab文件,材質,控制器等,都可以用文本文檔軟件打開。這里還是用Notepad++打開。

為了能夠簡潔地說明問題,我們在unity中創建一個新的場景,然后創建2個Cube,一個做成Prefab。如圖所示:

 

 

保存之后,用Notepad++打開這個1.unity3d。然后在菜單中設置語言為YAML。

 

 

可以看到大概的數據

  • OcclusionCullingSettings裁剪數據(菜單Window->Occlusion面板中的數據)
  • RenderSettings(菜單Window->Lighting->Settings面板中的部分數據)
  • LightmapSettings(菜單Window->Lighting->Settings面板中的其他部分數據)
  • NavMeshSettings(菜單Window->Navigation面板中的數據)
  • 之后就是場景中的物件的數據

GameObject數據

展開第一個GameObject,可以看到這個的Name就是Main Camera。這個物體上有4個組件,一一對應下面的數據。這就是物體內的引用關系。每一個Unity對象都會有一個FileID,然后在需要引用時,使用這些FileID即可。所以在實例化一個這樣的GameObject時,只要依照次序,依次創建物體,組件,初始化數據並進行引用綁定即可在場景中生成一個實例。

 

 

我們在Inspector面板中的右上角點擊,然后選擇Debug轉成Debug模式下的Inspector面板

 

 

在Hierarchy面板中選中Main Camera可以看到如圖所示,所有的組件的LocalIdentfierInFile的值就是剛剛在Notepad++中看到的數據

 

 

這里有一點,我們看到有一個叫做InstanceID的數據。這個是unity中一個實例的ID。每一個Unity實例都會有一個InstanceID。在運行時,可以使用UnityEngine.Object的GetInstanceID獲取。但是要注意的是,每一次運行,相當於重新生成了新的實例,所以這個值是可變的。(更多細節參考《Unity編輯器下和運行時的加載過程》)

組件數據

在GameObject之后就是這個GameObject的組件數據(不知道次序會不會亂,理論上不影響)。每一個組件的數據基本上就是這個組件的一堆參數了。可以結合Unity中這個組件的面板來了解每一個數據的意義。

這里有一個問題,比如這里有一個組件是FlareLayer,但是在YAML里面只是一個Behaviour(所有Behaviour組件都看不到類型名字),怎么樣才能知道他是一個FlareLayer?

 

 

可以看到在這個數據上方,在FileID左邊我們看到一個124。對,這個就是FlareLayer。請參考YAML Class ID Reference,每一個unity類型都有一個對應的數字。

那么自定義腳本類呢?

我們創建一個Test腳本,繼承MonoBehaviour。里面什么都不寫。添加到Main Camera物體上。保存場景然后回到Notepad++。

 

 

可以看到多了一個MonoBehaviour,並且這個里面有一個m_Script數據,指向對應的GUID及其FileID。上文我們已經說了,任何一個文件都可以通過GUID找到,然后通過FileID找到它內部的子文件。所以這樣就能識別出這個具體是什么類了。

我們往Test中寫2個字段

public int A;

public Test RefTest;

在Main Camera中,設置Test腳本的A值為111,RefTest設置為自身。保存后回到Notepad++;

 

 

看到數據想必都明白了。

可以往Test中寫一些其他類型的數據,看看這些序列化數據放在YAML的哪個位置!這里不再展開(這些數據和編輯器的SerializedProperty息息相關)

Prefab數據

在YAML的最下面有一個數據是Prefab數據

 

 

看起來很復雜,但是實際上,它就保存了最重要的幾個數據

  • Modification:每個組件的修改數據列表,但凡修改的數據,都會在這里體現。
  • ParentPrefab:表示是哪一個Prefab。

所以上面的數據就是GUID為161b04a3180d83a4080b19801daaf356的Prefab,修改后的FileID為4040867914032966的一堆數據和FileID為1205630099335284的Name數據。

通過打開我們制作的Cube的Prefab文件及其meta文件,我們可以看到,meta文件中的GUID就是那個,而Prefab中存在4040867914032966(Transform)和1205630099335284(GameObject)

 

OK,至此,序列化文件的數據和引用的原理都已完畢

 

----------------------------------  帖子 2 ------------------------------------------

 

重點來了:File GUID 及 Local ID。

File GUID

Unity會為每一個加入到Assets文件夾中的文件,創建一個同級同名的.meta文件,雖然文件類型的不同會影響這個.meta的具體內容,但它們都包含一個用來標記文件身份的File GUID。


 

 

例如,如果一個資源引用了另一個外部資源,比如一個Prefab引用了其他腳本、紋理或Prefab等,則一定會標明引用資源文件的File GUID。

 

 

Local ID

如果說File GUID表示為文件和文件之間的關系,那么Local ID表示的就是文件內部各對象之間的關系,打開一個*.Prefab文件可以很清晰的看到:

 

 

一個對象通常是由 一個或多個對象構成,每個記錄在&符號后面的數字都是一個Local ID,每一個Local ID也表示這它將來也會被實例化成一個對象。也就是說,當一個prefab文件要實例化成一個GameObject時,它會自動嘗試獲取其內部Local ID所指的那個對象。如果這個所指的對象當前還沒有被實例化出來,那么Unity會自動實例化這個對象,如此遞歸,直到所有涉及的對象都被實例化。


Unity通過Instance ID,來獲取或判斷一個對象是否已經被加載完畢。Instance ID由File GUID和Local ID轉換而成,可以簡單理解成是記錄了資源所在內存地址的寫着數字的鑰匙牌。

每當Unity讀入一個File GUID和LocalID時,就會自動將其轉換成一個簡單好記的數字牌,因為通過File GUID和Local ID定位資源的效率並沒有直接解引用一個地址那么快。

如果發現這個牌上並沒有掛着一把鑰匙,表示當前這個這個資源還在磁盤中,尚不在內存里(沒有加載);相反,如果這個牌子上有一把鑰匙,表示這個資源已經被加載完畢,你可以快速的找到並使用它。

Unity會在項目啟動后,創建並一直維護一張“映射表”,這張映射表記錄的就是File GUID、Local ID以及由它們轉換而成的Instance ID之間的關系,這樣下次在請求資源時就可以快速的通過查看鑰匙牌來獲取資源了。

4、剛才的例子里,因為沒有龍珠(資源沒有加載),因此我們必須經歷一場前往小巴黎的歷險(LoadingAsset),而能夠幫助我們准確定位北京通縣的86版《中國地圖》,可以近似理解成是Unity維護的一套將GUID和FileID解析為數據源地址的機制,這套機制中的信息,來自於:

(1) 場景加載時,Unity收集了與該場景關聯的資源信息。

(2) 項目啟動時,Unity收集了所有Resources文件夾下的資源信息。

(3) 讀取AssetBundle時,Unity獲取了AssetBundle文件的頭部信息(Header)。

可以理解為:隨着Unity知道更多的信息,這套機制將能夠解析並定位更多的GUID和FileID。

5、當我們費勁千辛萬苦找到龍珠后,記錄在小本本上的7條位置,就好比7個能幫助夠准確定位內存位置的Instance ID。想象一下,當我們下次再看到諸如《三顆龍珠召喚小神龍》這樣的小訣竅(另外一個*.prefab),便可直接打開小本本(查詢映射表中的Instance ID),對着編號及位置從書包里掏出龍珠(對InstanceID所指的內存地址進行解引用),啪啪啪一操作,小神龍這個游戲對象就能很快被召喚出來了,再也不用去什么通縣了,可以節省大把時間,想想就覺的美滋滋呢。


 

 

每一次調用BuildPipleLine.BuildAssetBundles時,將會生成一批AssetBundle文件,具體數量根據傳遞AssetBundleBuild數組決定,每一個AssetBundleBuild對象將對應一個AssetBundle及一個同名+.manifest后綴文件。其中AssetBundle文件的后綴用戶自行設置,比如".unity3d",".ab"等等;而.manifest文件是給人看的,里面有這個AssetBundle的基本信息以及非常關鍵的資源列表。

除了AssetBundleBuild數組所定的AssetBundle外,還將額外在output路徑下生成的一對與output文件夾同名的文件及一個同名.manifest后綴文件。這個同名文件可厲害了,它記錄了這批次AssetBundle之間的相互依賴關系。當然.manifest文件還是給人看的,我們可以用它分析資源間的依賴關系,但是在項目實際運行時,Unity並不會關心它。



 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM