1. 程式人生 > >Unity中AssetBundle的打包和載入

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