1. 程式人生 > >關於Unity資源載入的細節

關於Unity資源載入的細節

參考文章:http://www.cnblogs.com/88999660/archive/2013/03/15/2961663.html

Unity3D 裡有兩種動態載入機制:一個是Resources.Load,另外一個通過AssetBundle,其實兩者區別不大。

Resources.Load就是從一個預設打程序序包裡的AssetBundle里加載資源,而一般AssetBundle檔案需要你自己建立,執行時 動態載入,可以指定路徑和來源的。

其實場景裡所有靜態的物件也有這麼一個載入過程,只是Unity3D後臺替你自動完成了。

詳細說一下細節概念:
一、AssetBundle
執行時載入:

1、 LoadFromFile:從指定本地磁碟路徑載入,(

注意這種方法只能用於standalone程式)這是最快的載入方法,(CreateFromFile方法在5.3.7版本被移除

1.1、LoadFromFileAsync: 非同步檔案載入,從返回的AssetBundleCreateRequest物件的assetBundle欄位中得到

2、 LoadFromMemory(byte[]): 從記憶體中載入,這個byte[]可以來自檔案讀取的緩衝,www的下載或者其他可能的方式。(CreateFromMemory方法在5.3.7版本被移除

2.1、LoadFromMemoryAsync:非同步記憶體載入,返回與LoadFromFileAsync相同。

其實WWWassetBundle就是內部資料讀取完後自動建立了一個assetBundle而已

Create完以後,等於把硬碟或者網路的一個檔案讀到記憶體一個區域,這時候只是個AssetBundle記憶體映象資料塊,還沒有Assets的概念。

二、Assets的載入:

1、 AssetBundle.LoadAsset(string name):從AssetBundle的記憶體映象裡讀取並建立一個Asset物件,建立Asset物件同時也會分配相應記憶體用於存放(反序列化) (AssetBundle.Load的重新命名方法

1.1、LoadAssetAsync(string name):非同步載入,從返回的AssetBundleRequest物件的asset欄位中得到

AssetBundle

2、LoadAssetWithSubAssets(string name):載入Asset及SubAssets,返回Object[] 或 T[] (5.0版本加入方法)

2.1、LoadAssetWithSubAssetsAsync(string name):非同步載入Asset及SubAssets,返回AssetBundleRequest物件,從allAssets欄位獲取。

3、LoadAllAssets:載入全部Asset,可以指定Type,返回Object[] 或 T[] (AssetBundle.LoadAll的重新命名方法)

3.1、 LoadAllAssetsAsync: 非同步載入全部Asset,返回AssetBundleRequest物件,從allAssets欄位獲取。(5.0版本加入方法

三、AssetBundle和Assets的釋放:

1、AssetBundle.Unload(flase)是釋放AssetBundle檔案的記憶體映象,不包含Load建立的Asset記憶體物件。

2、AssetBundle.Unload(true)是釋放那個AssetBundle檔案記憶體映象和並銷燬所有用Load建立的Asset記憶體物件。

3、Resources.UnloadUnusedAssets: 釋放所有沒有被引用的Asset物件。

4、Resources.UnloadAsset(Object asset) :只會釋放從本地載入的Asset物件。

四、Asset的Clone和使用:

當使用load好的Assets物件,要對其Clone一份並掛接到目標節點上,這是一個淺拷貝的過程,比如GameObject transform新生成的,而像 mesh texture material shader只是引用了源Assets物件。

專門要提一下的是一個特殊的東西:Script Asset,看起來很奇怪,Unity裡每個Script都是一個封閉的Class定義而已,並沒有寫呼叫程式碼,光Class的定義指令碼是不會工作的。其 實Unity引擎就是那個呼叫程式碼,Clone一個script asset等於new一個class例項,例項才會完成工作。把他掛到Unity主執行緒的呼叫鏈裡去,Class例項裡的OnUpdate OnStart等才會被執行。多個物體掛同一個指令碼,其實就是在多個物體上掛了那個指令碼類的多個例項而已,這樣就好理解了。在new class這個過程中,資料區是複製的,程式碼區是共享的,算是一種特殊的複製+引用關係。

當你Destroy一個例項時,只是釋放那些Clone物件,並不會釋放引用物件和Clone的資料來源物件,Destroy並不知道是否還有別的object在引用那些物件。

等到沒有任何 遊戲場景物體在用這些Assets以後,這些assets就成了沒有引用的遊離資料塊了,是UnusedAssets了,這時候就可以通過 Resources.UnloadUnusedAssets來釋放,Destroy不能完成這個任 務,AssetBundle.Unload(false)也不行,AssetBundle.Unload(true)可以但不安全,因為它不管Asset是否被引用,都會釋放,所以不安全。

配個圖加深理解:(圖是老圖,理解就行)


五、注意

1、AssetBundle檔案的記憶體映象,並不是在託管堆上,所以GC是不會代你自動釋放,每次使用完,記得呼叫Unload手動釋放。

2、如果不再使用某個Asset,請確保沒有變數指向這個Asset物件,或者Clone出來的物件,哪怕這個Clone出來的物件已釋放,UnloadUnusedAssets也不能釋放這個Asset,除非這個變數本來被銷燬或者設為null。

3、在面向移動裝置遊戲的製作時,儘量減少在Hierarchy對資源的直接引用,而是使用Resource.Load的方 法,在需要的時候從硬碟中讀取資源,在使用後用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()儘快將其解除安裝掉。降低記憶體佔用

4、如果DontDestroyOnLoad了一個包含很多資源(比如大量貼圖或者聲音等大記憶體佔用的東西)的話,這部分資源在場景切換時無法卸 載,將一直佔用記憶體

5、DontDestroyOnLoad的指令碼,而這些指令碼很可能含有對其他物體的Component或者資源的引用,這樣相關的 資源就都得不到釋放。

6、static的單例(singleton)在場景切換時也不會被摧毀,同樣地,如果這種單例含有大量的對資源的引用,也會成為大問題