最近在处理UGUI的效率问题,所以顺便翻译一下Unity UI的相关文档,以供查阅。
基本概念
Canvas(画布)组件是所有UI元素都需要用到的类,通过 Canvas 组件,UI元素的顶点和面得以映射到世界坐标系中,并根据 Canvas 的设置决定如何绘制。一个很神奇的地方就是,当创建一个 Canvas 元素的时候,场景里会出现一个非常硕大的平面,相对于普通的模型而言非常巨大。
图中红框里的立方体的边长是1米,旁边巨大的白色细框是 Canvas 的范围,可以看到比立方体大很多。造成这个现象的原因是UI在 Scene View 视图中绘制时,用的是1个单位对应1个像素绘制的,而在世界坐标系中,1个单位就代表了1米。
Canvas 并不是每一帧都在刷新,而是当底下的UI元素需要重新合批(rebatch)的时候才刷新,这个时候 Canvas 被认为是有修改的(dirty)。另外,顶点和面的几何信息并不是 Canvas 直接收集的,而是通过 Canvas Renderer 组件。Unity官方文档强烈建议自定义的UI组件物体上面一定要添加一个 Canvas Renderer 组件。
另外两个比较重要的基础组件包括 Graphic 组件和 Layout 组件,前者是所有UI元素的基类,提供基本的绘制接口;后者是调整UI布局的组件,只跟 RectTransforms 相关,并不依赖于 Graphic 相关的类。它们共同的特征就是在 CanvasUpdateRegistry 类中注册了监听事件,当 CanvasUpdateRegistry 收到一个 willRenderCanvases 重建(rebuild)事件的时候,会调用相应的函数处理。
UGUI渲染时有一个特性,就是所有的UI元素都是在Transparent队列中绘制的,并且不会因为被别的UI元素遮挡而剔除出渲染队列。因此,如果有全屏的UI存在的话,如果有被盖住的UI,最好是将其设为Disabled状态,或者是将Canvas组件设为Disabled,后者的效率更高一些,但是无法处理特效的显隐。
UI的合批操作
在UI重建之前,需要对Canvas组件下的UI元素进行合批,以便按照深度排序,并收集各个UI的Mesh信息。正常情况下,合批操作只会在Canvas被设为Dirty的情况下需要处理。能够导致这种情况的时间包括但不限于子元素的移动,缩放,添加,删除等操作。文档中还提到一点,收集的Mesh信息只包括Canvas底下包含的Canvas Renderer组件传回来的信息,并且不包括子Canvas。因此,如果在同一个界面中有一部分元素需要频繁修改,另一部分保持不变的情况,可以考虑给经常修改的部分单独再添加一个Canvas组件。
UI的重建过程
完成了合批操作之后,Unity会发送渲染指令给图形管道,以便完成Mesh的重建过程。如前文所述,CanvasUpdateRegistry 负责处理重建过程,关联的函数是PerformUpdate。
一共有三个步骤:
- 被置为dirty状态的Layout组件需要通过ICanvasElement.Rebuild方法重建子UI元素的布局;
- ClippingRegistry.Cull方法会根据UI元素的大小裁剪掉所有注册的可裁剪元素;
- 被置为dirty状态的Graphic元素会重建它们的图形元素(前者Graphic指Graphic类相关的元素,后者的图形指的是实际显示的图形)
可以看出重建过程分为两部分,分别是Layout重建和Graphic重建。Layout重建有三个部分,分别是PreLayout,Layout和PostLayout。Graphic重建有两个部分,分别是PreRender和LatePreRender。
Layout重建之前,首先需要按照层级关系进行排序,考虑到更接近根物体的Layout可能会影响到层级更低的物体的Layout,因此必须先计算它们的Layout。因此,UGUI采用的是根据层级关系排序,层级越高则在Layout列表里的位置就越靠前。
当 ICanvasElement 组件的 Rebuild 方法被调用时,Graphic类会在PreRender这一步检查有哪些Graphic元素需要被重建。需要重建的情况包括:
- 顶点数据被置为dirty (比如RectTransform修改了大小),则mesh信息会被重建
- 材质数据被置为dirty (比如组件的材质球或者贴图被修改了),则同一个GameObject上挂在的Canvas Renderer的材质也会被更新
Graphic重建并不需要有任何的顺序,因此也不需要对输入进行排序。
参考文档:
https://unity3d.com/cn/learn/tutorials/topics/best-practices/fundamentals-unity-ui
Comments !