天天看點

AssetBundle進階記憶體優化(Unity 4.x)測試環境(Unity 4.7 + NGUI)

在項目中,我們用NGUI制作了2個UI,分别是a.prefab 和b.prefab,它們都使用了一張名為c.png的貼圖(這種現象在開發中非常常見,很多時候可能是數十個UI Prefab使用了同一張貼圖)。我們使用依賴項打包,打包的結果分别是3個檔案:

AssetBundle進階記憶體優化(Unity 4.x)測試環境(Unity 4.7 + NGUI)

我們可以看到,在檔案層面上已經沒有備援了,a.bundle及b.bundle都沒有貼圖的資源,貼圖的資源都在c中。

按照正常的加載過程使用如下代碼:

這樣的代碼能成功加載出我們想要的資源,但這個時候如果我們去看Memory,就會發現備援資料:

AssetBundle進階記憶體優化(Unity 4.x)測試環境(Unity 4.7 + NGUI)

我們發現,c的紋理已經被加載出來了,并且已經占用了記憶體,但是c.bundle的檔案映射依然還躺在記憶體中。一份資料出現了2份記憶體開銷,這對于我們來說是不可饒恕的。按照Unity的要求如果要釋放掉c.bundle的檔案映射記憶體,需要調用AssetBundle.UnLoad(false),可是調用過這個函數之後對它有依賴的檔案就沒有辦法再生成出來了,即你會發現紋理那個位置紋理指向的是null。比如這個時候還有其他的Prefab也使用了這張紋理,你會發現使用Bundle加載出來後材質指向的紋理為空,即使這個時候c的紋理已經在記憶體中。當然你還可以再次加載c.bundle,然後再加載其他Prefab,但你會發現記憶體中出現了兩個叫c的紋理,這個更扯了,很明顯不是一個合格的解決方法。

這個時候我們可以采用自我管理引用的方法來解決這個問題。這裡以修改UIAtlas為例,同樣的原理可以解決其他複雜引用的問題,比如UIFont,甚至3D場景的紋理管理等)。

首先我們調整UIAtlas的代碼,在編輯器模式下存下UIAtlas的紋理及與材質的引用關系。這樣我們在Bundle中就可以獲得這個UIAtlas的紋理引用關系。

正如代碼所示,這個UIAtlas所使用的紋理資訊已經被我們儲存到 PropertiesName 和 PropertiesTextureName 這兩個變量中。這些變量名将會被打包到Bundle中,并且在我們重新解開Bundle的時候可以得到。

其次,我們在c.bundle加載出來後,建立紋理名字對紋理的關系表,然後Unload(false)掉。

Texture2DBundlerCache 是一個簡單的名字紋理的查找表,提供名字對紋理的添加、查找 、删除等功能,這裡不再重複代碼了,相信各位都能搞定。

最後我們再修改UIAtlas的代碼,使得當材質在使用的時候,會重新将材質與紋理的引用關系恢複。是的,紋理已經在記憶體中了。

這樣,我們就可以通過一個很小的表的開銷及數個字元串的開銷,來避免大量的檔案記憶體占用,特别是在一些複雜引用關系中可以遊刃有餘地Unload掉資源,非常有效地控制住記憶體開銷,而且由于是自己做的引用表,因而更加可控。

原文出處:侑虎科技

本文作者:admin

轉載請與作者聯系,同時請務必标明文章原始出處和原文連結及本聲明。

繼續閱讀