在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:
CRC:
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 == )
{
string[] allAssetBundleNames = abms[].GetAllAssetBundles();
Hash128[] allAssetBundleHashes = new Hash128[allAssetBundleNames.Length];
for (int i = ; i < allAssetBundleNames.Length; i++)
allAssetBundleHashes[i] = abms[].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中還需要深入的研究。