AssetBundle(AB包)

什么是AssetBundle?

AssetBundle 是一个存档文件,包含可在运行时由 Unity 加载的特定于平台的非代码资源(比如模型、纹理、预制件、音频剪辑甚至整个场景)。AssetBundle 可以表示彼此之间的依赖关系;例如,一个 AssetBundle 中的材质可以引用另一个 AssetBundle 中的纹理。为了提高通过网络传输的效率,可以根据用例要求(LZMA 和 LZ4)选用内置算法选择来压缩 AssetBundle。

参考自官方文档

AssetBundle可以用来做什么?

AssetBundle相当于一个资源容器或资源存档,是Unity3D推荐的且与之兼容的一种资源存储文件格式,Unity3D提供了一系列相关API用于在运行时加载AssetBundle及其中的资源。

常见的应用场景:

1.分批加载游戏非代码资源;

2.游戏DLC;

3.游戏资源热更新;

AssetBundle的使用心得

在使用AssetBundle的过程中需要注意以下几点:

1.早期版本Unity3D的AssetBundle可以借助AssetBundle Manager工具在编辑器中完成高效操作,从2018.2新版本开始,官方弃用了该工具,转而改用Addressable Assets。除了通过编辑器构建AssetBundle之外,还可以通过相关代码API来构建;

2.AssetBundle及其中的资源都可以通过同步或异步的方式加载;

3.Unity3D还提供了相关的API用于网络下载和从结果中直接采集AssetBundle;

4.加载AssetBundle中的资源时要注意资源依赖问题,加载某个资源之前应该先加载其所依赖的AssetBundle;

5.构建AssetBundle时可以选择内置的压缩算法来提升其在网络中的传输效率;

6.可以通过AssetBundleManifest获取所有AssetBundle的名称或者某个AssetBundle直接甚至完全依赖的AssetBundle。

AssetBundle分组建议

1.将频繁更新的对象与很少更改的对象拆分到不同的 AssetBundle 中。

2.将可能同时加载的对象分到一组。例如模型及其纹理和动画。

3.如果发现多个 AssetBundle 中的多个对象依赖于另一个完全不同的 AssetBundle 中的单个资源,请将依赖项移动到单独的 AssetBundle。如果多个 AssetBundle 引用其他 AssetBundle 中的同一组资源,一种有价值的做法可能是将这些依赖项拉入一个共享 AssetBundle 来减少重复。

4.如果不可能同时加载两组对象(例如标清资源和高清资源),请确保它们位于各自的 AssetBundle 中。

5.如果一个 AssetBundle 中只有不到 50% 的资源经常同时加载,请考虑拆分该捆绑包。

6.考虑将多个小型的(少于 5 到 10 个资源)但经常同时加载内容的 AssetBundle 组合在一起。

7..如果一组对象只是同一对象的不同版本,请考虑使用 AssetBundle 变体。

总结自官方文档

各压缩方式的区别

LZMA和LZ4都是Unity3D对AssetBundle的内置压缩算法,通过压缩AssetBundle可以提高其在网络中传输的效率。三种压缩方式的区别如下:

压缩类型

优点

缺点

适用场景

LZMA

- 最高的压缩率,显著减少文件大小

- 节省下载带宽

- 解压缩时间较长,尤其是在加载时

- AssetBundle需要在加载前进行解压

- 需要对整个捆绑包进行解压,才能使用AssetBundle

适合网络下载的初始资源包,减少下载时间,但不适合需要频繁加载的资源

LZ4

- 较快的解压速度,加载时间短

- 保留一定的压缩率,减少文件大小

- 基于块解压,使用AssetBundle时无须解压整个捆绑包

- 压缩率较LZMA低

- 文件大小介于LZMA和不压缩之间

适合频繁加载或实时需要的资源,如游戏中场景的加载

不压缩

- 加载速度最快,不需要解压

- 可以直接使用

- 文件大小最大,占用更多存储空间和下载带宽

适合本地存储或对加载时间要求极高的场景,如开发调试或本地资源使用

PS:WebGL平台不支持LZMA压缩。

总结自官方文档

使用示例

示例1

示例1演示了如何通过代码API来构建项目中的AssetBundle,在实际项目开发中,我们可以编写特定的编辑器工具来使得我们的构建过程可视化或者简化,除此之外还可以使用一些成熟的插件,例如YooAsset。

主要代码

[MenuItem("Assets/AssetBundle/Build AssetBundle")]
static void BuildAB()
{
    AssetBundleManifest abm = BuildPipeline.BuildAssetBundles(Path.Combine(Application.streamingAssetsPath, "SLWindows"),
    BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.StandaloneWindows);

    if (abm != null)
    {
        foreach (string ab in abm.GetAllAssetBundles())
        {
            Debug.Log(ab);
        }
    }
}

示例2

示例2演示了以同步和异步两种方式加载AssetBundle及其资源,在需要加载大量AssetBundle或大体积AssetBundle时两种加载方式会有明显的区别,本示例未对此种情况作演示。

主要代码

public Button load;

void Start()
{
    load.onClick.AddListener(OnLoadClick);
}

void OnLoadClick()
{
    // LoadSync();
    StartCoroutine(LoadAsyncCoroutine());
}

// 同步加载AB包及其资源
void LoadSync()
{
    AssetBundle mainAb = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "SLWindows"));
    if (mainAb == null) throw new NullReferenceException("Main assetbundle is null.");

    AssetBundleManifest manifest = mainAb.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
    string[] deps = manifest.GetAllDependencies("prefabs");
    foreach (string dep in deps)
    {
        AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, dep));
    }

    AssetBundle prefabAb = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "prefabs"));
    GameObject prefaba = prefabAb.LoadAsset<GameObject>("prefaba");
    GameObject prefabb = prefabAb.LoadAsset<GameObject>("prefabb");
    if (prefaba != null) Instantiate(prefaba);
    if (prefabb != null) Instantiate(prefabb);
}

// 异步加载AB包及其资源
IEnumerator LoadAsyncCoroutine()
{
    AssetBundleCreateRequest mainReq = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, "SLWindows"));
    yield return mainReq;

    AssetBundleRequest abr = mainReq.assetBundle.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
    yield return abr;

    string[] deps = (abr.asset as AssetBundleManifest).GetAllDependencies("prefabs");
    AssetBundleCreateRequest abcr;
    foreach (string dep in deps)
    {
        abcr = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, dep));
        yield return abcr;
    }

    abcr = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, "prefabs"));
    yield return abcr;

    abr = abcr.assetBundle.LoadAssetAsync<GameObject>("prefaba");
    yield return abr;
    Instantiate(abr.asset);

    abr = abcr.assetBundle.LoadAssetAsync<GameObject>("prefabb");
    yield return abr;
    Instantiate(abr.asset);
}

资源下载

百度网盘 

(ABDemo.unitypackage)

F&Q

Q1.依赖分为直接依赖和间接依赖,如果prefabs中存在间接依赖的资源怎么办?

A1.实际上这就是AssetBundleManifest中GetAllDependenciesGetDirectDependencies的区别,前者是获取直接依赖和间接依赖的所有AB包,后者仅获取直接依赖的AB包,所以我们通常使用前者。

Q2.AssetBundleManifest是固定名称吗?是否可以更改?

A2.这个名称是固定的,目前据我所知只能通过这个名称来获取AssetBundleManifest对象,但是这个名称不区分大小写,既可以是"AssetBundleManifest"也可以是"assetbundlemanifest",或者"Assetbundlemanifest"等,结合如图2所示就能够明白了。

图2

如果这篇文章对你有帮助,请给作者点个赞吧!  

posted @ 2023-10-17 14:35  我与岁月的森林  阅读(57)  评论(0)    收藏  举报  来源