
这是提供有关如何优化 PC 和游戏主机游戏的深入指导的几个页面之一。你可以在免费的电子书《Optimize your console and PC game performance》中找到完整集锦,书中有80多个优化性能的可操作技巧和最佳做法。
问题:当UI画布上的单个元素发生更改时,它会脏化整个画布。
画布是Unity UI的基本组成部分。它可以生成代表放置在其上的UI元素的网格,当UI元素发生更改时重新生成网格,并向GPU发出绘制调用,以便实际显示UI。
生成这些网格的成本很高。UI元素需要批量收集,以便尽可能减少绘制调用次数。由于批量生成的成本很高,我们只在必要时进行重新生成。问题是,当画布上的一个或多个元素发生变化时,必须再次分析整个画布,以找出如何以最佳方式绘制其元素。
许多用户在单个包含数千个元素的画布上构建整个游戏的UI。当他们更改一个元素时,可能会出现多毫秒的 CPU 峰值。要了解重建为何如此昂贵,请观看Unite会话级别/会话次数的24:55。
解决方案:把画布拆开
每个画布都是一个孤岛,与其他画布的元素相互独立。利用 UGUI 支持多个画布的功能,将画布切片以解决 Unity UI 的批处理问题。
您还可以嵌套画布,让设计师能够创建大型分层 UI,而不必考虑不同元素在屏幕上的不同位置。子画布还会将内容与其父画布和兄弟画布分开。它们维护自己的几何体并执行自己的批处理。一种根据更新频率来划分方法。将静态UI元素放在单独的画布上,将动态元素放在较小的子画布上同时更新。另外,画布上的所有UI元素必须具有相同的Z值、材质和纹理。

问题:Graphic Raycaster(图形射线投射器)的使用不足
Graphic Raycaster是将输入转换为UI Events的组件。具体来说,就是将屏幕点击或触摸输入转换成UI事件,再发送给相关的UI元素。每个需要输入的画布(包括子画布)都需要Graphic Raycaster。但是,它也循环遍历屏幕上的每个输入点,检查它们是否在 UI 的 RectTransform 中,从而产生潜在的开销。
尽管Graphic Raycaster有这么个名字,但它并不是真的射线投射器。默认情况下,它仅测试 UI 图形。它获取对接收给定画布上的输入感兴趣的UI元素集并执行交叉检查。分层检测Graphic Raycaster画布上每个UI元素的RectTransform是否被标记为可互动。
问题是并非所有UI元素都需要接收更新。
解决方案:从非交互式UI画布上移除Graphic Raycaster,并关闭静态或非交互式元素的Raycast Target。
特别是,关闭Raycast Target按钮上的文本将直接减少Graphic Raycaster每一帧必须执行的交叉检查数量。
问题:Graphic Raycaster在某些方面的行为确实表现为射线投射器。
如果将画布上的Render模式设置为Worldspace Camera或Screen Space Camera,则可以添加遮挡遮罩。遮挡遮罩决定了射线投射器是通过2D还是3D物理投射光线,以确定是否有物理对象遮挡了用户与UI交互的能力。
解决方案:通过2D或3D物理投射光线的成本很高,所以要谨慎使用此功能。
不要将Graphic Raycaster添加到非交互式UI画布,从而尽可能减少它的数量。因为在这种情况下,没有理由检查交互事件。
请在这篇文档中详细了解Graphic Raycaster。

问题:大型列表、网格视图和大量重叠的UI元素会消耗大量性能。
大型List和Grid视图非常昂贵,将大量UI元素(即卡牌游戏中堆叠的卡牌)分层会造成过度绘制。
解决方案:避免大量重叠的UI元素。
自定义代码以在运行时将分层的UI元素合并为更少的元素和批处理。
如果需要创建大型List或Grid视图,例如包含数百个项目的库存屏幕,请考虑重用较小的UI元素池,而不是为每个项目使用单个UI元素。
查看该GitHub项目,了解优化滚动列表的示例。

问题:每个尝试脏化其布局的UI元素将执行至少一次GetComponents调用。
当布局系统上的一个或多个子 UI 元素发生更改时,布局会变得“脏”。更改后的子元素将使拥有它的布局系统无效。
布局系统是直接位于布局元素上面的一组连续的布局组。布局元素不仅仅是Layout Element组件(UI图像、文本和滚动矩形),它还包括布局元素,就像滚动矩形也是布局组一样。
现在,关于手头的问题:每个将其布局标记为“脏”的UI元素将至少执行一次GetComponent调用。此调用在布局元素的父级上查找有效的布局组。如果找到,它会继续沿着Transform层级向上走,直到停止查找布局组或达到层级根(以先到为准)。因此,每个布局组都会为每个子布局元素的脏化过程添加一次GetComponent调用,使得嵌套布局组的性能极其低下。
解决方案:尽可能避免布局组。
使用锚点进行比例布局。对于具有动态数量UI元素的热点UI,可以考虑编写自己的代码来计算布局。不要只针对一次改动来按广告需求/广告主使用。
请在文档中详细了解布局组。

问题:以错误的方式集中UI对象
用户通常通过重定父级再禁用的方式集中UI对象,但这会导致不必要的脏化。
解决方案:先禁用对象,然后将其父级重定到池中。
您将脏化旧层级一次,但是一旦您重定其父级,就可以避免第二次脏化旧层级,并且绝对不会脏化新层级。如果要从池中删除某个对象,请先重定其父级,更新数据,然后将其启用。
详细了解基本对象池在Unity的概念。

问题:不知道如何隐藏画布
有时隐藏UI元素和画布也很有用。但如何高效地做到这一点呢?
解决方案:直接禁用画布组件。
禁用画布组件将阻止画布向GPU发出绘制调用。这样一来,画布将不再可见。但是,画布不会丢弃顶点缓冲区,它会保留所有网格和顶点。然后,当您重新启用它时,它不会触发重新构建,而只会再次开始绘制它们。
此外,禁用画布组件不会通过画布层级结构触发代价高昂的OnDisable/OnEnable回调。只要小心禁用那些运行大量每帧代码的子组件即可。
请在此处详细了解画布组件。
问题:在UI上使用动画器
动画师在每一帧上都会脏化UI元素,即使动画中的值不变。
解决方案:使用 UI 动画的代码。
只为随时更改的动态UI元素添加动画器。对于很少发生更改或响应事件而临时更改的元素,请自行编写代码或使用调整系统。Asset Store上提供了许多很好的解决方案。
问题:全屏UI性能不佳
如果您的游戏显示暂停或开始屏幕完全覆盖场景,游戏的其余部分仍然在后台渲染,这可能会影响性能。
解决方案:隐藏所有其他内容。
如果您的屏幕覆盖场景中的所有内容,请禁用渲染 3D 场景的摄像机。同样,禁用隐藏在顶层画布后面的画布元素。
在全屏UI时可以降低Application.targetFrameRate,不必再以60 FPS更新。

为玩家提供最佳的游戏体验。借助 Unity 专家工程师提供的 80 多个可操作的技巧和最佳实践,您可以优化 PC 和主机游戏。
更具体地说,由Unity Success和Unity Studio Productions团队制定的这些详细实践 -- -- 从与顶级工作室的真实合作中收集 -- -- 将有助于提高游戏的整体表现。