可寻址:规划和最佳做法

今天的游戏比以往更大。随着游戏不断试探硬件设备的极限,在运行时高效地管理内容开始变得愈发关键。并且,游戏发行商们希望优化游戏的留存率和变现指标,于是小体积客户端及动态的云上内容更新也成为许多游戏成功的基本要求。
Unity为开发者和发行商们提供有一条端到端的业务管线,来帮助其立足于今日的游戏市场。该管道以Addressables 为起点和终点,这是一个于 2019 年推出的Unity 软件包,目前已为数千款成功的实时游戏和数万款正在开发的游戏提供了支持。
Addressables软件包提供了一个用户界面(UI)和应用程序接口,用于组织Unity资产,将其内置到资产包(AssetBundles)中,并在运行时动态加载和卸载。无论 AssetBundles 是随基础游戏一起提供,还是由Cloud Content Delivery 等远程内容交付网络 (CDN) 托管和交付,Addressables 都能帮助您在需要时加载所需的资产。
虽然Addressables系统能简化内容管理,但它绝不是能“设置完就不管”的功能。游戏资产的组织、打包、加载和卸载方式会极大地影响游戏的体积和性能。
本指南将探索最重要的影响因素,帮助您最大化利用Addressables系统。文末还带有一张涵盖常见设定和推荐策略的“小抄”。
不过,适合游戏和自己目标的策略才是好策略。请将本指南作为参考资料,与Unity Learn资料、Unity 手册文档和社区驱动的寻址论坛一起使用。
从核心来说,Addressables是打包与管理AssetBundles的工具。在深入了解Addressables UI和API前,您必须首先熟悉AssetBundles文件格式以及对运行时的影响。
AssetBundles可被看作是资产的容器:这些档案文件可为目标平台保存模型、纹理、预制件、ScriptableObjects(可编程对象)、音频,甚至是整个场景,用于在运行时加载。
AssetBundles 的一个主要特点是它们可以表达彼此间的依赖关系。例如,AssetBundle 1 可能包含一个依赖于 AssetBundle 2 中纹理的预制板。在运行时使用 Addressables 加载预制板时,Addressables 系统会自动将 AssetBundle 2 和从属纹理加载到内存中。如果包1的另一个资产用到了包3的某个资产,则包3也会被加载,以此类推。

在游戏运行期间,Addresssables系统会跟踪所有活跃的资产引用,包括上边提到的纹理,来确定哪些包需要加载。从 AssetBundle 加载的资产在其引用计数和同一 AssetBundle 中的所有其他资产引用计数均为 0 时才能从内存中释放。只有当 AssetBundle 中的所有资产引用计数为 0 时,才能从内存中释放 AssetBundle 本身。
考虑到可寻址内容和资产捆绑包之间的这种紧密关系,在组织可寻址内容时,最重要的规则是创建资产捆绑包,其中包含离散的资产集,这些资产集可以一起加载和卸载。
使用Addressables最重要的一步是规划资产的分组。您需要考虑如下问题:
- 资产是分成许多的小组还是分成几个大组?
- 每个小组需要打包成几个AssetBundle(即组内的资产应该一并打包、分开打包还是按标签打包)?
- 需要用标签吗?
- 各个分组是使用本地还是远程的加载路径?
我们也希望能有一种万能的方案,但最好的分组策略必须根据几个方面来决定。
请记住可寻址组为您的可寻址资产提供组织结构,决定这些资产如何构建到 AssetBundle 中。因此,最好的组织策略就是根据游戏的独特结构、目标和限制,最有效地打包、加载和卸载 AssetBundle。
在开始组织Addressable内容前,您必须掌握以下几个方面:
1.游戏的结构和路线图
2.游戏平台的强项与局限
3.用Addressables优化游戏性能的主要目标
我们会在下方逐一探讨这些因素。
游戏的结构和路线是首个需要考虑的因素。
“结构”的意思是游戏的结构。它是线性单人冒险,让玩家通过预设的关卡和环境?还是多平台PvP游戏,有数千种装饰性物品在不固定的时间点实例化?游戏的结构决定了资产加载和使用,以及卸载和释放内存的时机。
注意,请尽量将需要同时加载和卸载的资产打包到同一个包里。如果游戏是有固定中断点的线性冒险,可以根据关卡的不同部分将资产组分成较大的大组。如此一来,这些资产都能同时被加载和卸载。
如果游戏是非线性、更为不确定的,可以选择更小的分组,让加载和卸载更动态。请尽量为小组起更有逻辑和意义的名字,以方便快速找到资产并优化资产布局。
“路线图”是指游戏长期的变化方向。一旦游戏发售,除了bug修复和平衡性调整外,路线能否一直保持不变?或者您希望经常性地添加新内容,同时避免让玩家安装大型更新?
内容路线图对分组策略的制定有一定的参考作用。如果游戏的内容足够完整,不需要后续更新,可以按照上方问题来制定分组策略。如果游戏需要经常更新,可以仅把需要的内容按分发时间分成小组。
标签可以帮助区分需要同时加载的每个资产包的内容,比如随游戏成长的装饰性物品。您也可以用Groups Settings(组设定)的“Pack Together By Label”(按标签打包)模式来继续划分本地组的内容。
比如,您计划启动一场“Halloween 2023”活动,提供几种装饰性物品供玩家收集。“Halloween 2023 Outfits”组的资产可以带有“Hats”、“Shoes”和“Masks”标签。而这些资产又都能加上“Halloween 2023”标签。“Pack Together By Label”打包模式就能创建出三个AssetBundle。
在运行时,角色自定义界面可以加载所有带“Hats”标签的资产,完成资产的下载、加载和查看。或者,宣传页面可以加载所有带“Halloween 2023”标签的资产,保证玩家能看到它们。


深入理解游戏的结构和路线有助于制定合理的内容组织方式,为整个生命周期带来好处。
接着,我们来讨论平台的强项和局限,及其对内容组织策略的影响。
接下来一个因素是分发目标平台的强项与局限。在这一节,我们会列出常见的Addressable应用平台,以及各平台的关键考虑因素。
移动端和VR
对于移动端和VR平台,最值得考虑的是应用大小、资产包大小和下载速度。
在分组时,我们首先需要想到玩家安装游戏后马上需要的内容(比如教程)。这部分内容最好保存在本地的加载路径,包含到基础游戏中。把其他内容分到远程加载路径,在云端进行分发。
您所选的分组策略最好只打包小型的AssetBundle。包的大小需要根据游戏来平衡。避免使用超大型资产包,因为它们会占用大量的内存,在加载后也很难释放。同样,也别把资源拆分成牛毛一样多的小型包,如此产生的Addressables分类文件会非常长,该文件每次更新都会被重新下载。小型包太多同样会影响玩家下载所需内容的速度,请在找寻平衡时权衡一下这些优势和劣势。
桌面端和主机
对于台式机和游戏机来说,最重要的考虑因素是性能。相比于移动端和无线VR,桌面端和主机在内存和磁盘空间上更为富余。有鉴于此,可以考虑使用组设置来创建未压缩的AssetBundle。这可以尽可能地加快加载时间,在特定平台上甚至能提高打补丁的效率。
在专门为游戏机开发时,请密切关注可能适用的特定平台缓存限制。虽然移动平台允许您利用 Unity 的 AssetBundle 缓存下载内容,但对于某些游戏机和WebGL,Unity 引擎默认禁用了这一功能。在这些平台上,我们可以考虑更新基础游戏,而不是在云端远程分发的内容。不然,您将需要创建自己的资产包缓存系统,解决方案还必须遵守平台的服务条款。
在分析了平台的强项和局限后,我们再确定使用Addressables系统所需达成的目标。例如:你们的主要目标是缩小基础游戏的大小,还是计划向玩家提供空中内容更新?为做演示,我们在下方讨论下这些选项。
最小化基础游戏的大小
如果您的首要目标是最小化基础游戏的大小,并且不在意内存限制和安装后的大量下载时间,则您应该优先将尽可能多的资产从场景和Resources转移到带远程加载路径的一个或多个Addressables资产组。
尝试将项目里的资产做成可寻址的,除了那些必须包含那在主要游戏包里的内容。对于在安装后分发的内容,可以将场景添加到带远程加载路径的资产组。你甚至可以用一个几乎为空的场景构建一个播放器,然后从那里动态加载游戏的其他部分,正如这段Open Projects Devlog视频中所解释的那样。

如果一个场景要做成可寻址,其他场景最好也做成可寻址,以减少复制多余资产的几率和数据量。
对于将生成要远程托管的AssetBundle 的组,请务必启用AssetBundle 缓存。该设定可在玩家设备上缓存下载好的AssetBundle,防止每次游戏时重新下载资产。
尽管始终牢记许多小型捆绑包和少数大型捆绑包对运行时间的影响是有好处的,但在考虑您可能有的其他目标时,这些考虑因素就变得更加重要了。
向玩家高效地交付远程内容
如果您的首要目标是高效地交付远程内容,资产组应该分开“本地”(即包含在运行版里的资产)和“远程”(即托管于外部内容分发网络的资产)资产。同样,启用AssetBundle Cache来缓存设备上下载的内容。
资产组的大小、数量和打包模式需要视交付内容的时机和下载内容的时长而定。例如,如果您的游戏结构允许在用户安装基本游戏后不久交付所有远程内容,那么您可以选择 "打包在一起"或"按标签打包在一起"的较大群组,这将导致少量的大量下载。
如果希望在不干扰游戏体验的前提下分发一小段一小段的内容,我们可以选择更小的资产组以及/或者生成小型资产包的打包模式,使下载速度更快。
在大多数情况下,对于包含远程内容的组,可考虑为资产包循环冗余检查 (CRC) 选项启用、不包括缓存。该选项可以将内容缓存至玩家的设备,进一步保证内容的完整性,同时避免CRC检查设备缓存内容的开销。
优化运行时内存占用和性能
如果您的主要目标是优化游戏运行时的性能和内存使用情况,那么请记住 "可寻址组 "最重要的组织规则:计划同时装卸的资产应集中在一起。
总的来说,这样分出来的资产包会更小。您可以通过多种方式实现这一目标,包括创建较小的组,以及/或者避免在组设置中为包含游戏中不一定同时需要的资产的大型组设置 "打包在一起 "捆绑模式。
我们还应当密切关注运行时性能,以便找出潜在问题或可优化的区域。利用 Unity 官方工具,如Unity Profiler、Memory Profiler软件包或Addressables Event Viewer,它们都可以帮助优化游戏性能。
请关注即将推出的可寻址分析器模块,它将取代可寻址事件查看器。这款新工具可深入展示代码加载和卸载可寻址资产与AssetBundle的方式,详细显示资产和AssetBundle间的依赖关系。
达成多个目标
当然,大多数项目都会有一些与寻址相关的目标。在这种情况下,确实没有放之四海而皆准的方法。您需要自行权衡上方提及的得失,找到最合适的分组结构和设定,实现您自己的成功。
我们建议您使用Addressables1.21.3 中即将推出的Addressables 生成报告和Addressables Profiler 模块。可寻址构建报告将为您提供从您的可寻址构建生成的资产包的详细信息,包括文件大小、潜在的重复和深入的依赖性信息。新的运行时分析工具Addressables Profiler Module则能利用依赖数据精确地展示Addressables代码加载了那些内容,加载的原因是什么。
我们在下方提供了几张“小抄”,根据最常见的几种用法介绍几种推荐的Addressables设定和策略。当然,这些只是建议。最终需要靠您自己确定建议是否符合项目结构和您的目标。
如果您有问题或想进一步了解可寻址软件包,请访问我们的可寻址论坛。您也可以通过 Twitter@Unity_Jeff 直接与我联系。作为Tech from the Trenches系列的一部分,请务必关注其他 Unity 开发人员的新技术博客。