您想找什么?
Hero background image
Unity UI 优化技巧
了解如何全面优化您的 UI,包括划分画布和布局组、池化 UI 对象等技巧。

这是提供有关如何优化您的 PC 和主机游戏的深入指导的几个页面之一。您可以在免费电子书 《优化您的控制台和 PC 游戏性能》中找到完整的内容,其中包含 80 多个可操作的性能优化技巧和最佳实践。

拆分你的画布

问题当 UI Canvas 上的单个元素发生变化时,它会弄脏整个 Canvas。

Canvas 是Unity UI的基本组件。它生成代表放置在其上的 UI 元素的网格,在 UI 元素发生变化时重新生成网格,并向 GPU 发出绘制调用,以便实际显示 UI。

生成这些网格的成本可能很高。UI 元素需要被分批收集,以便用尽可能少的绘制调用来绘制它们。由于批量生成的成本很高,因此我们只想在必要时重新生成它们。问题是,当 Canvas 上的一个或多个元素发生变化时,必须再次分析整个 Canvas,以找出如何最佳地绘制其元素。

许多用户在包含数千个元素的单个 Canvas 上构建整个游戏的 UI。当他们改变一个元素时,他们可能会经历耗时数毫秒的 CPU 峰值。要了解重建为何如此昂贵,请转到 本次 Unite 会议的24:55 部分。

解决方案分割你的画布。

每个画布都是一个岛屿,将其元素与其他画布的元素隔离开来。利用 UGUI 支持多个 Canvas 的能力,通过切分 Canvas 来解决 Unity UI 的批处理问题。

您还可以嵌套 Canvas,这使得设计人员可以创建大型分层 UI,而不必考虑不同元素在 Canvas 之间的屏幕上的位置。子画布还将内容与其父画布和同级画布隔离开来。它们维护自己的几何形状并执行自己的批处理。决定如何分割它们的一种方法是基于它们需要刷新的频率。将静态 UI 元素保存在单独的画布上,并将同时更新的动态元素保存在较小的子画布上。另外,确保每个画布上的所有 UI 元素具有相同的 Z 值、材质和纹理。

图形 Raycaster 界面
限制图形射线投射器并禁用射线投射目标

问题Graphic Raycaster 使用不足

Graphic Raycaster 是将您的输入转换为 UI 事件的组件。更具体地说,它将屏幕点击或屏幕触摸输入转换为 UI 事件,然后将它们发送给感兴趣的 UI 元素。您需要在每个需要输入的画布(包括子画布)上添加一个 Graphic Raycaster。但是,它还会循环遍历屏幕上的每个输入点并检查它们是否位于 UI 的 RectTransform 内,从而导致潜在的开销。

尽管有这样的名字,Graphic Raycaster 实际上并不是一个光线投射器。默认情况下,它只测试 UI 图形。它采用一组有兴趣在给定的 Canvas 上接收输入的 UI 元素并执行交集检查。例如,它检查 Graphic Raycaster 的 Canvas 上每个 UI 元素的 RectTransform 发生输入事件的点是否被标记为交互。

问题是并非所有UI元素都需要接收更新。

解决方案从非交互式 UI 画布中移除图形射线投射器并关闭静态或非交互式元素的射线投射目标。

特别是,关闭 Raycast Target 按钮上的文字将直接减少 Graphic Raycaster 在每帧上必须执行的交叉点检查次数。

问题有时 Graphic Raycaster 确实充当了光线投射器的作用。

如果您将画布上的渲染模式设置为世界空间相机或屏幕空间相机,则可以添加阻挡遮罩。阻挡掩码决定 Raycaster 是通过 2D 还是 3D 物理投射射线,以确定某些物理对象是否阻挡了用户与 UI 交互的能力。

解决方案通过 2D 或 3D 物理投射射线可能会很昂贵,因此请谨慎使用此功能。

通过将 Graphic Raycasters 从非交互式 UI Canvases 中排除来尽量减少其数量,因为在这种情况下,没有理由检查交互事件。

本文档中了解有关 Graphic Raycaster 的更多信息。

网格接口
避免使用昂贵的 UI 元素

问题大型列表、网格视图和大量叠加的 UI 元素成本很高。

大型列表和网格视图成本高昂,并且分层放置大量 UI 元素(即卡牌战斗游戏中堆叠的卡片)会导致过度绘制。

解决方案避免大量重叠的 UI 元素。

自定义代码以在运行时将分层的 UI 元素合并为更少的元素和批次。

如果您需要创建大型列表或网格视图,例如包含数百个项目的库存屏幕,请考虑重复使用较小的 UI 元素池,而不是每个项目使用单个 UI 元素。

查看这个 GitHub 项目 ,了解优化滚动列表的示例。

布局组界面
尽可能避免布局组

问题每个试图弄脏其布局的 UI 元素都会执行至少一次 GetComponent 调用。

当布局系统上的一个或多个子 UI 元素发生变化时,布局就会变“脏”。改变的子元素使拥有它的布局系统无效。

布局系统是位于布局元素上方的一组连续的 布局组 。布局元素不仅仅是布局元素组件(UI 图像、文本和滚动矩形),它还包含布局元素——就像滚动矩形也是布局组一样。

现在,关于当前的问题:每个将其布局标记为“脏”的 UI 元素将至少执行一次 GetComponent 调用。此调用在布局元素的父级上查找有效的布局组。如果找到,它会继续沿着 Transform 层次结构向上走,直到停止寻找布局组或到达层次结构根;以先到者为准。因此,每个布局组都会向每个子布局元素的弄脏过程添加一个 GetComponent 调用,这使得嵌套布局组的性能极其糟糕。

解决方案尽可能避免布局组。

使用锚点进行比例布局。在具有动态数量 UI 元素的热门 UI 上,考虑编写自己的代码来计算布局。确保根据需要使用它,而不是针对每个单独的更改使用它。

在我们的 文档中了解有关布局组的更多信息。

对象池接口
以智能方式集中UI对象

问题错误地使用池化 UI 对象

人们经常通过重新设置父级然后禁用 UI 对象来池化它们,这会导致不必要的污染。

解决方案首先禁用该对象,然后将其重新放入池中。

您将会弄脏旧的层次结构一次,但是一旦您重新设置它的父级,您将避免第二次弄脏旧的层次结构 - 并且您根本不会弄脏新的层次结构。如果要从池中删除一个对象,请先重新设置它的父级,更新数据,然后启用它。

了解有关 Unity 中基本对象池的概念的更多信息。

UI 画布组件
如何隐藏画布

问题不确定如何隐藏画布

隐藏 UI 元素和画布有时很有用。但如何才能有效地做到这一点呢?

解决方案禁用 Canvas 组件本身。

禁用 Canvas 组件将阻止 Canvas 向 GPU 发出绘制调用。这样,画布将不再可见。但是,Canvas 不会丢弃其顶点缓冲区,它会保留其所有网格和顶点。然后,当您重新启用它时,它不会触发重建 - 它只会再次开始绘制它们。

此外,禁用 Canvas 组件不会通过 Canvas 层次结构触发昂贵的 OnDisable/OnEnable 回调。只需小心禁用运行昂贵的每帧代码的子组件。

在此了解有关 Canvas 组件的更多信息。

在UI元素上优化应用动画器

问题在 UI 上使用动画

动画师会在每一帧中弄脏他们的 UI 元素,即使动画中的值没有改变。

解决方案使用代码进行 UI 动画。

仅将动画师放在始终变化的动态 UI 元素上。对于很少改变或响应事件而暂时改变的元素,请编写自己的代码或使用补间系统。Asset Store中提供了很多很好的解决方案。

使用全屏 UI 时,隐藏其他所有内容

问题全屏 UI 性能不佳

如果您的游戏显示完全覆盖场景的暂停或开始屏幕,则游戏的其余部分仍在后台渲染,这可能会影响性能。

解决方案隐藏其他所有内容。

如果您的屏幕覆盖了场景中的所有其他内容,请禁用相机渲染 3D 场景。类似地,禁用隐藏在顶部 Canvas 后面的 Canvas 元素。

考虑在全屏 UI 期间降低 Application.targetFrameRate,因为您不需要以 60 fps 的速度更新。

更多资源
优化游戏性能
获取免费电子书以了解更多

为您的玩家提供最佳的游戏体验。借助 Unity 专家工程师提供的 80 多个可操作的提示和最佳实践,您可以优化您的 PC 和主机游戏。

更具体地说,这些详细的实践由 Unity 的成功和Accelerate Solutions工程团队创建,这些实践是从与顶级工作室的实际合作中收集而来的,将有助于提高游戏的整体性能。

此内容是否有帮助?
是的!
还行。