版本:Unity 6 (6000.0)
语言:英语
使用 AssetBundles 打补丁
AssetBundle 下载完整性和安全性

疑难解答

AssetBundle 构建结果调查技巧

以下方法可用于在 AssetBundle 构建运行后收集信息。在调查意外行为或调整资源到捆绑包的分配时,这些方法非常有用。

日志

在构建期间,Unity 编辑器日志 会收集信息,例如警告或错误消息。在 AssetBundle 构建结束时,会记录一条详细消息,其中显示有关每个 AssetBundle 的类型和大小细分的详细信息。

构建报告

AssetBundle 构建会生成一个 BuildReport,这是一个写入项目目录中 Library/LastBuild.buildreport 的 Unity 序列化文件。此文件有助于查看构建步骤的时间安排摘要以及 AssetBundle 内容的详细视图。可以使用 BuildReport API 读取 BuildReport 文件中的信息。

有两个不受支持的工具可用于查看 BuildReport 的内容

.manifest 文件

与每个 AssetBundle 一起生成的清单文件提供了一些有关 AssetBundle 内容的人类可读详细信息。

清理构建

为了加快迭代时间,Unity 支持对 AssetBundles 进行增量构建。例如,可以重用过去构建中的元素来处理自上次构建以来未发生更改的项目方面。虽然这可以加快迭代时间,但其检测输入变化的能力有一些限制,尤其是在构建回调运行的脚本一段代码,允许您创建自己的组件、触发游戏事件、随时间推移修改组件属性并以任何您喜欢的方式响应用户输入。 更多信息
请参阅 词汇表
以非确定性方式更改数据时。因此,在构建 AssetBundles 的正式版本时,您应该始终执行清理构建,而不是增量构建。

BuildAssetBundleOptions.ForceRebuildAssetBundle 标志(作为选项传递给 BuildPipeline.BuildAssetBundles())是执行清理构建的推荐方法。

在某些罕见情况下,可能还需要擦除 Library/ShaderCache 目录。当指定 BuildAssetBundleOptions.ForceRebuildAssetBundle 时,不会刷新此缓存。在许多项目中,着色器在 GPU 上运行的程序。 更多信息
请参阅 词汇表
编译阶段可能非常耗时,因此擦除缓存会增加下一个播放器或 AssetBundle 构建的时间。

或者,执行完全清理构建的最可靠方法是停止 Unity,擦除项目的 Library 目录,然后重新启动 Unity。这可能非常耗时,因为所有项目资源都需要重新导入,并且会重新生成其他数据。

检查 AssetBundle 内容

在某些情况下,您可能希望直接查看 AssetBundle 内部,或比较两个 AssetBundle 的内容。

Unity 编辑器安装包括 WebExtractBinary2Text 可执行文件。您可以使用 WebExtract 提取嵌套在 AssetBundle 中的文件,类似于提取 zip 文件。然后,您可以使用 Binary2Text 生成二进制序列化文件内容的文本格式转储。其输出与 Unity 使用的 yaml 格式有些类似。

另一种查看 AssetBundle 的序列化文件内容的类似机制是使用其“转储”参数运行 UnityDataTools

序列化文件的内容往往非常技术化且非常庞大,尤其是在存在着色器或网格或二进制数据时。但是,如果您可以将问题缩小到文件内的特定对象,那么这些转储也可以提供大量信息。使用差异工具比较两个类似 AssetBundle 的提取内容可以方便地缩小精确差异。

目标切换

BuildPipeline.BuildAssetBundles API 的参数允许您指定将在其中部署 AssetBundles 的目标(和子目标)平台。

请求的平台可能与当前在 构建配置文件 窗口中配置的平台不同。但是,在触发构建脚本之前,您应始终确保构建配置文件中的设置与 AssetBundle 构建的设置相匹配。当目标不匹配时,Unity 必须重新编译编辑器脚本以反映新平台,并且还可能触发对资源(例如具有平台特定表示形式的纹理)的导入。然后,在构建结束时,它将恢复状态以匹配原始目标平台。通常情况下,这工作得很好,但它会为每次构建增加大量时间,在执行大量构建迭代时,这些时间会累积。此外,包含对 BuildPipeline.BuildAssetBundles 的调用的脚本将继续在为当前目标编译的脚本域中执行,而不是指定的构建目标。只有当构建脚本或回调脚本期望平台特定的代码或程序集可用时,这才是问题。对于许多项目来说,这种细微的差异不会成为问题。但为了帮助避免这种陷阱,您应该始终确保在构建期间执行的任何代码都动态检查目标(例如,使用 if 语句),而不是使用平台条件编译(#ifdef 语句)。

对于命令行构建,应使用 --buildTarget 命令行参数 来匹配您要构建的目标。

资源重复

Unity 的 AssetBundle 系统将在将对象构建到 AssetBundle 中时发现该对象的所有依赖项。这是使用 Asset Database 完成的。此依赖项信息用于确定将包含在 AssetBundle 中的对象集。

分配给 AssetBundles 发生在资源级别。明确分配到 AssetBundle 的资源中的对象将仅构建到该单个 AssetBundle 中。根据调用的 BuildPipeline.BuildAssetBundles 的签名,资源要么通过将资源的 AssetImporter.assetBundleName 属性设置为非空字符串来“明确分配”,要么通过将其列在 AssetBundleBuild.assetNames 中来分配。

任何不是在 AssetBundle 中明确分配的资源的一部分的对象都将包含在包含任何引用它的对象的每个 AssetBundle 中。

换句话说,如果两个不同的对象分配给两个不同的 AssetBundle,但两者都引用一个公共依赖项对象,则该依赖项对象将被复制到这两个 AssetBundle 中。重复的依赖项也将被实例化,这意味着依赖项对象的两个副本将被视为具有不同标识符的不同对象。这将增加应用程序的 AssetBundles 的总大小。如果应用程序加载其两个父对象,这还将导致对象的两个不同副本加载到内存中。

有几种方法可以解决此问题

  1. 确保构建到不同 AssetBundle 中的对象不共享依赖项。任何确实共享依赖项的对象都可以放置到同一个 AssetBundle 中,而无需复制其依赖项。

    • 此方法通常不适用于具有许多共享依赖项的项目。它可能会生成必须过于频繁地重新构建和重新下载才能方便或高效的单片 AssetBundles。
  2. 对 AssetBundles 进行分段,以便不会同时加载共享依赖项的两个 AssetBundles。

    • 此方法可能适用于某些类型的项目,例如基于关卡的游戏。但是,仍然存在权衡,因为重复的对象会增加项目 AssetBundles 的构建时间和大小,并可能影响加载时间。
  3. 确保所有依赖项资源都构建到其自己的 AssetBundles 中。这完全消除了重复资源的风险,但也引入了复杂性。应用程序必须跟踪 AssetBundles 之间的依赖项,并确保在调用任何 AssetBundle.LoadAsset API 之前加载正确的 AssetBundles。

对象依赖项通过位于 UnityEditor 命名空间中的 AssetDatabase API 进行跟踪。顾名思义,此 API 仅在 Unity 编辑器中可用,而不是在运行时可用。 AssetDatabase.GetDependencies 可用于查找特定对象或资源的所有直接依赖项。请注意,这些依赖项可能具有其自己的依赖项,因此这可能是一个递归计算。资源到 AssetBundles 的分配将通过在调用 BuildPipeline.BuildAssetBundles 时传递 AssetBundleBuild 结构的数组来明确定义,或者可以使用 AssetImporter API 查询。可以编写一个编辑器脚本,确保 AssetBundle 的所有直接或间接依赖项当您的项目请求一个本身“依赖于”另一个包的包时,就会发生间接或传递依赖。例如,如果您的项目依赖于[email protected]包,而该包又依赖于[email protected]包,那么您的项目对 Alembic 具有直接依赖项,对 Timeline 具有间接依赖项。 更多信息
请参阅 词汇表
都已分配给 AssetBundles,或者没有两个 AssetBundles 共享尚未分配给 AssetBundle 的依赖项。

注意:使用 Addressables 包构建 AssetBundles 时,可以使用“Addressables 分析”窗口发现重复的资源。

精灵图集重复

以下部分描述了在与自动生成的精灵2D 图形对象。如果您习惯于使用 3D,则精灵本质上只是标准纹理,但有一些特殊的技术可用于组合和管理精灵纹理,以便在开发过程中提高效率和便利性。 更多信息
请参阅 词汇表
图集结合使用时,资源依赖项计算代码的一个特性。

任何自动生成的精灵图集一个实用程序,将多个精灵纹理紧密地打包到一个称为图集的纹理中。 更多信息
请参阅 词汇表
将被分配到包含生成精灵图集的精灵对象的 AssetBundle。如果精灵对象分配给多个 AssetBundles,则精灵图集将不会分配给 AssetBundle 并且将被复制。如果精灵对象未分配给 AssetBundle,则精灵图集也不会分配给 AssetBundle。

为了确保精灵图集不会被复制,请检查标记到同一精灵图集中的所有精灵是否都分配给同一个 AssetBundle。

Android 纹理

由于 Android 生态系统中的设备碎片化严重,因此通常需要将纹理压缩为几种不同的格式。虽然所有 Android 设备都支持 ETC1,但 ETC1 不支持具有 alpha 通道的纹理。

要使用 AssetBundle 变体,所有无法使用 ETC1 清晰压缩的纹理必须隔离到仅包含纹理的 AssetBundle 中。接下来,创建这些 AssetBundle 的足够变体以支持 Android 生态系统中不支持 ETC2 的部分,使用供应商特定的纹理压缩一种存储数据的方法,可以减少其所需的存储空间。参见 纹理压缩动画压缩音频压缩构建压缩
参见 术语表
格式(例如 DXT5、PVRTC)。对于每个 AssetBundle 变体,将包含的纹理的 TextureImporter 设置更改为适合该变体的压缩格式。

在运行时,可以使用 SystemInfo.SupportsTextureFormat API 检测对不同纹理压缩3D 图形硬件需要纹理以专门的格式压缩,这些格式针对快速纹理采样进行了优化。 更多信息
参见 术语表
格式的支持情况。此信息应用于选择和加载包含以受支持格式压缩的纹理的 AssetBundle 变体。

有关 Android 纹理压缩格式的更多信息,请参见此处

着色器与 AssetBundle 之间的交互

构建 AssetBundle 时,Unity 会使用该捆绑包中的信息来选择要编译的着色器变体。这包括有关场景场景包含游戏环境和菜单。可以将每个唯一的场景文件视为一个唯一的关卡。在每个场景中,您放置环境、障碍物和装饰,从本质上讲,将游戏分成部分进行设计和构建。 更多信息
参见 术语表
、材质、图形设置ShaderVariantCollections在 AssetBundle 中的信息。

单独的构建管道独立于其他管道编译自己的着色器。如果 Player 构建和 Addressables 构建都引用一个着色器,则 Unity 会编译着色器的两个单独副本,每个管道一个。

此过程不考虑任何运行时信息,例如关键字、纹理或由于代码执行而导致的更改。如果要将特定着色器包含在构建中,请包含一个包含所需着色器的 ShaderVariantCollection,或者通过将其手动添加到图形设置中的“始终包含的着色器”列表中,将着色器包含在构建中。

使用 AssetBundles 打补丁
AssetBundle 下载完整性和安全性