Unity中AssetBundle的打包和載入
在Unity中,實現物體動態載入的方法主要包括了Resources.Load()和AssetBundle兩種。當我們的遊戲資源需要熱更新時,AssetBundle是一種不錯的實現方式。
Unity官方提供了十分方便的打包工具Asset Bundle Browser,同時該工具也作為開源專案放到了GitHub上。通過Window->AssetBundle Browser便可開啟該工具進行相關操作。我們只需將需要打包的內容拖拽到工具中便會生成相應的Bundle。如果我們同時打多個Bundle,如果Bundle中出現了重複的資源,該工具也會在旁邊以黃色的小歎號給我們提示,這樣我們便可以將重複的資源單獨打包,從而讓我們的Bundle執行更高效。
在命名Bundle的過程中,如果我們不手動新增.assetbundle字尾的話,打包出來的檔案也不存在該字尾,當我們為AssetBundle新增字尾後打包出來的bundle檔案也會自動包含該字尾名稱(該字尾名稱主要取決於我們的檔案伺服器可以下載的檔案型別,只有可以下載的檔案才能通過Unity的WWW方法加載出來,否則將會報錯。如果要修改檔案伺服器中的下載型別,只需修改伺服器中的配置檔案web.config檔案中的MIME屬性便可。)
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension="." mimeType="application/File" />
<mimeMap fileExtension=".assetbundle" mimeType="unity3d/assetbundle" />
</staticContent>
</system.webServer >
</configuration>
如上所示,fileExtension=”.”表示可以下載無後綴的檔案,當我們設定fileExtention=”.assetbundle”時,則表示可以下載字尾名為.assetbundle的檔案。
我們打包的assetbundle檔案,除了.assetbundle檔案外,還包含了manifest檔案。同時除了我們已經命名的檔案外,還包含了一個則外的assetbundle檔案和manifest檔案,這兩個檔案中包含了我們此次打包的所有資源資訊。其中Manifest檔案資訊如下所示:
ManifestFileVersion: 0
CRC: 3392523828
AssetBundleManifest:
AssetBundleInfos:
Info_0:
Name: overlookpointparent.assetbundle
Dependencies: {}
Info_1:
Name: dotweenpathparent.assetbundle
Dependencies: {}
Info_2:
Name: props.assetbundle
Dependencies: {}
Manifest檔案中包含了我們打包的Bundle中的資訊。我們在載入Bundle時一般不會載入Manifest檔案,我們可以通過包含所有Bundle資訊的檔案來動態獲取每一個Bundle的資訊。
在Unity中,載入Bundle的方式主要通過WWW來實現
WWW www = new WWW(URL);
WWW www = WWW.LoadFromCacheOrDownload(URL,hash);
上述方法中,第一種在每次執行時都會從給定地址中獲取相應的檔案並載入,如果是在伺服器的檔案,則需要使用者每次都進行下載,使用者體驗不會很好,第二種方式則會先對快取目錄進行判斷,如果當前快取的Bundle與給定地址的Bundle版本一致,則直接載入快取中的檔案,根據Hash值判斷如果不一致,則需要重新下載之後再載入。
那麼我們如何獲取每一個Bundle的Hash值呢,這就用到了我們上邊提到的Manifest檔案。在Manifest檔案中儲存了所有Bundle的Hash值。
我們不能直接載入Manifest檔案來獲取,需要通過那個包含所有Bundle資訊的Bundle檔案來獲取Manifest資訊。我們可以每次都通過new WWW(URL)來下載佔用較小空間但包含全部Bundle資訊的檔案來獲取Hash值,通過判斷Hash值來動態載入其他佔用空間較大的Bundle。
IEnumerator DownloadManifestFile(string fullBundlePath)
{
WWW ManifestBundleFromWWW = new WWW(fullBundlePath);
yield return ManifestBundleFromWWW;
if (string.IsNullOrEmpty(ManifestBundleFromWWW.error))
{
AssetBundle bundle = ManifestBundleFromWWW.assetBundle;
AssetBundleManifest[] abms = bundle.LoadAllAssets<AssetBundleManifest>();
if (abms.Length == 1)
{
string[] allAssetBundleNames = abms[0].GetAllAssetBundles();
Hash128[] allAssetBundleHashes = new Hash128[allAssetBundleNames.Length];
for (int i = 0; i < allAssetBundleNames.Length; i++)
allAssetBundleHashes[i] = abms[0].GetAssetBundleHash(allAssetBundleNames[i]);
}
bundle.Unload(false);
}
else
Debug.Log(ManifestBundleFromWWW.error);
yield return null;
}
根據以上我們得到的AssetBundle的Name和Hash值,再加上前邊伺服器的地址,我們便可以得到每一個Bundle的地址以及每一個Bundle的Hash值,通過WWW.LoadFromCacheOrDownload(url,hash)便可動態載入所有的Bundle,只有Bundle發生變化時才會從指定地址重新下載。
在載入AssetBundle時,根據AssetBundle的名字進行載入。
string detailBundlePath = "(BundleAddress)/(BundleName)";//BundleName為上邊的allAssetBundleNames[N]
WWW www = new WWW(detailBundlePath);//WWW.LoadFromCacheOrDownload(detailBundlePath, hash);//這一行可以通過Hash值來判斷是否需要下載。
yield return allAssetBundleFromWWW[bundleIndex];
AssetBundle bundle = www.assetBundle;
GameObject[] allObjs = bundle.LoadAllAssets<GameObject>();//可以根據不同的型別載入不同的AssetBundle。
值得注意的是,在WebGL中,本人測試版本為5.6.2f,使用WWW.LoadFromCacheOrDownload()方法進行下載時,會出現瀏覽器佔用大量記憶體的情況,暫時還沒有找到合適的解決方法,只能通過new WWW(url)來暫時實現需要的功能。要使用WWW.LoadFromCacheOrDownload()方法在WebGL中還需要深入的研究。