1. 程式人生 > >unity 資源管理之AssetBundle

unity 資源管理之AssetBundle

上一篇講了unity 資源管理的總體結構連結,這裡主要講unity AssetBundle 相關的記憶體管理內部機制

AssetBundle 由header 和data segment兩部分組成

header包含AssetBundle 一些相關資訊 唯一標識、壓縮型別、清單檔案(identifier, compression type, and a manifest)
清單檔案是個查詢表 key是物件名稱,每個條目對一個data segment 位元組位置。在大部分平臺這個查詢表實現的是一個平衡搜尋樹,具體在pc平臺,windows、ios 實現的是紅黑樹。
data segment 是序列化AssetBundle中資源的資料,根據壓縮選擇有些不同
LZMA:所有資源被壓縮在一起
LZ4 :每一個資源被分別壓縮
沒壓縮:原始的資料資源

note:5.3之前的unity版本 物件不能被單獨壓縮,當讀取一個或者多少的時候會解壓整個AssetBundle 為了提高效率unity會快取解壓後的AssetBundle

載入AssetBundle

提供了5個api來載入,每一個都是同步和非同步方法,這些api的不同根據兩個條件
1. 平臺的不同
2. 壓縮型別LZMA , LZ4 ,未壓縮

這些api可以隨意混合使用
它們是:
AssetBundle.LoadFromMemory(Async optional)
AssetBundle.LoadFromFile(Async optional)
AssetBundle.LoadFromStream(Async optional)
UnityWebRequest’s DownloadHandlerAssetBundle
WWW.LoadFromCacheOrDownload (版本5.6或者低於)

AssetBundle.LoadFromMemory(Async)

unity 官方是建議不要使用該api的,因為c# 載入之後會把他拷貝到一塊新的連續的記憶體裡面,如果壓縮格式是LZMA 那個會拷貝載入解壓之後的整個檔案,如果是沒有壓縮的和LZ4 的則會拷貝當前的全部位元組。這樣會造成記憶體的峰值至少為AssetBundle的兩倍,最後載入一個asset 還需要把該資源拷貝一次,拷貝到gup 或者系統記憶體裡面

AssetBundle.LoadFromFile(Async)

該api 對於未壓縮和LZ4 壓縮的檔案載入時很高效的因為調研該函式時僅僅夾AssetBundle 的header 只有當需要時才會從檔案中載入Object 如InstanceID 被引用或者呼叫AssetBundle.Load()

對於LZMA 格式的AssetBundle 載入整個檔案並儲存解壓的拷貝

在Unity Editor 會直接載入整個檔案到記憶體中,和AssetBundle.LoadFromMemory 類似,真實測試需要在真機上面

UnityWebRequest’s DownloadHandlerAssetBundle

提供了一個下載儲存assetBundle 的方法,是否儲存和儲存位置都配置,最簡單的使用
UnityWebRequest.GetAssetBundle. 下載一個資源,如果assetBundle 是LZMA 格式的在下載過程中可以儲存為LZ4 格式,根據設定Caching.CompressionEnabled.。下載完成之後可以直接獲取到assetBundle 載入過程和AssetBundle.LoadFromFile類似
如果當前檔案已經下載那麼會直接讀取檔案,載入過程和AssetBundle.LoadFromFile類似

WWW.LoadFromCacheOrDownload

在2017.1之後改還是棄用了,請使用UnityWebRequest 相關方法

在之前版本中改行為類似UnityWebRequest ,每次呼叫改方法會建立一個執行緒來下載。多次呼叫需要注意

對於下次LoadFromCacheOrDownload 和UnityWebRequest 都需要Dispose 推薦是有using 語法

載入Asset 從AssetBundle中

有3個載入方法都在AssetBundle 物件上,他們是同步非同步都存在的
LoadAsset (LoadAssetAsync)
LoadAllAssets (LoadAllAssetsAsync)
LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)

同步:同步方法載入會快於非同步方法,至少快一幀(非同步是呼叫的下一幀開始的)
非同步:會載入多個物件直到達到當前時間限制

LoadAllAssets :會載入該AssetBundle中的全部資源,只要需要該AssetBundle大部分比如超過66%的時候官方才推薦使用
LoadAssets:載入單個資源
LoadAssetWithSubAssets :載入資源及其子類資源:如fbx的模型資源及嵌入的animation或者相關紋理等

載入資源不是在主執行緒上面的,與執行緒不敏感的unity系統(scripting, graphics)會轉換到工作執行緒上面,列如VBO從mesh上面建立,textures解壓等
5.3之後的版本,是並行反序列化多個Object 的。這些處理和整合是在工作執行緒上面的,當物體載入完成後會呼叫Awake ,在下一幀Object會變得可用。
同步方法會暫停主執行緒直到載入完成

AssetBundle 依賴關係

追蹤依賴關係有兩種API方法,它們和具體的環境有關
Unity Editor:AssetDatabase
執行時:AssetBundleManifest

object 直接的引用是通過GUID 和 localID 的(具體內容檢視上一篇),在執行時是instancID 。當載入AssetBundle時會給每一個Object分配一個instanceID,當instanceID被其他物件引用時會自動創建出來,AssetBundle 之間的載入順序沒有要求,只要被引用的(object所在的AssetBundle)是已經被載入就好。

AssetBundle manifests

使用BuildPipeline.BuildAssetBundles 打包時,unity會序列化AssetBundle的依賴關係到一個獨立的檔案中,型別是AssetBundleManifest
儲存位置在打包資料夾下,以資料夾為名稱如:打包路徑(projectroot)/build/Client/ 那麼這個檔案的路徑是(projectroot)/build/Client/Client.manifest

AssetBundleManifest 提供了操作該檔案的一些方法:如
GetAllAssetBundles:獲取所有AssetBundle 的名稱
GetAllDependencies :獲取給定AssetBundle 的所有依賴關係
GetDirectDependencies 獲取直接子物件的依賴關係

解除安裝AssetBundle

使用AssetBundle.Unload(unloadAllLoadedObjects )api
當unloadAllLoadedObjects 為true 時會解除安裝所有資源包括在使用的
unloadAllLoadedObjects 為false 時沒有被引用到的資源會被解除安裝,之後重新載入AssetBundle會生成新的instanceID,重新載入Object 會重新生成一份,這種情況會造成記憶體多佔用了一份(一次這樣的操作)
有些情況需要重新載入資源如unity失去對裝置的控制權(如pc被鎖定,app被切換到後臺),gpu會清理掉當前記憶體,需要從新載入資源

如果使用了false 那麼之後可以使用Resources.UnloadUnusedAssets.來解除安裝,當切換場景不是add模式時會自動呼叫該方法