AssetBundle 相关知识点

1.AssetBundle是什么

本文参考学习来源:http://blog.shuiguzi.com/2016/12/15/GuideToABAndRes/

                                 https://blog.csdn.net/Mr_Sun88/article/details/86553465                         

  AssetBundle(简称AB)是一个包含平台特定资产(模型,纹理,预制,音频剪辑,甚至整个场景)的压缩包文件,可以在游戏运行时加载。assetbundle可用于可下载内容(DLC),减少初始安装大小,为最终用户的平台优化加载资产,并减少运行时内存压力。(把一些可以下载的内容放在AB包里面,可以减少安装包大小)。

  一个AssetBundle文件由两部分组成:文件头 + 数据段。

  头部数据是在Unity生成AssetBundle文件时构建的,它包含了一些AssetBundle的信息,例如标识符、是否压缩和清单(manifest)数据。

  清单(manifest)数据是由对象的名称为key的查找表(lookup table),每个条目都提供了一个字节(byte)的索引来标识对象在AssetBundle数据段的位置,大部分平台上,这个操作表的底层实现是 C++ STL的 std::multimap。虽然这个算法在各个平台上实现有差异,但是大部分都是平衡搜索树(balanced search tree)的变种,比如 Windows 和 OSX的衍生平台(包括 iOS)使用的是红黑树(red-black tree)。随着AssetBundle里对象的数量增长,构建清单的时间将大于线性增加(O(NlogN))。

  AssetBundle的数据段包含了对象序列化后的原始数据(raw data),如果数据段被压缩过的话,那么这些有序的序列化原始数据就会用LZMA算法来压缩,就是说,首先所有的资源都被序列化,然后所有的序列化数据再被压缩一次。

2.加载AssetBundle 

  一般的,应该尽可能的使用 AssetBundle.LoadFromFile,这个 API 在速度,磁盘使用率和运行时内存方面都是最高效的。

  对于需要下载 AssetBundle 或者给 AssetBundle 打补丁的项目,强烈推荐在 Unity 5.3 或更新版本中使用 UnityWebRequest ,在 Unity 5.2 或者更老版本中使用 WWW.LoadFromCacheOrDownload。可以在项目安装的时候(下载器)将 AssetBundle包 解压从而事先准备好 AssetBundle缓存。

  当使用 WWW.LoadFromCacheOrDownload 时,为了避免内存峰值引起的程序闪退,强烈推荐确保 AssetBundle 保持在项目最大内存预算的 2-3%。对于大多数项目而言,AssetBundle 的文件大小最好不要大于 5M ,并且不要超过 2 个 AssetBundle 在同时下载。

  当使用 WWW.LoadFromCacheOrDownload 或者 UnityWebRequest 是,确保下载的代码在加载完 AssetBundle 后正确的调用 Dispose。C# 的 using 是确保 WWW 或者 UnityWebRequest 的 Dispose 方法会被调用 的最简便的做法。

  对于一个有着大型开发团队的项目来说,自定义下载器对于缓存和下载新内容是很有必要的。写一个自定义的下载器是一个艰巨的工程,特别是下载器要和AssetBundle.LoadFromFile兼容的话。

//不同平台下路径不同
        #if UNITY_ANDROID
        _abPath = "jar:file://" + Application.dataPath + "!/assets/AssetBundles/";
        #elif UNITY_IPHONE
        _abPath = Application.dataPath + "/Raw/AssetBundles/";
        #elif UNITY_STANDALONE_WIN || UNITY_EDITOR
        _abPath = Application.dataPath + "/AssetBundles/";
        #else
        _abPath = string.Empty;
        #endif

  加载方式1:  LoadFromMemoryAsync 异步加载,通过LoadFromMemoryAsync加载从服务器或本地获取的字节流来加载

StartCoroutine(LoadABPackage1(_abPath));
 
IEnumerator LoadABPackage1(string path)
    {
        AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path+"cube.unity3d.assetbundle"));
        yield return request;
        ab = request.assetBundle;
        Object cube = ab.LoadAsset("Cube.prefab");
        Instantiate(cube);
    }

  加载方式2:LoadFromMemory 同步加载,通过LoadFromMemoryAsync加载从服务器或本地获取的字节流来加载

void LoadABPackage2()
{
    ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(_abPath+"cube.unity3d.assetbundle"));
    Object cube = ab.LoadAsset("Cube.prefab");
    Instantiate(cube);
}

      加载方式3:LoadFromFileAsync 直接通过本地路径异步加载,只能加载本地AB包

IEnumerator LoadABPackage3(string path)
    {
        AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path+"cube.unity3d.assetbundle");
 
        yield return request;
 
        ab = request.assetBundle;
        Object cube = ab.LoadAsset("Cube.prefab");
        Instantiate(cube);
    }

  加载方式4:LoadFromFile 直接通过本地路径同步加载,只能加载本地AB包

 

void LoadABPackage4(string path)
    {
        ab = AssetBundle.LoadFromFile(path + "cube.unity3d.assetbundle");
        Object cube = ab.LoadAsset("Cube.prefab");
        Instantiate(cube);
    }

 

  加载方式5:WWW异步加载方法 本地服务器均可

 

IEnumerator LoadABPackage5(string path)
    {
        WWW www = WWW.LoadFromCacheOrDownload(path+"cube.unity3d.assetbundle",1);
        yield return www;
        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.Log(www.error);
            yield break;
        }
 
        ab = www.assetBundle;
        Object cube = ab.LoadAsset("Cube.prefab");
        Instantiate(cube);
    }

 

3.从 AssetBundle 中加载资源

  有3 种不同 AssetBundle 的 API 来从 AssetBundle 中加载 UnityEngine.Objects对象:LoadAsset,LoadAllAssets 和 LoadAssetWithSubAssets。这些API都带有 -Async 后缀的异步版本:LoadAssetAsync,LoadAllAssetsAsync 和 LoadAssetWithSubAssetsAsync。

  同步版本的 API 总是比异步版本 API 的完成时间至少快一帧,特别是 Unity 5.1(包括)之前的版本。在Unity 5.2之前,所有的异步在每帧中最多加载一个对象,这意味着 LoadAllAssetsAsync 和 LoadAssetWithSubAssetAsync 会明显比其同步版本的 API 慢。这在Unity 5.2(包括)之后修复了,现在异步 API 可以在每帧中加载多个对象,但最高不超过时间片的限制。

  当加载多个独立的 UnityEngine.Objects对象时,应该要使用 LoadAllAssets,它应该用在当一个AssetBundle里的全部(绝大部分)都需要加载的时候。相对于其它两个API, LoadAllAssets 比多次调用 LoadAssets 会稍微快一些。因此,如果需要加载的资源数量比较多,并且在同一时间需要加载的数量小于 AssetBundle 内资源数量的 2/3,可以考虑将这个 AssetBundle 切分为更小的 AssetBundle, 然后调用 LoadAllAssets。

4.AB包卸载

AssetBundle.Unload(false),卸载AB包资源,同时保留内存中已加载的资源; 
AssetBundle.Unload(true),卸载AB包资源,同时卸载内存中已加载的资源,会造成资源的丢失

5 AssetBundle 清单(manifest)

  当通过 BuildPipeline.BuildAssetBundles API 来构建 AssetBundle 时,Unity 会序列化一个包含 AssetBundle 依赖信息的对象,这个对象的数据被存储在一个单独的,包含一个 AssetBundleManifest 类型的对象的 AssetBundle 中。

这个资源会存储在一个 AssetBundle 中,这个 AssetBundle 的名字跟 AssetBundle 所在的目录的名字一样。如果一个项目打包它的 AssetBundle 到 (Projectroot)/build/Client/ 中,这个 AssetBundle 的清单文件会被保存为 (projectroot)/build/Client/Client.mainifest。

  AssetBundle 包含的清单可以被加载,缓存和卸载,就像其他的 AssetBundle 一样。
  AssetBundleManifest 对象提供了 GetAllAssetBundles API 来列出所有跟清单(manifest)一起编译出来的 AssetBundle的依赖信息,还有两个API来查询 AssetBundle 的依赖信息。

AssetBundleManifest.GetAllDependencies 返回一个 AssetBundle 的所有依赖,包括 AssetBundle 直接依赖和间接依赖等等。
AssetBundleManifest.GetDirectDependencies 只返回一个 AssetBundle 的直接依赖。

需要注意的一点,这两个 API 都会产生字符串数组。要尽量少使用它们,不要用在应用运行期中对性能敏感的部分。

4 建议

  在用户进入应用的性能要求高的部分前,例如主关卡和场景之,最好尽可能预先加载好所需的对象,这对移动平台特别关键,移动平台对存储的访问慢并且在运行时加载和卸载对象对内存的消耗会触发垃圾回收器(garbage collector)。

posted @ 2020-03-16 18:50  漏船载酒泛中流  阅读(480)  评论(0编辑  收藏  举报