天天看點

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: 
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中還需要深入的研究。

繼續閱讀