1.获取源码
-
新建Unity项目,找到Project/Packages/UnityUI,注意在项目目录的Packages文件夹中找,右键 Show in Explorer,将其复制到任意一个新的文件夹中(记住保存的位置,待会需要引用)。
-
接下来打开Window/Package Manager
-
找到Unity UI,将其Remove
-
然后点击“+”号,选择Add package form disk...,找到之前保存的UI包,进入目录后选中package.json,点击打开。
2.UGUI事件系统
简单地说就是找到所点位置最上面可互动对象,然后查找对象上所有继承IPointerClickHandler、IDragHandler、IScrollHandler等的组件并执行。
2.1 流程
- 所在canvas所属相机的depth(如果有的话)
- 所在canvas的sortOrderPriority
- 所在canvas的renderOrderPriority
- sortingLayer
- sortingOrder
- depth
- distance
- index
2.2 优化
组件上的Recast Target,如果不需要可以取消勾选,防止运算的时候浪费性能
3.UGUI图形系统
一个物体的显示需要顶点、三角面、材质。顶点和三角面是UI提供的,UI把这些信息提供给底层的Unity Engine的CanvasRender。最终由Canvas进行渲染,Canvas功能都由C++代码完成,没有公开源码。
在UGUI中用于显示的都继承自Graphic。
3.1 流程
3.2 UI合批
可以使用Profiler面板(Window->Analysis->Profiler)来查看合批结果。
合批规则:
-
合批是以Canvas为单位的,把所有的Canvas找出来,提出不必渲染的Canvas。
-
计算Canvas下各UI空间的深度值。计算规则如下
-
按照Hierarchy中从上往下的顺序一次遍历Canvas下所有UI元素;
-
对于当前的UI元素CurrentUI
-
如果CurrentUI不渲染,则Depth = -1;
-
如果CurrentUI要渲染,但CurrentUI下面没有UI元素与它相交,则Depth = 0;
-
如果要渲染,下面只有一个UI元素(LowerUI)与其相交,若可以合批,则深度相同,如果不能合批,深度在LowerUI的深度上+1;
-
如果要渲染,下面有n个元素,则按照步骤iii分辨计算出n个Depth,取其中的最大值。
-
排序得到VisiableList。具体的排序规则是:依次按照Depth、material ID、texture ID、RendererOrder(UI层级队列顺序、即Hierarchy面板上的顺序)排序。然后剔除Depth = -1的UI元素,得到Batch前的UI元素队列,就是VisiableList。
-
先按Depth从小到大的顺序排列;
-
Depth排完之后,Depth相同的元素按照material ID从小到大排序;
-
Material ID排完后,material ID相同的元素按照texture ID从小到大排序;
-
texture ID排完后,texture ID相同的元素按照Hierarchy删的顺序排列,Hierarchy越上面的排在队列前面。
-
得到VisiableList,判断VisiableList中相邻的元素是否能够合批(材质相同)。这里不再考虑Depth是否相同,只要两个元素相邻,材质相同,即使两个元素的Depth不相同,这两个元素也能合批。然后一个批次一个批次合并网络,提交GPU进行渲染。
3.3 合批优化
-
使用图集
-
动静分离,即动态部分和静态部分分别使用不同的Canvas
-
Text如果可以就用图片代替
-
避免频繁删除/增加UI对象,UI层次机构变化会引起Canvas的更新
-
避免UI元素数目过多和层次结构过于复杂,影响Batch更新的速度
3.4 Overdraw优化
Overdraw是指一帧当中,同一个像素被重复绘制的次数。Fill Rate(填充率)是指显卡每帧每秒能够渲染的像素数。在每帧绘制中,如果一个像素被反复绘制的次数越多,那么它占用的资源也必然更多。Overdraw与Fill Rate成正比,目前在移动设备上,FillRate的压力主要来自半透明物体。因为多数情况下,半透明物体需要开启 Alpha Blend 且关闭 ZTest和 ZWrite,同时如果我们绘制像 alpha=0 这种实际上不会产生效果的颜色上去,也同样有 Blend 操作,这是一种极大的浪费。
-
减少UI重叠层级,隐藏处于底下被完全覆盖的UI面板。
-
对于需要暂时隐藏的UI,不要直接把Color属性的Alpha值改为0,UGUI中这样设置后仍然会渲染,应该用CanvasGroup组件把Alpha值置零。
-
需要响应Raycast事件时,不要使用空Image,可以自定义组件继承自MaskableGraphic,重写OnPopulateMesh把网格清空,这样可以响应Raycast而又不需要绘制Mesh。
-
打开全屏界面,关闭场景摄像机。对于一些非全屏但覆盖率较高的界面,在对场景动态表现要求不高的情况下,可以记录下打开UI时的画面,作为UI背景,然后关掉场景摄像机。
-
裁掉无用区域,镂空,对于Sliced类型的Image可以看情况取消Fill Center。
-
慎用Mask组件和Outline、Shadow组件
-
尽量保持UI上的粒子特效简单,尽量使用序列帧实现。
3.4 Mask与RectMask2D遮罩
Mask:继承自IMaterialModifier,对材质球进行修改,实质上改了材质的模板属性。
Mask组件需要依赖一个Image组件,裁剪区域就是Image的大小,其性质包括:
-
Mask会在首尾(首=Mask节点,尾=Mask节点下的孩子遍历完后)多出两个drawcall,Mask下的子节点Image导致1个drawcall。
-
多个Mask间如果符合合批条件这两个drawcall可以对应合批(mask1的首和mask2的首合,mask1的尾合mask2的尾合,首尾不能合)。重叠的话就不能合批,因为不在一个depth
-
Mask内的UI节点和非Mask内的UI节点不能合批。
-
计算Depth的时候,当遍历到一个Mask的首,把它当作一个不可合批的UI节点看待,但注意可以作为其他孩子UI节点的bottomUI。
RectMask2D:继承自IClipper,default shader会执行UnityGet2DClipping,并把结果赋给α,这个函数会判断点是否在矩形框中,如果是返回1,否则返回0(也就是不可见)。
RectMask2D不需要依赖一个Image组件,其裁剪区域就是它的RectTransform的rect大小,其性质如下:
-
RectMask2D节点下的所有孩子都不能与外界UI节点合批且多个RectMask之间也不能合批。
-
计算depth的时候,所有RectMask2D都按一般UI节点看待,只是它没有CanvasRenderer组件,不能看作任何UI空间的bottomUI。
参考资料
-
[UGUI源码一]6千字带你入门UGUI源码 [UGUI源码一]6千字带你入门UGUI源码 - 知乎
-
unity的ugui源码解读之事件unity的ugui源码解读之事件_哔哩哔哩_bilibili
-
unity的ugui源码之显示和布局unity的ugui源码之显示和布局_哔哩哔哩_bilibili
-
Unity3D UGUI系列之合批 https://www.laowangomg.com/?p=488
-
UGUI性能优化总结 UGUI性能优化总结 | 无境

1万+

被折叠的 条评论
为什么被折叠?



