搜索 Unity

优化移动游戏性能:来自Unity顶级工程师的Physics、UI和音频设置小贴士

2021年7月13日 类别 游戏 | 13 分 阅读
Creepy image of cartoon cavern illuminated by a lantern featuring skeletons and a spider.
Creepy image of cartoon cavern illuminated by a lantern featuring skeletons and a spider.
分享

Is this article helpful for you?

Thank you for your feedback!

Unity的Integrated Success团队向来以帮助客户解决最复杂的技术问题为目标。本次,我们邀请到了这支高级软件工程师团队来与大家分享一些移动端游戏优化的专业知识。

作为一支掌握引擎核心代码的团队,Accelerate Solutions致力于为广大Unity客户提供收益最大的项目优化方案。团队的日常工作就是深入了解客户项目,分析并找出有待优化的部分,再思索如何提高其速度、稳定性和效率。

我们的工程师在分享中可谓是倾囊相授,如此多的锦囊妙计无法在一篇博文完整地讲完。因此,我们决定将这些山一般的知识编纂成一本完整的电子书(请在此处下载),并编写一个新博客系列,重点介绍其中70多个可操作性较强的技巧。

在本文属系列第二篇,我们将话题聚焦于UI、Physics和音频上的性能优化方法。第一篇关于剖析、内存和代码架构的博文可在此处查看——我们将在下一篇博文中专门讨论资源、项目配置和图形方面的优化,请继续关注。 

想要立即看完所有内容?那就来这里免费下载完整版电子书吧。

让我们马上进入正题!

Physics物理模拟

Unity内置的Physics物理模拟(Nvidia PhysX)在移动设备上很可能会耗费过多机能,而下方提示将指导你如何挤出更高的帧率。

项目设置优化

请尽可能多地使用PlayerSettings下的Prebake Collision Meshes(预烘焙碰撞网格)。

Image of the in player settings box with a dark grey background and the text "prebake collision meshes*" checked and squared off in red
启用Prebake Collision Meshes。

找到项目的Physics设置Project Settings>Physics),并尽量简化Layer Collision Matrix(碰撞层级矩阵)。 

禁用Auto Sync Transforms(自动同步变换),启用Reuse Collision Callbacks(重复使用碰撞回调)。

Picture of the project settings box with a grey background and the sections for "Auto Sync Transforms" and "Layer Collision Matrix" squared off in red.
简化项目物理模拟设置可以增强性能。
Image of the physics module of the profiler with the 5ms section showing activity in green, blue, and orange against a black background.
时刻注意Profiler中Physics模块可能出现的性能问题。

简化碰撞体

网格碰撞体的性能开销一般比较高昂,因此用原始或简化后的网格碰撞体来代替复杂的碰撞体是较好的选择。

Use primitives or simplified meshes for colliders.
使用原始网格或简化网格来表示碰撞体。

使用物理手段移动刚体

使用类似MovePositionAddForce等方法来移动Rigidbody对象。直接在Transform组件上进行平移会让引擎重新计算整个物理空间,如果恰巧场景还非常复杂,则整个流程会耗费大量的机能。物理对象的移动方法应防在FixedUpdate而非Update下。

正确设定Fixed Timestep(固定时间步)

Fixed Timestep在默认情况下为0.02(50 Hz),请根据你的目标帧率将其调成合适的值(如30 fps应为0.03)。 

如果运行时出现了掉帧,这可能暗示了Unity在一帧上调用了多次FixedUpdate,且执行了过于繁重的物理运算,消耗了过多的CPU性能。

Maximum Allowed Timestep(最大时间步步长)可在掉帧出现时限制物理运算和FixedUpdate的运行时长。更低的数值表示物理运算和动画会在掉帧时被限制,从而降低其对帧率的影响。

Image of the fixed time module inside the project settings with a grey background and white text.
请根据你的目标帧率来修改Fixed Timestep的步长,将时间限定到合理的范围内来减少卡顿。

使用Physics Debugger可视化物理运算

Physics Debug(物理模拟调试)窗口(Window>Analysis>Physics Debugger)可帮助你排除有问题的碰撞体或不准确的物理运算,它会以一定的颜色来显示出可相互碰撞的GameObject。

Image of an orange boat with a mesh overlay and a blue shadow inside the physics debugger dialog box.
Physics Debug工具能以图像显示出物理对象的相互作用。

关于工具的更多信息请参见Unity文档的Physics Debug Visualization

用户界面(UI)

Unity UI(UGUI)是性能问题中的常客。UGUI的Canvas组件负责为UI元素生成和更新网格,并向GPU发出绘制调用,而这些运作的性能消耗较大,因此我们在使用时要记住以下要点。

分割Canvas画布

如果某个大型Canvas包含了上千个元素,单个UI元素的更新也会导致整个Canvas更新,造成不必要的CPU占用。 

这时我们可利用UGUI的多画布功能,根据更新频率来划分UI元素。将相对静止的UI元素放在一个画布上,将需要时常更新的动态元素放在小型的子画布上,

然后统一画布内所有UI元素的Z轴坐标、材质和纹理。

隐藏不可见的UI元素

部分UI元素可能只会间断地出现(譬如角色受伤时出现的血条),就算这个元素变得不可见,它仍可能会发出绘制调用。因此我们需要具体地禁用所有不可见的UI组件,仅在需要时重新激活。

如果你只需隐藏画布,可以禁用Canvas组件,不必禁用整个GameObject,从而避免引擎再度绘制网格和顶点。

限制GraphicRaycasters数量、禁用Raycast Target

类似屏幕触摸或点击等输入事件需要使用GraphicRaycaster组件处理。组件会循环检测屏幕的每个输入点,检查其与UI的RectTransform是否有重叠。 

但我们不必将GraphicRaycaster放在默认Canvas层级的顶层,而可以将添加到需要互动的元素(按钮、滚动条等等)上。

Image of the grey graphic raycaster dialog box with white text.
Reversed Graphics(反转图形)默认会启用,我们可出于性能考虑禁用它。

此外,不一定每个UI文本都要为Raycast Target(射线目标)。在不必要的位置禁用这个选项可让UI运算进一步简化,这在复杂的UI上意味着不小的性能收益。

Image dialog box with a grey background and the text "Raycast Target" outlined in a red square.
如无必要,请禁用Raycast Target。

避免使用Layout Group(布局组)

由于Layout Group的更新效率较低,所以非动态内容完全可以不使用这项功能,转而借助锚点来实现比例化布局。或者,你也能用自定义代码在布局生成完毕后禁用Layout Group组件。

如果你确实需要在动态元素上使用(水平、垂直、网格式)Layout Group,请避免嵌套使用。

Grid layout group dialog box with grey background and white text.
Layout Group,尤其是在嵌套使用时,可能会显著降低性能。

避免使用大型的List与Grid视图

大型List和Grid视图会占用大量性能。如果你需要创建一个大型的List或Grid视图(比如一个包含数百个物品的物品栏),可以考虑建立一个UI库,重复使用库中的元素而非为每个物品创建一个元素。请在这个GitHub项目样例中查看实例。

避免多元素的重叠

将大量的UI元素堆在一起(比如卡牌游戏中的卡堆)会造成过度绘制。我们可使用自定义代码,在运行时将堆叠的多个元素合并成几个或几批较小的元素。

使用多种分辨率和长宽比

目前移动设备的分辨率和屏幕尺寸五花八门,而制作不同分辨率的UI、让其完美适配设备也就很有必要了。

Device Simulator(设备模拟器)支持在引擎中模拟UI在各个设备上的表现。你也可以使用XCodeAndroid Studio来创建虚拟设备。

Image of the device simulator that has a grey dialog box background and a simulation of a racing boat game on a smartphone.
在Device Simulator中预览各种样式的屏幕。

在UI覆盖全屏时隐藏不可见内容

如果暂停或开始菜单覆盖了整个场景,我们可以禁用渲染场景的摄像机,同样地,隐藏在顶层画布背后的元素也可被禁用。

你甚至可以在UI显示期间使用Application.targetFrameRate来降低帧率,因为UI不需要以高帧率刷新。

在游戏空间与Camera Space Canvas上摆放摄像机

如果EventRender Camera字段为空,则Unity会强行在其中加入Camera.main,造成不必要的性能浪费。 

我们可以使用在画布的RenderMode中使用Screen Space - Overlay,来省略掉摄像机的使用。

Image of the Camera space canvases dialog box with a greyed out background and white text.
当然,在World Space Render Mode(游戏空间渲染模式)下,请不要忘了指定一个Event Camera。

音频

虽然音频通常不会产生性能瓶颈,但一定的优化仍然可以省下些许内存。

Inspector dialog box with grey background and white text with an orange voice modulator at the bottom.
优化AudioClips(音频片段)的导入设置。

尽可能地采用未经压缩的原始WAV文件作为资源

Unity会在导入时解压所有的压缩格式(如MP3或Vorbis),在构建时再度进行压缩,这就导致了音频经受两次压缩,最终音质将不可避免地受损。

降低音频压缩时的压缩比

当然,我们的确能使用压缩技术来降低音频大小和内存使用:

  • Vorbis格式适用于大部分音效(不用循环播放的音效可使用MP3)。
  • ADPCM格式适用于短促且经常出现的声音(如脚步声、枪声)。相较于未压缩的PCM格式,该格式减小了文件体积,还能在播放时快速解码。

移动设备上的声效最高频率应为22,050赫兹,较低的频率虽然对最终音频的影响不大,但你最好用自己的双耳来做判断。

选择恰当的Load Type(加载类型)

Load Type设置应根据音频的体积而异。

  • 短音频(< 200 kb)可以设为Decompress on Load(加载时解压),这时音频会被解压成原始的16位PCM音频数据,产生一定CPU和内存占用,所以音频不宜过大。
  • 中等音频(>=200kb)可保持Compressed in Memory(在内存中压缩)。
  • 长音频(背景音乐)应设为Streaming(流播放),避免将整个音频资源一次性加载到内存中。

释放无声音AudioSources的内存

如果游戏中有静音按钮,静音行为不能只是简单地将音量设置为0。假设玩家无需经常性地开关静音,我们可以调用Destroy方法来将AudioSource组件从内存中移除。

下载完整版电子书

我们将在下一篇博文中讨论图形和资源方面的优化。当然,如果你立即学习所有Unity工程师们提出的技巧和窍门,可在此处下载全本电子书。

Image of the ebook cover with red background and a graphic of an adventure game with a dragon and the words "Optimize your mobile game performance" in bold white text.

下载电子书

如果你想进一步了解Integrated Support,或希望直接获取Unity的软件工程知识、专业建议和最佳实践指南,可在此处了解Unity的Success计划。

敬请期待更多优化技巧

我们希望帮助每位Unity创作者发挥出自己项目的最大潜力,如果你有任何想要深入发掘的优化课题,请在评论中给我们留言。

2021年7月13日 类别 游戏 | 13 分 阅读

Is this article helpful for you?

Thank you for your feedback!

相关文章