图形引擎实战:AssetBundle构建,更新与动态加载的实践

本文从开发者视角探讨Unity引擎中AssetBundle的资源管理。介绍了AssetBundle构建、下载更新及加载卸载方法,分析了Asset冗余问题及解决办法,还阐述了资源收集、分配、版本管理和DLC支持等内容,以应对资源管理的挑战。

在 Unity 引擎中,AssetBundle 系统允许我们在游戏发布后随时更新资源,还支持在游戏运行时根据实际需要动态加载资源。但是,由于 AssetBundle 系统的特定设计和固有属性,实际项目中使用它来进行资源管理可能会变得复杂,带来不少挑战。本文旨在从开发者的视角出发,探讨 AssetBundle 的构建、下载更新,以及加载与卸载的一些实践方法。

0. Basics

0.1 Assets

在 Unity 引擎中,asset 是构成游戏或 App 的基本元素,并以单个文件的形式存在于 Assets 目录中。它可以是 3D 模型,纹理贴图,材质,音频剪辑,动画,场景,C# 脚本或其它类型的文件。

在制作和生产的过程中,这些 asset 之间会自然地形成依赖(或引用)关系。例如,一个 prefab 可能依赖某些 fbx 模型,而这些 fbx 模型又依赖一些材质,而材质又依赖一些贴图和 Shader。

当 asset 首次被导入时,Unity 会在相同的目录中为其创建一个同名但扩展名为 .meta 的文件,这个 meta 文件中记录了为对应 asset 生成的 GUID。Asset GUID 提供了一种稳定的标识符,确保即使 asset 被修改、重命名或移动到另一个目录,它们之间的依赖关系也不会被破坏。

此外,在导入的时候,Unity 会对 asset 进行处理,把它转成游戏运行时可读取的内部数据格式(例如贴图的压缩等),并缓存在 Library 目录中。而原始的 asset 文件则会按原样保留,不会被修改。

0.2 AssetBundles

AssetBundle 是一个归档文件,类似于 zip 或 rar 压缩包。它包含了一组面向特定平台的(platform-specific)、非代码的 asset。在游戏运行时,Unity 允许我们动态地加载或者卸载 AssetBundle。

就像前面提到的,asset 之间自然存在着依赖关系。在构建 AssetBundle(AB)之后,asset 之间的依赖关系会转变为 AB 之间的依赖关系。

如上图所示,Material 1 依赖 Texture A,Material 2 依赖 Texture X。如此构建 AB 之后,将转变成 AB 1 依赖 AB 2 和 AB 3。在游戏运行时,如果要加载 Prefab A,则先要通过 AssetBundle.LoadFromFile 将三个 AB 都加载到内存中(AB 的加载顺序并不重要;实践中 AB 之间出现循环依赖也是常见的),之后通过在 AB 1 上调用 AssetBundle.LoadAsset 来加载 Prefab A。值得注意的是,Unity 会自动加载 Prefab A 依赖的 Texture A 和 Texture X,并不需要我们手动加载它们。

Unity 为我们提供了从 AB 中加载单个 asset 的能力,而不必一次全部加载。但不幸的是,就目前来说,Unity 没有为我们提供卸载单个 asset 的功能。

这些从 AB 获取的 asset 与它的 AB 之间存在着链接,要卸载这些 asset 则必须首先卸载它的 AB。如果我们使用 AssetBundle.Unload(false) 来卸载 AB,将导致这个 AB 与它的 asset 之间的链接断开:

如图所示,在使用 AssetBundle.Unload(false) 卸载 AB 后,AB 与 M 之间的链接断开了。

之后如果再次加载这个 AB,它并不会与 M 重新建立链接。可能令人感到惊讶的是,如果再次从这个 AB 加载 M,Unity 会重新创建一个新的 M 实例:

类似 M 这种与 AB 断开链接的 asset 后续只能通过调用 Resources.UnloadUnusedAssets 来卸载。

由于 AssetBundle.Unload(false) 并不理想,所以实际应用并不多见。大部分时候,我们会选择使用 AssetBundle.Unload(true) 来卸载 AB,这样在卸载 AB 的同时也会卸载所有从它获取的 asset。通常这意味着我们需要为每个 AB 维护一个引用计数,只有当一个 AB 里的所有 asset 都不再使用时,我们才会卸载这个 AB。

现在,情况变得稍微有些复杂:asset 能够单独加载,但必须(与其 AB 中的其它 asset 一起)批量卸载。

这意味着,在游戏运行的某个时刻,即使某些 asset 当前并不使用,我们可能也无法立即卸载它们。这是因为其它一些正在使用的 asset 直接或间接地引用了这些当前不使用的 asset 的 AB。无法及时卸载的 asset 会持续占用内存,增加内存占用的峰值。在最坏的情况下,内存占用到达系统上限,程序崩溃退出。

由于 asset 的生命周期强行与 AB 捆绑在了一起,致使我们必须将性能优化的主战场从游戏运行时转移到游戏编译时——我们必须巧妙地构建 AB,使每个 AB 所包含的 asset 子集表现出高聚合性(或称局部性),也即,使这个子集中的 asset 几乎总是同时被使用(和不被使用),从而提高内存占用的有效载荷比。这将是我们面临的第一个挑战。

0.3 Asset Redundancy

自 Unity 5 起,AB 构建系统会自动检索 asset 的所有依赖,并将它们统一打包进 AB 中。这个设计的确彻底避免了构建不完全 AB 的可能性(缺少被依赖的 asset),但同时也带来了一个新的问题:asset 的冗余。

请考虑一种常见的情形:Prefab Wall01、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搜狐畅游引擎部

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值