UGUI深入理解--性能优化总结

在游戏的开发中,UI逻辑占很大比重,所以对UI性能的优化就很关键,而阅读源码,可以让我们知道性能消耗点在哪,能做哪些有针对性的优化。总结一下性能优化的要点,以及优化方法对应的原理,希望能真的理解,并表达清楚。

下面准备分3方面来总结优化经验,首先要知道性能消耗点在哪,以及对这些消耗点有哪些方法可以优化,然后是常见组件的一些优化方法,最后是一些项目中用到过的策略,这部分是比较通用的。


性能消耗的关键点

unity将UI的渲染分为两个步骤,对mesh的操作称为Rebatch,对material和layout的操作称为Rebuild,所以性能消耗也是在这两个部分。

Rebatch的内部实现

Rebatch发生在C++层面,是指Canvas分析UI节点生成最优批次的过程,节点数量过多会导致算法(贪心策略)耗时较长。对应SetVerticesDirty,当一个canvas中包含的mesh发生改变时就触发,例如SetActive、transform的改变、 颜色改变、文本内容改变等等,canvas独立处理,互相不影响。消耗在对meshes按照深度和重叠情况排序、共享材质的检测等。

Batch以Canvas为单位,同一个Canvas下的UI元素最终都会被Batch到一个Mesh中。Batch前,UGUI根据UI材质以及渲染顺序重排,在不改变渲染结果的前提下,尽可能将相同材质的UI元素合并在同一个SubMesh中,以减少DC。Batch只在UI元素发生变化时进行,合成的Mesh越大,耗时越大。重建对Canvas下所有ui元素生效,不论是否修改过。

5.2之后底层是多线程的,考虑到现在手机上都是多核,这部分消耗可能会小很多。不过对于复杂的UI,还是多注意一些更好。

针对Rebatch的优化方法

Canvas动静分离,合理划分,按游戏类型和UI数量划分,太多也有额外消耗。

减少节点层次和数量,合批计算量小,速度快。

使用相同材质贴图的UI尽量保持深度相同,这样对合批算法友好,速度快。

修改Image的Color属性,原理是修改顶点色,会引起网格Rebatch,同时触发Canvas.SendWillRenderCanvases。好处在于修改顶点色材质不变,没有额外DC。修改shader颜色不会重绘,材质不变,没有Rebatch。

Rebuild的触发原因

Rebuild发生在C#层面,是指UGUI库中layout组件调整RectTransform尺寸、Graphic组件更新Material,以及Mask执行Cull的过程,耗时和发生变化的节点数量基本呈线性相关。

只有LayoutGroup的直接子节点,并且是 Graphic类型的(比如 Image 和 Text)会触发SetLayoutDirty。

Graphic改变的原因包括,基本的大小、旋转以及文字的变化、图片的修改等等,对应SetMaterialDirty。

针对Rebuild的优化方法

少用layout,简单的布局RectTransform代替

Canvas动静分离,按项目类型去规划。

可以说针对这两部分的优化方法,基本都是UI规范,一个靠谱的UI策划可以说是十分重要了。


针对组件的优化

不要用空的Image,只接收事件不显示的对象,继承Graphic,填充数据函数写成空的。

不显示的对象,不要SetActive,设置Canvas Group的alpha为0,scale为0,这样vbo不会被清除。或是canvasRenderer.cull为true。

不需要响应事件的,取消RaycastTarget。添加工具,代码设置Image和text默认取消。

canvas渲染模式为World Space或Screen Space Camera,始终分别设置事件摄像机和渲染摄像机非常重要,没有设置会通过FindWithTag查找主摄像机。

少用Mask,用RectMask2D代替。

TextMeshPro代替原生text。

字体OutLine多绘制4次。PixelPerfect有消耗,滑动消耗更大。

Font.CacheFontForText:生成动态字体Font Texture,一次性打开UI界面中的文字越多,开销越大。如果当前Font Texture不能容下接下来的文字,扩大texture,性能影响大。


通用策略

实现一个效果的方法有很多,一开始就选择更高效的那个,可以为后续减少很多麻烦。

压缩图片,降低内存,动态加载释放和缓存。

图集管理,减少drawcall,加载压力,这部分按游戏类型不同策略不同。一个简单的规则是一个UI只依赖自身一个图集,加上通用图集,实际做的时候图集还要多花时间检测,是个持续调优的事。

特定条件下用大图片代替多个小图组合,减少overdraw。

血条、飘字等实时刷新的注意性能,容易出现瓶颈。可以减少可见数量,降低远处刷新频率等。

gameobject隐藏方法,enable有很大消耗,底层处理很多东西,移动坐标或是设置layer。

简化UI结构,空的节点越少越好,层级越少越好,不用的节点及时删除。因为UI一般修改较多,很容易把不显示的隐藏,依然有一定消耗。

动态图集,用在图片数量多,不能合成一个大图集的情况。

界面上少挂粒子特效,动态加载。

全屏UI时关闭场景相机。


大致总结了一下UI优化上的一些要点,再分享一点优化UI的体会吧。

一直以来做的都是mmo类型的游戏,UI算是开发的大头,但一般不会成为项目性能的瓶颈,因为UI分两种,常驻的和功能性的。常驻的就是主界面,战斗相关飘字血条等,这部分是要注意的,并没有太好的优化方法,因为数量在那摆着,一般血条飘字这种会限制数量,用插件等方法,也算是比较成熟的解决方案了。再一个就是功能UI,一般这种都是全屏的,也就是说同时并没有多少其他消耗,UI占用多些问题也不大,当然做的时候还是会加限制。

个人感觉UI开发的难点,不在于实现功能和性能,而在于把效果做好,策划程序美术很好的配合,很多项目一遍遍给临时资源,真让人头大。。

发布于 2021-01-13 16:13