Unity性能调优手册8UI:Canvas,Layout,RaycastTarget,Mask,TextMeshPro,UI显示

2023-11-20 09:20:32 浏览数 (2)

翻译自https://github.com/CyberAgentGameEntertainment/UnityPerformanceTuningBible/ uGUI (Unity标准UI系统)和TextMeshPro(将文本绘制到屏幕的机制)的调优实践

Canvas分区

在uGUI中,当Canvas中的元素发生变化时,会运行一个过程(重建)来重建整个Canvas UI网格。变化是任何变化,如主动切换、移动或调整大小,从外观的大变化到第一眼看不出来的小变化。重建过程的成本很高,所以如果执行太多次,或者Canvas中的ui数量很大,性能就会受到不利影响。 相反,重构的成本可以通过将Canvas除以某种程度的UI内聚来降低。例如,如果您有动画的ui和不动画的ui,您可以通过将它们放在单独的控件下来最小化动画重建 画布。 但是,您需要仔细考虑如何拆分它们,因为拆分画布将不适用于绘制批次。 Tips 当画布嵌套在画布下时,拆分画布也有效。如果子画布中包含的元素发生变化,则只会运行子画布的重建,而不会运行父画布。然而,仔细观察,当子画布中的UI被SetActive切换到活动状态时,情况似乎是不同的。在这种情况下,如果在父Canvas中放置了大量的ui,似乎就会出现导致高负载的现象。我不知道为什么会发生这种行为的细节,但似乎在切换活动状态时应该小心

UnityWhite

在开发ui时,我们经常希望显示一个简单的矩形对象。这就是UnityWhite派上用场的地方。UnityWhite是Unity内置的纹理,当Image或RawImage组件没有指定要使用的图像时使用(图8.1)。你可以看到UnityWhite是如何在框架中使用的调试器(图8.2)。该机制可用于绘制白色矩形,因此,通过将其与倍增色相结合,可以实现简单的矩形类型显示。

然而,由于UnityWhite的纹理与项目中提供的SpriteAtlas不同,因此绘制批次会被中断。这增加了绘制调用并降低了绘制效率。 因此,你应该在SpriteAtlas中添加一个小的(例如,4 x 4像素)白色正方形图像,并使用该Sprite绘制一个简单的矩形。这将允许批处理工作,因为相同的SpriteAtlas将用于相同的材质。

Layout 组件

uGUI提供了一个布局组件,允许您整齐地对齐对象。例如,VerticalLayoutGroup用于垂直对齐,GridLayoutGroup用于网格对齐。

使用Layout组件时,在创建目标对象或编辑某些属性时,会发生布局重建。布局重建,像网格重建一样,是一个昂贵的过程。 为了避免由于布局重建而导致的性能下降,尽可能避免使用布局组件是有效的。 例如,如果不需要动态放置,例如根据内容改变放置位置的文本,则不需要使用Layout组件。如果您确实需要动态放置,或者如果它在屏幕上大量使用,那么最好使用您自己的脚本来控制它。此外,如果需要将需求放置在相对于父元素的特定位置,即使父元素的大小发生了变化,也可以通过调整RectTransform锚点来实现。如果您在创建预制件时使用布局组件,因为它便于放置,在设置好位置后,如果不需要动态调整位置把布局组件删除再保存。

Raycast Target

Image和RawImage的基类Graphic有一个属性Raycast Target 。启用此属性后,其图形将成为单击和触摸的目标。当单击或触摸屏幕时,启用了此属性的对象将成为处理的目标,因此尽可能禁用此属性将提高性能

默认情况下启用此属性,但实际上许多图形并不需要启用此属性。另一方面,Unity有一个叫做预置的功能,允许你改变项目的默认值。具体来说,您可以分别为Image和RawImage组件创建预设,并将它们注册为Project Settings中的预置管理器中的默认预设。你也可以使用这个功能来禁用Raycast Target属性。 译者增加部分 可通过复写[MenuItem(“GameObject/UI/Image”),覆盖Unity自己创建的Image https://blog.csdn.net/Speculator_m/article/details/131988110

Masks

要在uGUI中表示遮罩,可以使用Mask组件或RectMask2d组件。 由于Mask使用模板来实现Mask,因此绘制成本会随着每个模板的增加而增加。另一方面,RectMask2d使用着色器参数来实现蒙版,因此抑制了绘图成本的增加。然而,Mask可以在任何形状中被掏空,而RectMask2d只能被掏空为矩形。 人们普遍认为,如果可用,应该选择RectMask2d,但最近才启用Unity用户也应该小心使用RectMask2d。 具体来说,当启用RectMask2d时,用于剔除每帧的CPU负载与其屏蔽目标的增加成正比。根据uGUI内部实现中的评论,这种现象即使在UI不移动任何东西时也会产生每帧加载,似乎是Unity 2019.3中包含的问题修复的副作用。 因此,采取措施尽可能避免使用RectMask2d是有用的,即使使用了,在不需要时将enabled设置为false,并将被屏蔽的目标保持在必要的最低限度。 译者增加部分 【腾讯文档】Mask与MaskD https://docs.qq.com/doc/DWlhrQ3lVemlQRVZx

TextMeshPro

在TextMeshPro中设置文本的常用方法是将文本分配给text属性,但是还有另一个方法SetText。 例如,SetText有许多重载,它们接受字符串和float类型的值作为参数。如果像清单8.1那样使用此方法,则可以打印第二个参数的值。但是,假设label是一个类型为TMP_Text(或继承自它)的变量和number的类型为float。 清单8.1

代码语言:javascript复制
label.SetText("{0}", number);

这种方法的优点是它减少了生成字符串的成本。 清单8.2

代码语言:javascript复制
label.text = number.ToString();

在使用text属性的方法中,如下例所示,执行float类型的ToString(),因此每次执行此过程都会产生字符串生成成本。相反,使用SetText的方法被设计成生成尽可能少的字符串,当要显示的文本频繁变化时,这是一个性能优势。 TextMeshPro的这个特性在与ZString 结合使用时也非常强大。 ZString是一个库,它减少了字符串生成过程中的内存分配。ZString为TMP_Text类型提供了许多扩展方法,通过使用这些方法,可以实现灵活的文本显示,同时减少字符串生成的成本。

UI显示开关

uGUI组件的特点是使用SetActive切换对象的高成本。这是由于OnEnable为各种重建设置Dirty标志并执行与掩码相关的初始化。因此,考虑使用SetActive方法的替代方法来切换UI的显示是很重要的。 第一种方法是将Canvas的enabled更改为false。这将阻止画布下的所有对象被渲染。因此,这种方法的缺点是,它只能在您想要隐藏Canvas下的所有对象时使用。

另一种方法是使用CanvasGroup。它有个函数可以调整它下面所有物体的透明度。如果你使用这个函数并将透明度设置为0,你可以隐藏其CanvasGroup

虽然这些方法有望避免由SetActive引起的负载,但你可能需要小心,因为GameObject将保持在活动状态。例如,如果定义了Update方法,请注意它们即使在隐藏状态下也会继续运行,这可能会导致负载意外增加。 作为参考,我们测量了带有图像组件的1280个GameObject的处理时间,使用每种方法在可见和隐藏状态之间切换。处理时间是使用Unity编辑器测量的(没有使用Deep Profile)。该方法的处理时间为实际切换的执行时间与UIEvents.WillRenderCanvases的执行时间之和。因为UI重建需要UIEvents.WillRenderCanvases,所以它被包含进入。 方式 显示处理时间 隐藏处理时间

从结果来看,我们发现在我们这次尝试的情况下,使用CanvasGroup的方法处理时间是迄今为止最短的。 译者增加部分 手游项目中会把频繁需要显示出现的UI的Scale为0

0 人点赞