Unity Asset Bundles,不可不知的使用技巧和误区

来源: Unity官方平台


Asset Bundles 是包含游戏资产的存档文件。它们用于将游戏分成逻辑块,实现按需交付和更新内容,同时使游戏构建变得更小。它们还常用于交付补丁和 DLC(可下载内容)。Asset Bundles 可以包含各种资产,如 Prefabs、Materials、 Textures、Audio Clips、Scenes 等,但不能包含脚本。

此前,你需要手动构建 Asset Bundles,分别标记每个资产,并在运行时跟踪和解决依赖关系。现在,所有这些工作都可交由 Addressables 系统处理。它会根据你定义的资产组(Asset Group)为你构建 Asset Bundles,并透明地加载和处理依赖关系。

现在已经有了很多关于 Asset Bundles 如何工作的指南,在这篇文章中,我们想重点介绍一些这个系统较少为人所知的方面,重点放在游戏性能、内存运行时使用和一般兼容性上。


加载和卸载资产

每当尝试使用包含在资产包中的资产时,Unity 会确保相应的包已加载到内存中,然后依次加载内存中的资产。

虽然可以部分加载 Asset Bundle 中的特定资产,但不允许反向操作。这表示一旦加载了 Asset Bundle 中的一个资产,只有不再需要整个资产组时,才能将其卸载。

因此,如果 Bundle 结构不理想,随着游戏的推进,会经常看到运行时内存使用量增加,导致性能下降和潜在的崩溃。所以最好避免在一个Bundle 中包含大量的资产,因为这将最终占用大量的运行时内存,并成为游戏的瓶颈。要做的应该是根据资产被加载和一起使用的频率来打包它们。


引擎版本兼容性

Asset Bundles 通常是向前兼容的,因此使用旧版 Unity 构建的 Bundle 在大多数情况下可以在使用新版 Unity 构建的游戏中正常工作(假设你没有去除 TypeTree 信息,这将在后面讨论),反之则不成立。因此如果构建 Bundle 的 Unity 版本比构建游戏的 Unity 版本高,Bundle 就可能无法正确加载。

随着用于构建 Bundle 的 Unity 版本和用于构建游戏的 Unity 引擎版本之间的差异增大,兼容性变得越来越不可能。也有一些情况下,虽然 Bundle 可能仍然被加载,但其中包含的对象无法在新版的 Unity 中正确加载。这可能是由于对象序列化方式的变化,从而导致问题。在这种情况下,你需要重新构建 Bundle 以保持兼容性。

加载使用不同版本 Unity 构建的 Bundle 也有性能成本,这将在下面的 TypeTree 部分中讨论。

出于这些原因,建议每次更新游戏构建的 Unity 版本时都要对现有的 Asset Bundles 进行彻底测试,并尽可能更新它们。


跨平台兼容性

Asset Bundles 通常不支持跨平台。虽然在编辑器中你可以加载来自其他目标平台的 Bundle,但在设备端这样做就会失败。即使这些 Bundle 中包含的资产不是特定于某个平台的,这个问题也仍会存在。

这种限制的原因是数据可能会以仅适用于目标平台的方式进行优化或压缩。此外,Bundle 可能包含不应在不同平台之间共享的特定平台数据,以防止泄露不适用于其他平台的内容。


加载缓存

加载缓存(Loading cache)是一个共享的页面池,Unity 用它来存储最近访问的 Asset Bundles 数据。这个缓存是全局的,因此你游戏中所有的 Asset Bundles 都可共享这个缓存。

这个功能是最近(大约是在 Unity 2021.3 版本)引入的,然后移植回 2019.4 版本。在此之前,Unity 为每个 Asset Bundle 使用单独的缓存,这导致运行时内存使用显著增加(在下文的“序列化文件缓冲区”部分会讨论)。

默认情况下,这个缓存大小设置为 1MB,但可以通过设置 AssetBundle.memoryBudgetKB 来改变。

默认的缓存大小在大多数情况下应该是足够的,虽然在某些场景下,改变它可能给你的游戏带来好处。例如,如果你的 Bundle 中有很多小对象,增加缓存大小可能会导致更多的缓存命中,从而提高游戏性能。


其他内部数据

除了游戏资产外,Asset Bundles 还包括一系列额外的信息和头文件,这些信息让 Unity 用来知道要加载哪些资产以及如何加载,并且还包含一个专用的缓存(取决于你使用的 Unity 版本)。


资产目录

Asset Bundles 中的资产映射,让你能够通过名称查找和加载每个资产。通常,这部分在内存中的大小不是问题,除非你有包含数千个对象的超大资产包。


预加载表

预加载表( Preload Table)列出了 Bundle 中每个资产的依赖项。Unity 用它来正确加载和构建资产。

如果 Bundle 中的资产有很多显式和隐式的依赖项,以及来自其他 Bundle 的级联依赖项,这张表可能会变得相当大。因此(以及其他许多原因),设计 Bundle 时最好尽量减少依赖链。


TypeTrees

TypeTrees 定义了 Asset Bundles 中对象的序列化布局。

TypeTrees 的大小取决于 Bundle 中包含的不同类型的对象数量。因此,避免将多种不同类型的对象混在一个大的 Bundle 中是个好方法。

TypeTrees 是在升级游戏构建的 Unity 版本时,尝试加载在较旧版本引擎上构建的 Asset Bundles 时保持兼容性所必需的。例如,如果对象的格式或结构发生了变化,TypeTrees将允许进行安全的二进制读取,以便 Unity 无论如何都能尝试加载它。这虽然有性能开销,但通常建议在更新引擎时尽可能更新 Bundle。

你可以在构建 Bundle 时设置 BuildAssetBundleOptions.DisableWriteTypeTree 标签来禁用 TypeTrees。这会使 Bundle 及其相关的内存开销更小,但也意味着在每次更新游戏构建的引擎版本时需要重建所有 Bundle。如果你依赖于玩家构建的 Bundle(用户生成内容),这尤其麻烦。因此,除非你有非常有力的理由去禁用 TypeTrees,否则建议保持 TypeTrees 启用。

一种可以安全禁用 TypeTrees 的情况是将 Bundle 直接包含在游戏构建中。在这种情况下,升级引擎需要重新构建游戏和新的 Asset Bundles,所以其向后兼容性方面并不相关。

每个 Bundle 都有自己的 TypeTrees,因此多个包含相同类型对象的小 Bundle 会略微增加磁盘上的总大小。然而,在加载时,TypeTrees 存储在内存中的全局缓存中,所以如果多个 Asset Bundles 存储相同类型的对象,不会增加运行时的内存成本。


序列化文件缓冲区

注意:正如上文提到的,自 Unity 2019.4 版本起,这个功能已经被全局共享的加载缓存所取代。

当一个 Asset Bundle 被加载时,Unity 会分配内部缓冲区来将它们的序列化文件存储到内存中。

普通的 Asset Bundles 包含一个序列化文件,而在 Streaming Scene Asset Bundles 中,其中的每个场景最多包含两个序列化文件。这些缓冲区的大小取决于平台。在 Switch、PlayStation 和 Windows RT 上,缓冲区大小为 128KB,而在所有其他平台上,缓冲区大小为 14KB。

因此,最好避免使用大量非常小的 Asset Bundles,因为这些缓冲区占用的内存相对于实际提供的资源可能会变得非常大。


CRC 完整性检查

CRC(Cyclic Redundancy Check)用于对 Asset Bundles 进行校验和验证,以确保传送到游戏中的内容与预期完全一致。CRC 是基于 Bundle 的未压缩内容计算的。
在主机平台上,Asset Bundles 通常作为本地存储上的内容安装的一部分或作为 DLC 下载,因此,不需要进行 CRC 检查。在其他平台(如 PC 或移动设备)上,对从 CDN 下载的 Bundle 进行 CRC 检查就非常重要。这是为了确保文件没有损坏或被截断,从而避免潜在的崩溃,同时也为了防止潜在的篡改。

CRC 检查在 CPU 使用方面相当昂贵,尤其是在主机和移动设备上。出于这些原因,通常一个较好的折衷方案是在本地和缓存的 Bundle 上禁用 CRC 检查,只对非缓存的远程 Bundle 启用 CRC 检查。


减少资产查找的开销

默认情况下,Unity 提供了三种在 Bundle 中查找资源的方法:

  • 项目相对路径(Project Relative Path,例如:Assets/Prefabs/Characters/Hero.prefab)
  • 资产文件名(Asset Filename,例如:Hero)
  • 带扩展名的资产文件名(Asset Filename with Extension,例如:Hero.prefab)

虽然这些方法很方便,但也有代价。为了支持后两种方法,Unity 需要构建查找表,对于大型 Bundle,这些表会消耗大量内存。

此外,使用除项目相对路径之外的方法加载资源会产生性能成本,因为需要进行表查找。

因此,建议避免使用这些方法。你甚至可以在构建 Asset Bundle 时禁用它们,这样可以提高资源包的加载性能并减少运行时内存使用。

为此,你可以在构建 Bundle 时设置以下两个标记:

  • BuildAssetBundleOptions.DisableLoadAssetByFileName
  • BuildAssetBundleOptions.DisableLoadAssetByFileNameWithExtension

想要了解更多关于资产管理的信息、分享反馈,可以到 Unity 社区提问哦~
https://developer.unity.cn/ask


本文转自:Unity官方平台,转载此文目的在于传递更多信息,版权归原作者所有。如不支持转载,请联系小编demi@eetrend.com删除。

最新文章