间接寻址级别不同_使用Unity Addressables可寻址资源系统

本文介绍了在面临内存减半的移植任务时,如何使用Unity Addressables可寻址资源系统来优化内存管理,减少游戏内存占用。通过对比传统资源管理方法,展示了Unity Addressables在内存效率、加载速度和设备兼容性方面的优势。文章还提供了逐步实施的教程,从资源标记到异步加载,以及引用计数和实例化,帮助开发者更好地理解和应用这一系统。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Rubén Torres Bonet是一名优秀的游戏开发者,参与开发的游戏包括:《Time Stall》、《Catan Universe》、《Diamond Dash》、《Jelly Splash》等,这些游戏面向Oculus Quest、Android、iOS、PS4、Xbox One等多个平台,全球的累计安装量超过3000万次。

本文,Rubén Torres Bonet将分享使用Unity Addressables可寻址资源系统的方法。

引言

如果你正带领着一支由程序和美术组成的团队,在6个月内要完成将精美的PS4 VR游戏移植到Oculus Quest,并且面临只有1个月的时间处理内存减半的问题

你会怎么做?我们可以考虑使用Unity Addressables可寻址资源系统

在移植的过程中,我们必须同时处理多项复杂的任务,不同的开发者会有不同的考虑方向。但哪项任务会最耗费时间呢?

我猜超过70%的人会说:在把游戏移植到Oculus Quest平台时,CPU和GPU的性能是开发者面临最大的问题

性能是VR游戏中最难提升的方面之一。对于这类优化,我们需要对产品有深入的了解。但这是一个极为耗时的过程,有时我们甚至无法进一步优化,只能牺牲大量游戏内容和图形效果。但这样导致移植出来的成品不符合玩家的期待,这也是非常棘手的问题。

“性能”这个词往往让开发者感到不寒而栗。Oculus Quest平台的性能是怎样的?项目在Oculus Quest的运行效果如何?

实际上,如果对Oculus Quest有一定开发经验,你可能知道,其实把Oculus Quest作为移动硬件来看,它的功能是非常强大的。Oculus Quest的主要区别在于它的主动冷却系统,该系统对可用CPU/GPU硬件级别提供了其它移动平台没有的大幅提升效果。

此外对于Oculus Quest的软件方面,和通用的Android变体相比,专用操作系统更加适合对虚拟现实渲染进行优化。

在过去几年中,移动硬件正在快速赶上独立平台。但要实现持续在72fps下渲染依旧非常困难,特别是从高端平台移植的VR项目。更确切地说,谈到Oculus Quest的移植时,我们必须把该平台设想为使用带有屏幕、电池、四个摄像头和一个风扇的高通骁龙835处理器的设备。

这款设备的缺点从其它角度看也可以是优点。该移动平台是经过大量研究的硬件,如今有很多已知的技巧可以快速把其CPU和GPU的负载降低到可以接受的程度。

我们任务重点是:和PS4相比,Oculus Quest减少了一半RAM内存。PS4的RAM内存是8GB,而Oculus Quest是4GB

这是粗略估算的结果,因为在两个平台上,操作系统都不会允许使用所有内存,这样它可以跟踪一些子系统,让生态系统正常工作。在Oculus Quest上,我们最多可以使用约2.2 GB的RAM内存,否则会出现问题

合适的内存处理对游戏来说非常重要,因为有两个限制:

  • 严格的内存限制:如果超过特定使用量阈值,操作系统会直接关闭游戏。

  • 缓冲的内存限制:除了特定限制外,在用户最小化游戏窗口,脱下VR设备,或走出Oculus Guardian区域时,操作系统也可能会关闭游戏,

我们不希望这些问题出现在游戏中,否则可以想想一名玩家在丢失2小时游戏进度后的愤怒。

78408c4a1067b35ac03fff6bb626ecd8.png

2.2 GB的可用RAM内存其实并不多。对于从一开始就跟踪统计数据的新项目,这并不是问题,但如果要移植项目到性能大幅降低的硬件,这会是一个大问题。

如果过去处理过类似的移植过程,你一定明白减半游戏的RAM内存预算有多么困难。这项任务取决于游戏架构是否对这种改动做好准备。

减小内存压力的最常用方法包括:调整资源压缩设置、优化脚本、减少着色器变体等

根据项目的具体情况,调整纹理导入设置是我们的首选方法,但如果需要的话,我们也可以压缩网格、动画和音频。但这些方法本身非常复杂,而且有一定限制。

不是所有平台都支持相同的导入设置,面向多个设备构建会大幅提升构建管线的开销,更不用说QA阶段、美术、设计和编程方面的复杂性。开发者要考虑:某款Android设备是否支持ASTC或ETC2?

我们也希望构建64位的版本,同时在32位版本上留住玩家。我们要想好到底要创建多少个APK版本,更糟糕的是,游戏的每次更新都要对每个版本分别管理和测试。我们希望让工作变得轻松,所以我们不应该只依赖这些方法。

我们希望更进一步,让事情尽可能简单,特别是在进行移植的时候。为了性能而重新设计游戏是最坏的选择,不如不进行移植。

由于以上这些原因,我们将介绍传统资源管理方法和可寻址资源管理系统。它将帮助你在数小时内减半项目的内存预算。

为了展示这个过程,我们会把简单的旧项目移植到全新的Unity Addressables可寻址资源系统

下图是使用Unity Addressables的最终效果图。

 

d5045e540d92961e65a36980c74bf5d4.png

使用Unity Addressables的原因

我们的目标是分析如何轻松改进内存,快速实现改进效果。最有效最简单的实现方法是:加载初始场景,打开Profiler性能分析器

由于未优化的游戏架构可以在游戏中任意时间点频繁进行分析,所以最快捷的检查方法是:对初始场景进行性能分析。因为不少游戏经常有开销很大的脚本,它会造成资源引用过度消耗性能。

无论在特定时间是否使用某些资源,这类脚本组件会一直独立加载所有资源。如果游戏容易受到内存容量的限制,那么这是风险很大的使用方法,因为游戏无法随着开发者添加资源而扩展,例如:未来加入的DLC内容。

如果开发者要面向不同的设备开发,每种设备会提供不同的内存预算,我们必须考虑最糟糕的内存情况。

另一方面,是否有传统资源管理方法可以提供很好使用效果的情况呢?答案是肯定的。

如果你正在为PS4等同类平台开发项目,大多数要求已经在一开始决定好,使用全局对象的优点可能会胜过新内存管理系统的额外复杂度。

如果有足够好的使用效果,使用传统全局对象满足所有需求是一种很好的解决方案。这样可以简化代码,预加载所有引用的资源。但在任何情况下,传统的内存管理方法不适合打算挑战硬件极限的开发者。

现在,我们开始使用Unity Addressables可寻址资源系统吧。

学习准备

我们需要使用Unity 2019.2.0f1或更高版本,然后从GitHub上下载的Level 1项目:

https://github.com/The-Gamedev-Guru/Unity-Addressables-Level-1/archive/master.zip

58b02f9fe7058a79f1cb1bb7a0effac5.png

Unity Addressables - 红色天空盒

Level 1开发者:传统资源管理方法

我们从最简单的传统资源管理方法开始。在本示例中,需要在我们的组件中有天空盒材质的直接引用列表。

设置过程只需简单的三步:

  • 通过Git下载Level 1项目。

  • 打开Unity Hub,添加Add按钮。找到下载目录,打开项目文件。

  • 在Windows系统,按下Ctrl+P,在Mac系统,按下CMD+P。

我们可以按下按钮修改天空盒。这种方法很原始,也非常乏味。

我们的项目结构围绕着两个主要系统。我们有SkyboxManager游戏对象,其中的组件是保存天空盒材质引用的主要脚本,可以根据UI事件来切换材质。

using UnityEngine;

public class SkyboxManager : MonoBehaviour

{

    [SerializeField] private Material[] _skyboxMaterials;

    public void SetSkybox(int skyboxIndex)

    {

        RenderSettings.skybox = _skyboxMaterials[skyboxIndex];

    }

}

SkyboxManager对象为UI系统提供功能,可以通过使用RenderSettings API,应用特定材质到场景设置。

其次,我们有CanvasSkyboxSelector。该游戏对象包含一个画布组件,用于渲染一组垂直分布的按钮。

点击每个按钮时,它会调用之前提到的Manager函数,根据按钮ID交换渲染的天空盒。也就是,每个按钮的OnClick事件会在Manager对象调用SetSkybox函数。

5f661038862e632b564aba6e0a770ece.png

场景的层级窗口

我们按下Ctrl+7或Cmd+7,或者点击Window > Analysis > Profiler,打开性能分析器,点击顶部的“录制”按钮。

几秒后停止录制,查看各项指标信息:CPU,内存等。项目的性能结果很好。我们会专注于内存部分,简单的视图模式会展示以下内容。

15b750300fe00632fcc53f97d0af05dd.png

简单的内存性能分析结果

考虑到我们只会在一段时间展示一个天空盒,纹理大小的数值非常夸张,这是我们会在很多未优化项目中发现的情况。

在示例场景中,我们只使用了几个天空盒而已。而在其它项目中往往会有角色、星球、音效、音乐等更多内容。

现在我们要把内存分析部分切换为细节模式,然后进行查看。

5620076540a47ddc0f1551092007e316.png

详细的内存分析结果

我们发现所有天空盒纹理都加载到内存中,但每次只会显示一个天空盒。但这个简单的架构使用了400MB左右的内存

这样的结果无法令人满意,特别这仅仅是游戏的一小部分。解决这个问题的方法是我们下一部分所谈到的使用Unity Addressables可寻址资源系统。

Level 2开发者:使用Unity Addressables

开发游戏时,我们往往会从Level 1部分的情况开始,现在我们将学习使用Unity Addressables可寻址资源系统来进行处理。

访问GitHub获取Level 2项目文件:

https://github.com/The-Gamedev-Guru/Unity-Addressables-Level-2/archive/master.zip

通过对性能分析器的观察,我们知道,虽然每次只会使用一个天空盒,但是项目中所有天空盒都会加载到内存中。因为我们可能会遇到不同资源变体数量的限制,所以这并不是有可扩展性的解决方案。

现在,我们来学习使用Unity Addressables可寻址资源系统摆脱传统资源管理方法的束缚。

首先,添加新工具Unity Addressables可寻址资源系统的API。我们要安装Addressables资源包,打开Window > Package Manager,窗口如下图所示。

 

7ff6e50a6fc36bce40092af7dd5cf229.png

Unity资源包管理器

安装好Addressables资源包后,我们要把材质标记为可寻址资源,在检视窗口勾选Addressable。

747b1499e0d609a31e97e497f9332ee2.png

Level 2资源管理方法:使用Unity可寻址资源

这可以让Unity在可寻址资源数据库包含这些材质和纹理依赖。该数据库会在我们的构建中用来打包资源为多个部分,从而在游戏中任意时候轻松加载。

打开Window > Asset Management > Addressables,此时会打开我们的数据库。

da95b77e165fb46924cc8c0c4972585d.pngLevel 2资源管理方法:主窗口

我们查看之前的SkyboxManager脚本,注意到该脚本仍旧保存着资源的直接引用。我们不希望这样,所以必须告诉脚本如何使用间接引用,即使用AssetReference。

下面,我们要优化脚本组件。

using UnityEngine;

using UnityEngine.AddressableAssets;

using UnityEngine.ResourceManagement.AsyncOperations;

public class SkyboxManager : MonoBehaviour

{

    [SerializeField] private List _skyboxMaterials;

    private AsyncOperationHandle  _currentSkyboxMaterialOperationHandle;

    public void SetSkybox(int skyboxIndex)

    {

        StartCoroutine(SetSkyboxInternal(skyboxIndex));

    }

    private IEnumerator SetSkyboxInternal(int skyboxIndex)

    {

        if (_currentSkyboxMaterialOperationHandle.IsValid())

        {

            Addressables.Release(_currentSkyboxMaterialOperationHandle);

        }

        var skyboxMaterialReference = _skyboxMaterials[skyboxIndex];

        _currentSkyboxMaterialOperationHandle = skyboxMaterialReference.LoadAssetAsync();

        yield return _currentSkyboxMaterialOperationHandle;

        RenderSettings.skybox = _currentSkyboxMaterialOperationHandle.Result;

    }

}

我们对以上代码进行一些解释。

  • 主要改动发生在第7行,我们在此利用AssetReference保存了一组间接引用。修改后,材质在被引用时不会自动加载。我们必须明显指定加载操作,它们才会进行加载。之后,请在编辑器中重新指定该字段。

  • 第13行:由于现在使用异步工作流程,所以我们倾向于使用协程。我们会开启一个新的协程,用于处理天空盒材质的变化。

  • 在第18-20行,检查是否有天空盒材质的句柄,如果有,我们会释放之前渲染的天空盒。每次使用Addressables API执行这类加载操作时,我们都会得到为之后操作保存的句柄。句柄是一种数据结构,包含有关特定可寻址资源管理的数据。

  • 在第23行,我们将特定可寻址引用解析到天空盒材质,然后调用它的LoadAssetAsync函数。我们在第25行使用yield关键字,因此我们会在进一步处理前等待这项操作。由于使用了泛型,所以不需要开销较大的调用。

  • 最后,在材质和依赖加载后,我们会在第26行修改场景的天空盒。材质会在Result字段提供,该字段属于我们用来加载材质的句柄。

3f9ec2478c730369682614c7ea00490b.png

Level 2资源管理方法:AssetReference列表

现在我们查看该方法的实际效果,执行的步骤如下:

  • 在Addressables窗口中,单击Build Player Content,构建内容。

  • 然后为所选择的平台构建版本。

  • 运行版本,将它和内存分析器连接起来。

a474f506d2b3e9219a82c4ab1ef155b3.png

Build Player Content选项

如下图所示,我们看到性能分析结果非常出色。

598b7e1d5b70132617b5615150691e82.png

内存分析器

优秀的性能分析结果可以改善多个方面,这意味着玩家可以在低端设备上玩你的游戏,也会为游戏开发者带来更多收入。这就是Addressables可寻址资源系统的强大功能

Addressables系统会给团队带来少量成本。程序员必须支持异步工作流程,使用协程很容易实现该工作流程。设计师需要学习Addressables系统的功能,例如:可寻址分组,积累经验来作出聪明的决策。

使用Unity Addressables可寻址资源系统,我们实现了以下效果:

  • 适当的内存管理功能。

  • 更快的初始加载速度。

  • 更快的安装速度,减少应用的存储大小。

  • 更好的设备兼容性。

  • 异步架构。

  • 能够在网上存储内容,把数据和代码互相分离。

 

进阶内容:实例化和引用计数

我们将Unity Addressables工作流程应用到了天空盒,由于每次只会启用一个天空盒,所以过程非常简单,但我们在游戏中要管理的东西不只是天空盒。

例如,我们可能会给游戏角色添加变体。这个角色可能是玩家角色,也可能是敌人角色,这样难度就变大了。

现在我们不只是要卸载资源并加载新资源,我们可能打算卸载的资源正在被其它实例使用,这种情况下如何进行内存管理呢?

Unity可以使用自带的集成引用计数器来处理这种情况。这意味着:每次我们实例化某个资源时,它会自动增加引用数量。每次我们卸载资源时,引用计数会减小。如果引用数量减小为0,即不存在任何实例,那么该资源便可从内存卸载。

这些操作可以使用下面的结构完成。

var operationHandle = prefabMaterialReference.InstantiateAsync(transform, true);

yield return operationHandle;

// ...

Addressables.ReleaseInstance(operationHandle);

请注意:低于Unity 2019的版本中Instantiate和Destroy不会更新引用计数,这些计数只受到Addressables系统的InstantiateAsync和ReleaseInstance变体的影响。因此,我们要避免在使用Addressables系统时,使用到Instantiate和Destroy。

其它加载方式

如果你希望使用硬编码的资源字符串标识符,而不是使用AssetReferences,我们可以在Manager类中使用其它结构。

它们的行为是相似的,该结构会返回开发者可以命令的async操作句柄。由于它有泛型形式,Result字段会设为我们最初传入方法调用的类型。

_currentSkyboxMaterialOperationHandle = Addressables.LoadAssetAsync("Skybox" + skyboxIndex);

我们传入函数的字符串标识符不仅可以在资源检视窗口设置,也可以在Addressables主窗口设置。此外,我们也可以加载某个标签的所有资源,这样有助于预加载所有会在后面关卡生成的敌人。

8d872dfc34202f3734ebed19df10da1a.png

在网络提供内容

Level 3 开发者: 进阶使用Addressables

在前面两部分内容中,我们通过从传统资源管理系统转为Addressables系统的工作流程,实现了很好的性能提升。只需要少量的时间成本,我们就可以更好扩展项目中的资源,同时保持较低的内存使用量。

我们目前只探索了Addressables系统的部分功能,我们仍可以使用这个不同寻常的系统通过多种方法改进项目。

对于Addressables系统,我们不必记住所有详细功能,但建议开发者对所有功能进行大致的了解,因为在开发过程中,我们可能会遇到更多挑战。

了解Addressables的全部功能会给我们提供帮助,因此我们准备了额外的指南,你可以在指南中了解以下内容:

  • Addressables窗口。

  • Addressables系统的分析:内存泄漏会产生很大问题。

  • 通过网络供给减少用户等待的时间。

  • 构建管线集成。

  • 实际方法:加速工作流程,不必花10分钟以上的时间等待。

更重要的是,该指南可以回答下列问题:

  • Send Profiler Events有什么潜在影响?

  • AddressableAssetSettings API的实用性如何?

  • 如何把所有内容和BuildPlayerWindow API集成?

  • Fast Mode,Virtual Mode和Packed Mode的区别是什么?

请发送“可寻址指南”到微信公众号后台,获取指南文档下载地址。

小结

本文,我们介绍了项目挑战:在一个月内,为面向Oculus Quest开发的项目减少一半的内存预算。Unity Addressables可寻址资源系统帮助我们按时完成耗费时间的任务,避免对大量游戏内容进行重新设计,通过导入设置几乎无法实现这些目标。

Addressables可寻址资源系统是一个很有前景的系统,Unity开发人员正在努力改进该系统。它适用于正式制作,而且提供完善的文档。

我们可以把使用Addressables可寻址资源系统看做一项中期投入。投入一些时间,让项目在未来数月或来年得到提升。不仅我们从中受益,由于游戏可以支持更多设备,更多玩家也将能够玩你的游戏。

下载Unity Connect APP,请点击此处。 观看更多Unity官方精彩视频,请关注“Unity官方”B站账户。

你可以访问Unity答疑专区留下你的问题,Unity社区和官方团队帮你解答:

Connect.unity.com/g/discussion

推荐阅读

使用Unity制作游戏AI

Unity Labs新一代AR/MR工具:Project MARS

使用Cinemachine设置3D格斗游戏的摄像机

《使命召唤手游》首周下载破亿,技术演讲免费观看

可寻址资源系统介绍

全新设备模拟器加速移动端迭代

Marza动画星球新作《The Peak》

fb4ce8fb610db9fa2719b51d66298dd5.png

喜欢本文,请点击“在看”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值