[Unity workflows] Unity Addressables 教程:学习基础知识
基于地址的资产管理规模刚刚好可寻址对象引入异步行为不要忘记烹饪内容的变化!在帖子的开头,我向您展示了我所面临的挑战:在短短一个月内为 Oculus Quest 开发的项目的内存预算减半。结果?任务完成。这就是我要对这个全能的 Unity 包说的话:Addressables 系统救了我的命。它避免了大规模的游戏重新设计,从而使定时任务成为可能;在我们的港口环境中,这些几乎是不可能实现的。Addres
英文原文:https://thegamedev.guru/unity-addressables/tutorial-learn-the-basics/
欢迎来到这个 Unity Addressables 教程。
您正在带领一个程序员和艺术家团队将一款好看的 PS4 VR 游戏移植到 Oculus Quest。 您有 1 个月的时间将内存预算减半,并有 6 个月的时间来完成它。
你的第一步是什么? 让我们将 Unity Addressables 带到桌面上。
内容:
- Unity Addressables: 为什么?
- Unity Addressables 教程:我们项目的要求
- 一级开发人员:传统资源管理
- 二级开发人员:Unity 可寻址工作流教程
- 中级:实例化和引用计数
- 可选:替代加载策略
- 三级开发人员:Guru 的 Unity Addressables 方式
- 总结
你意识到你必须同时处理许多具有挑战性的任务。 我敢打赌,有些人可能比其他人更关心个人,这取决于您在每个领域的专业水平。 如果非要选一个最能夺走你神圣睡眠的,你会选哪一个?
我最初的猜测是:超过 70% 的读者会说 CPU/GPU 性能是在将游戏移植到 Quest 时最关心的问题。 对此我说:你很可能是对的。 性能是 VR 游戏中最难改进的领域之一。 对于此类优化,您需要对产品有深入的了解,这是一个耗时的过程。 有时您甚至无法进一步优化,这通常会导致放弃昂贵的游戏或图形功能。 不满足人们的期望是一个危险的地方。
性能,性能,性能…这个词可能会让你的脊椎发冷。 在这方面,您对 Quest 平台有何期待? 它的表现如何? 问题是,如果你有过一些开发经验,你可能会知道,尽管它是移动硬件,但它的功能却是惊人的强大。
但是鲁本! 不要给我那些废话。 我告诉你,当我打开第二个浏览器选项卡时,我的手机会显着变慢。 你怎么敢说移动平台可以表现得如此出色?
我读懂了你的想法,不是吗? 最大的不同在于 Quest 的主动冷却系统,这极大地提高了其可达到的 CPU/GPU 硬件水平,这是其他移动平台无法提供的。 这是一个强大的风扇,可以防止你的头发聚集灰尘,避免 CPU 和你的脸一起融化(我想到了 GoT 皇冠场景)。
此外,在 Quest 软件方面,更专业的操作系统比通用的 Android 变体更适合虚拟现实渲染(惊喜)。 在过去的几年里,移动硬件一直在迅速赶上独立平台。
但是,与此同时,我不能否认,我们以 72 fps 持续渲染的任务将被证明具有挑战性,尤其是对于来自高端平台的 VR 端口。 更准确地说,当我们谈论 Oculus Quest 时,你必须想象自己是一台配备屏幕、电池、四个摄像头和一个风扇的 Snapdragon 835。
看起来像缺点的东西实际上可以被认为是优势。 这个移动平台是一个经过充分研究的 s*** 硬件。 可以说,您可以使用无数已知技巧来快速将 CPU 和 GPU 负载降低到可接受的水平。 如果您感兴趣,您将能够在即将发布的帖子中阅读有关它的信息。 到目前为止,我们将把性能排除在这篇文章之外。
在我们的挑战中可能会引起你注意的是,与 PS4 相比,Quest 有一个硬件特性几乎减半:RAM 容量。 没错,我们从 8 GB RAM 升级到 4 GB RAM。 这是一个近似值,因为在这两个平台中,操作系统都不允许您全部使用它,因此它可以跟踪一些子系统以使生态系统正常工作。 在 Quest 上,您将能够大致使用多达 2.2 GB 的 RAM,以免事情变得混乱。
鲁本,你说的凌乱到底是什么意思? 问题是,正确的内存处理对您的游戏至关重要。 之所以如此,是因为您有两个约束:
- 硬内存限制:如果你超过某个阈值,操作系统只会杀死你的游戏
- 软内存限制:超过另一个特定限制,当您的用户最小化您的游戏、摘下耳机或您离开 Oculus 监护区时,操作系统可能会决定终止您的游戏。
显然,您不希望这两种情况中的任何一种发生在您的游戏中。 你能想象一个愤怒的玩家刚刚输掉了最后两个小时的游戏吗? 是的,他们会去你的商店,而且不会从他们嘴里说出任何漂亮的东西。
问题是,老实说,2.2GB RAM 的保证可用性并不多。 对于从一开始就不断跟踪统计数据的新项目来说,这通常不是问题,但对于严重降级硬件的移植来说,这绝对是一个问题。
如果您过去处理过类似的端口,您会很快意识到将游戏的 RAM 预算减少一半是多么具有挑战性。 这在很大程度上取决于游戏架构为这种变化做好了准备,但在大多数情况下,这会暂时将你变成一台催泪机器。
减少内存压力的最流行策略包括调整资产压缩设置、优化脚本、减少着色器变体等。根据项目的具体情况,调整纹理导入设置是您的首选解决方案,但如果您需要 也可以诉诸于压缩网格、动画和音频。 问题是这些技术通常本质上很复杂,并且会有上限。
并非所有平台都支持相同的导入设置; 针对多个设备将大大增加您的构建管道开销,更不用说 QA、艺术、设计和编程复杂性了。 这个 Android 设备支持 ASTC,还是只支持 ETC2(如果有的话)? 哦,我们还想构建 64 位版本,但也要让玩家继续使用 32 位版本。 对于我们对游戏进行的每次更新,我们应该创建多少个拆分 APK,更糟糕的是,管理和测试? 你想让你的生活更轻松,所以你不应该完全依赖这些技术。
我们想走得更远。 像往常一样,我们希望使事情尽可能简单 ( TM ),尤其是在您进行移植时。 为了性能而重新设计整个游戏比不移植它更糟糕。 就此而言,在今天的主题中,我将为您提供最大的收益之一:我将向您展示如何在数小时内将项目的内存预算减半。 Boom!
那不是很有用吗?
来吧,去吧……问我:这对我来说真的可能吗? 答案是:这取决于你的出发点,但根据我的经验,是的。 Unity Addressables 在这里可以发挥很大的价值。 捕获? 你必须愿意投入工作并掌握过程。 但是这个工作流程将为您赢得当月员工的称号。
如果您有兴趣,请继续阅读。
在这篇文章中,你和我将经历从传统资产管理转向基于可寻址资产管理系统的旅程。 为了说明本教程的过程,我们将把一个简化的老式项目移植到 Unity Addressables 的新时代。
现在,你可能会问我:你为什么不向我展示你在现实生活中所做的工作?
在一个非竞争的世界里,我只会向你展示我创造的所有材料。 不过,在现实世界中,这可能会让我吃不消。 更糟糕的是,破产了。
相反,我将做的是为您提供指导,以便您和我迭代绝对代表您明天在下一个项目中可能会遇到的困难的项目。 我们将首先欢迎 Unity 的 Addressables 加入我们的建议包系列。
在本教程中,我将帮助您尽快开始使用 Addressables,以便您可以在几分钟内实现自己的 Unity Addressables 系统。

Unity Addressables: 为什么?
是时候关注这个非常重要的部分了。 我们的目标是诊断容易获得的内存并快速实施它们。 有几种方法可以做到这一点,但最强大但最简单的方法之一是通过加载初始场景并打开分析器来挑选低垂的果实。 为什么这个?
因为在游戏过程中的任何时候都可以经常诊断出未优化的游戏架构,所以检查这一点的最快方法是分析初始场景。 其原因是普遍过度使用类似单例的脚本,其中包含对项目中每个资产的引用,以防万一。
换句话说,在许多游戏中,通常会有一个万能的脚本导致资产引用地狱。 该组件始终保持每个资产的加载,与该时间点是否使用无关。
这有多糟糕?
这取决于。 如果您的游戏可能会受到内存容量的限制,这是一个非常冒险的解决方案,因为您的游戏不会随着您添加的资产数量而扩展(例如考虑未来的 DLC)。 如果您针对异构设备(例如 Android),则没有单一的内存预算; 每台设备都会提供不同的设备,因此您只能接受最坏的情况。 如果我们的用户决定在 Facebook 上回复快速消息,操作系统可以随时决定终止您的应用程序。 然后用户回来并惊讶地发现,他们所有的游戏都永远消失了。
这有多有趣?
零。 除非您过度劳累并且睡眠不足,否则这种情况可能会让您绝望地大笑。
更复杂的是,如果稍后您决定(或有人为您决定)将您的游戏移植到另一个功能较弱的平台,同时保持交叉游戏工作,祝您好运。 您不想陷入这种技术挑战中。
另一方面,是否存在这种传统资产管理解决方案运行良好的情况? 答案是肯定的。 如果您正在为 PS4 等同类平台开发它,并且大多数要求都是从一开始就设置的,那么全局对象的好处可能会超过更好的内存管理系统额外增加的复杂性。
因为让我们面对现实:一个简单的、好的旧全局对象包含你需要的一切,如果它对你来说足够好的话,它就是一个简单的解决方案。 它将简化您的代码,还将预加载您引用的所有资产。
无论如何,对于寻求突破硬件界限的开发人员来说,传统的内存管理方法是不可接受的。 当您阅读本文时,我认为您想提高自己的技能。 所以是时候这样做了。
欢迎使用 Unity Addressables 教程。
Unity Addressables 教程:我们项目的要求
如果您打算只阅读此博客条目,那么您的屏幕就足够了。 否则,如果你想和我一起做这件事,你需要:
- 功能手
- 超频大脑
- Unity 2019.2.0f1 或更高版本
- 来自 GitHub 的 1 级项目(下载 zip 或通过命令行)
- 愿意用 Unity Addressables 弄脏你的手
git 存储库将包含三个提交,博客中每个技能升级部分一个(除非我在某个时候搞砸了,在这种情况下我将提交修复)。

一级开发人员:传统资产管理
我们从这里最简单的资产管理方法开始。 在我们的例子中,这需要在我们的组件中有一个直接引用天空盒材质的列表。
如果你和我一起做这个,设置是一个简单的 3 步过程:
- 从git下载项目
在上一节中,您将找到 ZIP 文件的链接,但如果您熟悉命令行,您也可以运行列出的命令。 - 在 Unity 中打开项目
打开 Unity Hub 并按下添加(现有项目)按钮。 导航到下载的目录并打开它。 - 点击该死的播放按钮!
只需按 CTRL + P(或 CMD + P)。
好好。 您可以单击几个按钮并更改天空盒。 这太原始了……而且很无聊。 我接受它,还没有 Unity Addressables 教程。
很快你和我就会明白为什么我们需要忍受这种短暂的无聊。
首先,我们的项目结构如何? 它以两个主要系统为中心。 一方面,我们有 SkyboxManager 游戏对象。 该组件是主脚本,它保存对天空盒材质的引用,并根据 UI 事件在它们之间切换。 很容易。
public class SkyboxManager : MonoBehaviour
{
[SerializeField] private Material[] _skyboxMaterials;
public void SetSkybox(int skyboxIndex)
{
RenderSettings.skybox = _skyboxMaterials[skyboxIndex]; // Set the current renderscene skybox material through RenderSettings API
}
}
SkyboxManager 通过使用 RenderSettings API 为 UI 系统提供了将特定材质应用于场景设置的功能。
其次,我们有 CanvasSkyboxSelector。 这个游戏对象包含一个画布组件,渲染一组垂直分布的按钮。 单击每个按钮时,将调用上述管理器函数以根据按钮 ID 交换渲染的天空盒。 换句话说,每个按钮的 OnClick 事件都会调用 Manager 上的 SetSkybox 函数。 这不是很简单吗?
一旦我们完成了玩 X2 的沉浸式白日梦,就该动手了。 让我们启动多感官体验并打开分析器(ctrl/cmd + 7;或窗口 - 分析 - 分析器)。 我带你熟悉这个工具,否则你知道顶部的记录按钮做什么。 记录几秒钟后,停下来看看自己的指标:CPU、内存等。有什么感兴趣的吗?
性能相当不错,考虑到项目的范围,这不足为奇。 你完全可以把这个项目变成 VR 体验,我向你保证,你的用户不会填满许多玩家在玩 Eve: Valkyrie 时填满的胆汁桶。
在我们的例子中,我们将专注于内存部分。 简单视图模式将显示如下所示的内容:

纹理大小上的数字对于在任何给定时间仅显示单个天空盒来说看起来非常大,您不同意吗? 惊喜来袭:这是您在许多未优化的项目中会发现的模式,您可能会因此受到牵连。 但见鬼,在这种情况下,它只是天空盒的集合。 在其他情况下,它将更多地与角色、行星、声音、音乐有关。 你的名字,我有。
如果处理许多资产属于您的责任,那么我很高兴您正在阅读本文。 我将帮助您过渡到可扩展的解决方案。
魔法时间。 让我们将内存分析器切换到详细模式。 看一看!

天哪,那里发生了什么? 所有天空盒纹理都加载到内存中,但在任何给定时间只显示一个。 你看到我们做了什么吗? 这种新秀架构产生了 400mb 的惊人数字。
考虑到这只是未来游戏的一小部分,这绝对是不行的。 解决这个问题是我们下一节的基础。
跟我来!
总结:
- 传统资产管理需要直接引用
- 因此,您始终保持所有对象的加载
- 您的项目不会以这种方式扩展
二级开发人员:Unity 可寻址工作流教程
在电子游戏中,您从 1 级开始,这很棒,但是一旦您了解了游戏规则,就该离开安全的城墙以进行升级了。 这正是本节的内容。
获取下面的2级项目:
直接从 GitHub 下载 ZIP 格式的项目
或者,访问 GitHub 存储库
正如我们之前在分析器中看到的那样,我们将所有天空盒加载到内存中,即使只有一个被积极使用。 这不是一个可扩展的解决方案,因为在某些时候,您将受限于您可以提供给您的玩家的不同资产的数量。 一个建议? 不要限制玩家的乐趣。
来,我来帮你。 拿起我的铁锹,我们就可以挖掘出逃离传统资产管理监狱所需的隧道。 让我们在工具箱中添加一个新奇特的工具:Unity Addressables 的 API。
我们需要做的第一件事是安装 Addressables 包。 为此,请转到 Window → Package Manager,如下所示:

安装后,是时候将 materials 标记为可寻址了。 选择它们并在检查器窗口中激活可寻址标志。

这将做的是礼貌地要求 Unity 将这些Materials及其纹理依赖项包含在可寻址数据库中。 该数据库将在我们的构建过程中用于将资产打包成块,以便在我们游戏中的任何时候轻松加载。
我现在给你看一些很酷的东西。 打开窗口→资产管理→可寻址。 猜猜那是什么? 这是我们的数据库尖叫着上线!

我亲爱的读者:那是最容易的部分。 有趣的来了。
我想让你去拜访我们上一节中的一位老朋友:SkyManager 先生。 如果您检查它,您会发现它仍然直接引用我们的资产! 我们不希望这样。
我们正在教我们的经理使用间接引用来代替 - 即 AssetReference(在虚幻引擎中,您可能将它们称为软引用)。
让我们这样做,让我们美化我们的组件:
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public class SkyboxManager : MonoBehaviour
{
7. [SerializeField] private List<AssetReference> _skyboxMaterials;
private AsyncOperationHandle _currentSkyboxMaterialOperationHandle;
public void SetSkybox(int skyboxIndex)
{
13. StartCoroutine(SetSkyboxInternal(skyboxIndex));
}
private IEnumerator SetSkyboxInternal(int skyboxIndex)
{
18. if (_currentSkyboxMaterialOperationHandle.IsValid())
19. {
20. Addressables.Release(_currentSkyboxMaterialOperationHandle);
21. }
23. var skyboxMaterialReference = _skyboxMaterials[skyboxIndex];
_currentSkyboxMaterialOperationHandle = skyboxMaterialReference.LoadAssetAsync<Material>();
yield return _currentSkyboxMaterialOperationHandle;
26. RenderSettings.skybox = _currentSkyboxMaterialOperationHandle.Result;
}
这里发生的情况如下:
-
第 7 行发生了一个重大变化,我们在其中保存了一个间接引用列表 (AssetReference),而不是直接 Materials 引用。 此更改是关键,因为这些 Materials 不会仅通过引用而自动加载。 它们的加载必须明确。 之后,请在编辑器中重新分配该字段。
-
第 13 行:由于我们现在处于异步工作流程中,因此我们倾向于使用协程。 我们只需启动一个新的协程来处理天空盒材质的变化。
-
我们在第 18-20 行检查我们是否有一个天空盒材质的现有句柄,如果有,我们释放我们之前渲染的天空盒。 每次我们使用 Addressables API 执行这样的加载操作时,我们都会收到一个句柄,我们应该存储以供将来操作使用。 句柄只是一个数据结构,其中包含与特定可寻址资产的管理相关的数据。
-
我们在第 23 行解析了对天空盒材质的特定可寻址引用,然后您调用它的 LoadAssetAsync 函数,您可以通过该函数产生(第 25 行),因此我们等待此操作完成,然后再继续。 由于使用了泛型,因此不需要糟糕的cast。 整洁的!
-
最后,一旦加载了材质及其依赖项,我们继续在第 26 行更改场景的天空盒。材质将在属于我们用来加载它的句柄的 Result 字段中提供。

请记住:此代码不是生产就绪的。 编程飞机时不要使用它。 我决定更倾向于简单而不是健壮,以保持问题足够简单。
足够的解释。 现在是你和我看到这一点的时候了。
如果您愿意执行以下步骤:
- 在可寻址窗口中,烹饪内容(构建播放器内容)
- 然后在您选择的平台上进行构建
- 运行它并将(内存)分析器连接到它。
- 保护您的下巴不至于掉落。


内存分析器
Asset 烹饪好吃吗?
我喜欢快乐的profiler。 你看到的是世界上最快乐的分析器。 一个满意的分析器将意味着几件事。 一方面,这意味着快乐的玩家在诺基亚 3210 上玩你的游戏。这也意味着快乐的制作人。 至于你,这意味着一个快乐的钱包。
这就是 Addressables 系统的强大之处。
可寻址对象,团队开销很小。 一方面,程序员必须支持异步工作流(使用 Coroutines 很容易)。 此外,设计师必须了解系统的可能性,例如 可寻址的群体,并收集经验以做出明智的决策。 最后,如果您选择在线托管资产,IT 将很高兴建立基础架构以通过网络交付资产。
我必须祝贺你。 让我告诉你我们取得了什么成就:
- 适当的内存管理。
- 更快的初始加载时间。
- 更快的安装时间,减少店内应用程序的大小。
- 更高的设备兼容性。
- 异步架构。
- 打开在线存储此内容的大门→将数据与代码解耦。
我会为这样的收获感到自豪。 这肯定是一个很好的投资回报。
哦,一定要在求职面试中提及您使用 Addressables 的经验。
中级:实例化和引用计数
到目前为止,一切都很好。 我们将 Unity 可寻址工作流应用于天空盒,这很容易。 其简单的原因是您一次只有一个天空盒处于活动状态,因此可以直接管理它们的生命周期。
但是天空盒并不是您在游戏中必须管理的唯一资产类型,对吧?
例如,在某些时候,您可能想要添加角色的变体。 这些可能是您自己的角色,也可能是其他玩家和敌人。 你看到这里增加了难度吗? 现在,这不是卸载资产并加载下一个资产的问题。 例如,您要卸载的资产可能仍被另一个实例使用。
在这种情况下我们如何进行内存管理?
对你和我来说幸运的是,Unity 使用其集成的参考计数器为我们处理了这个问题。 这意味着:每次您实例化资产(例如角色预制件)时,都会自动为您增加一个参考编号。 每次释放资产时,引用计数器都会减少。 如果在任何时候,这个数字下降到零——即不存在实例——那么资产将准备好从内存中卸载。 两种操作都可以使用以下结构完成:var operationHandle = prefabMaterialReference.InstantiateAsync(transform, true); yield return operationHandle; // …Addressables.ReleaseInstance(operationHandle);请注意,传统 Unity 版本的 Instantiate 和 Destroy 不会更新引用计数; 这些只会受到使用 Addressables InstantiateAsync 和 ReleaseInstance 变体的影响。
因此,暂时避免在使用 Addressables 时使用 Instantiate 和 Destroy。
可选:替代加载策略
如果您更愿意使用硬编码的资产字符串标识符而不是 AssetReferences,您可以在我们的 Manager 类中使用另一种构造来执行此操作:行为类似,它将返回您可以让步的异步操作句柄。 由于其通用形式,Result 字段将是您最初传递给方法调用的类型。_currentSkyboxMaterialOperationHandle = Addressables.LoadAssetAsync < Material >("Skybox" + skyboxIndex);您传递给函数的那些字符串标识符在您的资产检查器或可寻址主窗口中设置。 或者,也可以加载属于标签的所有资产,我们将在下一节中看到。 比如说,这可能对预加载最终会在第 4 级产生的所有敌人很有用。
总结
- 基于地址的资产管理规模刚刚好
- 可寻址对象引入异步行为
- 不要忘记烹饪内容的变化!

三级开发人员:Guru 的 Unity Addressables 方式
在上一节中,我们实现了最大的收益。 我们通过从传统的资产管理系统转向基于可寻址的工作流程来提升我们的技能。 这对您的项目来说是一个巨大的胜利,因为少量的时间投资为您的项目提供了更好地扩展资产的空间,同时保持低内存使用率。 那个成就确实让你升到了2级,恭喜! 然而,还有一个问题尚未回答:
是这样吗?
不。我们几乎没有触及 Addressables 的表面,还有其他方法可以使用这个改变游戏规则的包来改进您的项目。
当然,您不必记住有关 Addressables 的所有细节,但我强烈建议您对它们有一个概述,因为在此过程中您可能会遇到更多挑战,并且您会很感激能够进一步阅读。 这就是为什么我为您准备了一份额外的简短指南。
在那里,您将了解以下方面:
- Addressables 窗口:细节很重要
- 可寻址分析:不要泄漏(内存)泄漏破坏您的日常事务
- 网络交付:减少播放时间的用户体验问题
- 建立管道整合事项
- 实用策略:加快工作流程,浪费 10 分钟的茶歇时间
更重要的是,回答以下问题:
- 发送探查器事件背后的隐藏含义是什么?
- AddressableAssetSettings API 有多大用处?
- 如何将这一切与 BuildPlayerWindow API 集成?
- 快速模式、虚拟模式和打包模式有什么区别?
总结
在帖子的开头,我向您展示了我所面临的挑战:在短短一个月内为 Oculus Quest 开发的项目的内存预算减半。 结果? 任务完成。
这就是我要对这个全能的 Unity 包说的话:Addressables 系统救了我的命。 它避免了大规模的游戏重新设计,从而使定时任务成为可能; 在我们的港口环境中,这些几乎是不可能实现的。
Addressables 是一个很有前途的系统。 一方面,Unity 开发人员正在努力进一步改进它。 另一方面,它已准备好生产并提供文档。
这意味着:您可以从今天开始在您的项目中使用它。 如果您按照文章和指南进行操作,您应该花很少的时间就可以在您的项目中运行它。
将其视为一项中期投资。 本周您将投入一些时间,以便在您项目的未来几个月/几年中重新获得它。 不仅您会从中受益,而且由于支持更多设备,更多玩家将能够玩您的游戏。
更多推荐


所有评论(0)