如果一个或多个 UnityEngine.Objects
包含对位于另一个包中的 UnityEngine.Object
的引用,则 AssetBundle 会依赖于其他 AssetBundle。如果 UnityEngine.Object
包含对未包含在任何 AssetBundle 中的 UnityEngine.Object
的引用,则不会发生依赖关系。在这种情况下,当您生成 AssetBundle 时,需要依赖项的包的副本就会被复制到包中。如果多个包中的多个对象包含对未分配给包的相同对象的引用,则将对该对象有依赖关系的每个包都会制作该对象的副本,并将其打包到已生成的 AssetBundle 中。
如果某个 AssetBundle 包含依赖项,则当您尝试加载要实例化的对象时,包含这些依赖项的包必须先加载,这一点很重要。Unity 不会尝试自动加载依赖项。
考虑以下示例,包 1 中的材质引用了 包 2 中的纹理
在此示例中,在从 包 1 中加载材质之前,您需要将 包 2 加载到内存中。加载 包 1 和 包 2 的顺序并不重要,关键是要在从 包 1 中加载材质之前加载 包 2。在部分 原生地使用 AssetBundle 中,我们将讨论在运行时如何使用生成过程产生的 AssetBundleManifest
对象来确定依赖项并将其加载。
默认情况下,Unity 不会优化 AssetBundle 中的重复信息。这意味着您的项目中的多个 AssetBundle 可能会包含相同的信息,例如多个 预制体一种资产类型,允许您存储带有组件和属性的 GameObject。预制体充当模板,您可以从中在场景中创建新的对象实例。更多信息
请在 词汇表 中查看 使用的材质。在多个 AssetBundle 中使用的资产称为公共资产。这些资产会影响内存资源和加载时间。
本页描述了 Unity 如何管理 AssetBundles 中的重复信息,以及你可以如何进行优化。
默认情况下,Unity 不会执行任何操作来减少或尽量减少存储重复信息所需的内存。在构建创建过程中,Unity 会构建 AssetBundles 中所有隐式引用的 Asset 的副本。若要防止这种复制情况,请将通用 Asset(例如材质)分配给他们自己的 AssetBundle。
例如:你可以有一个包含两个预制体的应用程序,这两个预制体均分配给自己的 AssetBundle。两个预制体共享同一材质,该材质未分配给 AssetBundle。此材质引用未分配给 AssetBundle 的纹理。
如果你遵循AssetBundle 工作流并使用示例类CreateAssetBundles
来构建 AssetBundles,则每个生成的 AssetBundle 都包含该材质(包括其着色器和引用的纹理)。在下面的示例图片中,预制体文件的大小分别为 383 KB 和 377 KB。
如果某个项目包含更多数量的预制体,则这种行为会影响最终的安装程序大小和加载两个 AssetBundles 时的运行时内存占用。AssetBundles 之间的数据复制还会影响批处理,因为 Unity 会将同一材质的每个副本视为唯一材质。
若要避免数据复制,请将材质及其引用的 Asset 分配给他们自己的modulesmaterials
AssetBundle。你还可以只标记材质,因为纹理依赖关系也会在构建期间自动包含在 AssetBundle 中。
现在,如果你重建 AssetBundles,则生成的输出将包含一个单独的modulesmaterials
AssetBundle(359 KB),其中包含该材质及其关联纹理。这会显著减少预制体的其他 AssetBundles 的大小(从大约 380 KB,减少为 20 KB)。
下图对此作出了说明。
当将一些通用 Asset 提取到单个 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 (materialsAB == null || moduleAB == null)
{
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = moduleAB.LoadAsset<GameObject>("example-prefab");
Instantiate(prefab);
}
}
注意:当你使用 LZ4 压缩和未压缩 AssetBundles 时,AssetBundle.LoadFromFile 仅加载其内容的目录到内存中,而不加载内容本身。若要检查是否发生了这种情况,请使用内存分析器程序包来检查内存使用量。