Unity中AssetBundle的打包和加载

发表于2018-09-29
评论0 5k浏览
在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中还需要深入的研究。
来自:https://blog.csdn.net/beihuanlihe130/article/details/76216072

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引