Version: 2020.3
构建 AssetBundle
本机使用 AssetBundle

AssetBundle 依赖项

如果一个或多个 UnityEngine.Objects 包含对位于另一个捆绑包中的 UnityEngine.Object 的引用,则 AssetBundle 可以变为依赖于其他 AssetBundle。如果 UnityEngine.Object 包含对任何 AssetBundle 中未包含的 UnityEngine.Object 的引用,则不会发生依赖关系。在这种情况下,在构建 AssetBundle 时,捆绑包所依赖的对象的副本将复制到捆绑包中。如果多个捆绑包中的多个对象包含对未分配给捆绑包的同一对象的引用,则每个对该对象具有依赖关系的捆绑包将创建其自己的对象副本并将其打包到构建的 AssetBundle 中。

如果 AssetBundle 中包含依赖项,则在加载尝试实例化的对象之前,务必加载包含这些依赖项的捆绑包。Unity 不会尝试自动加载依赖项。

参考以下示例,__Bundle 1__ 中的材质引用了 Bundle 2 中的纹理:

在此示例中,在从 Bundle 1 加载材质之前,需要将 Bundle 2 加载到内存中。加载 Bundle 1Bundle 2 的顺序无关紧要,重要的是在从 Bundle 1 加载材质之前应加载 Bundle 2。在下一部分,我们将讨论如何使用我们在上一部分介绍的 AssetBundleManifest 对象在运行时确定并加载依赖项。

AssetBundle 之间的重复信息

默认情况下,Unity 不会优化 AssetBundle 之间的重复信息。这意味着您项目中的多个 AssetBundle 可能包含相同信息,例如多个预制件使用的同一种材质。在多个 AssetBundle 中使用的资源被称为公共资源。这些会影响内存资源和加载时间。

本页介绍 Unity 如何管理 AssetBundle 之间的重复信息,以及您可以如何应用优化措施。

Editor 设置

默认情况下,Unity 不会执行任何优化措施来减少或最小化存储重复信息所需的内存。在创建构建版本期间,Unity 会在 AssetBundle 中对隐式引用的资源构建重复版本。 为避免发生此类重复,请将公共资源(例如材质)分配到它们自身的 AssetBundle。

例如:可能有一个应用程序包含两个预制件,这两个预制件都分配到它们自身的 AssetBundle。两个预制件共享相同材质,而该材质未分配到 AssetBundle。该材质引用一个纹理,而该纹理也未分配到 AssetBundle。

如果您遵循 AssetBundle 工作流程并使用示例类 CreateAssetBundles 来构建 AssetBundle,每个生成的 AssetBundle 都会包含此材质(包括其着色器和引用的纹理)。在以下示例图像中,预制件文件的大小分别为 383 KB 和 377 KB。

如果项目包含更多数量的预制件,此行为会影响最终安装程序的大小以及加载这两个 AssetBundle 时的运行时内存占用量。由于 Unity 将同一材质的每个副本视为独特材质,因此 AssetBundle 之间的数据重复问题也会影响批处理。

为了避免发生数据重复,请将材质及其引用的资源分配给其各自的 modulesmaterials AssetBundle。还可以仅标记材质,因为构建过程中,纹理依赖关系也会被自动包含在 AssetBundle 中。

现在,如果重新构建 AssetBundle,则生成的输出包含单独的 modulesmaterials AssetBundle (359 KB),其中包含此材质及其关联的纹理。这会显著减小预制件的其他 AssetBundle 的大小(从上一个迭代的大约 380 KB 减小到大约 20 KB)。

下图说明了此情况。

运行时加载

将公共资源提取到单个 AssetBundle 时,请注意依赖关系。特别要注意的是,如果仅使用预制件来实例化模块,则不会加载材质。

一个未加载材质的预制件
一个未加载材质的预制件

要解决此问题,请先在内存中加载材质 AssetBundle,然后再加载属于模块的 AssetBundle。在以下示例中,这个过程发生在变量 materialsAB 中。

using System.IO;
using UnityEngine;

public class InstantiateAssetBundles : MonoBehaviour
{
    void Start()
    {
        var materialsAB = AssetBundle.LoadFromFile(Path.Combine(Application.dataPath, Path.Combine("AssetBundles", "modulesmaterials")));
        var moduleAB = AssetBundle.LoadFromFile(Path.Combine(Application.dataPath, Path.Combine("AssetBundles", "example-prefab")));

        if (moduleAB == null)
        {
            Debug.Log("Failed to load AssetBundle!");
            return;
        }
        var prefab = moduleAB.LoadAsset<GameObject>("example-prefab");
        Instantiate(prefab);
    }
}

一个已正确加载材质的预制件
一个已正确加载材质的预制件

注意:使用 LZ4 压缩和未压缩的 AssetBundle 时,AssetBundle.LoadFromFile 仅在内存中加载其内容目录,而未加载内容本身。要检查是否发生了此情况,请使用内存性能分析器 (Memory Profiler) 包来检查内存使用情况

构建 AssetBundle
本机使用 AssetBundle