UGUI源码解析——Graphic
  gEMN1CQh1PXt 2023年11月02日 100 0


一:前言

Graphic是UGUI的核心组件,负责图像的显示与更新,是MaskableGraphic的基类,而MaskableGraphic是Text、RawImage、Image的基类


二:源码解析——类头

UGUI源码解析——Graphic_赋值

Graphic继承自UIBehaviour和ICanvasElement

UIBehaviour是所有UGUI组件的最基类,负责接收来自UnityEngine或者UnityEditor的事件:UGUI源码解析——UIBehaviour

ICanvasElement负责接收Canvas重新渲染的事件:UGUI源码解析——CanvasUpdateRegistry

Graphic添加了三个特性

——DisallowMultipleComponent:不允许一个对象添加多个相同的组件

——RequireComponent:依赖于CanvasRenderer画布渲染组件和RectTransform组件

——ExecuteAlways:编辑器模式下可以运行

另外Graphic是一个抽象类,意味着不能被实例化,但是它提供了很多可重写的方法可被继承并重写


三:源码解析——继承自UIBehaviour的方法

——OnEnable

UGUI源码解析——Graphic_图像重建_02

首先调用CacheCanvas方法找到渲染当前元素的Canvas,之后调用GraphicRegistry.RegisterGraphicForCanvas方法将此元素注册到m_Graphics字典中(每个Canvas对应了一个Graphic序列),接着设置s_WhiteTexture(mainTexture的默认贴图),最后调用SetAllDirty将元素添加到布局和图像重建序列中(这样说有些不严谨,因为布局重建是通过LayoutRebuilder类管理的,如果父物体身上没有继承ILayoutGroup的组件是不会添加到布局重建序列中的),SetAllDirty下面会重点说

——OnDisable

UGUI源码解析——Graphic_unity_03

首先从GraphicRegistry和CanvasUpdateRegistry中将当前元素移除注册并清理canvasRenderer,最后通过LayoutRebuilder.MarkLayoutForRebuild方法将元素添加到布局重建序列中(因为Graphic已经Disable所以不需要将元素添加到图像重建序列中)

——OnRectTransformDimensionsChange

UGUI源码解析——Graphic_unity_04

将元素添加到图像和布局重建序列中,如果正在进行布局重建则只将元素添加到图像重建序列中

——OnBeforeTransformParentChanged

UGUI源码解析——Graphic_数据_05

首先将渲染当前元素的canvas从GraphicRegistry移除注册,并将元素添加到布局重建序列中

——OnTransformParentChanged

UGUI源码解析——Graphic_赋值_06

首先调用CacheCanvas方法找到渲染当前元素的Canvas,之后调用GraphicRegistry.RegisterGraphicForCanvas方法将此元素注册到m_Graphics字典中(每个Canvas对应了一个Graphic序列),最后使用SetAllDirty将元素添加到图像和布局重建序列中

——OnCanvasHierarchyChanged

UGUI源码解析——Graphic_数据_07

首先调用CacheCanvas方法找到渲染当前元素的Canvas,如果此canvas与缓存的canvas不同则移除之前注册的canvas并调用GraphicRegistry.RegisterGraphicForCanvas方法重新将此元素注册到m_Graphics字典中(每个Canvas对应了一个Graphic序列)


四:源码解析——继承自ICanvasElement的方法

——Rebuild

UGUI源码解析——Graphic_数据_08

在CanvasUpdateRegistry类中给委托Canvas.willRenderCanvases注册了PerformUpdate方法,PerformUpdate会在CanvasRender渲染之前会遍历布局和图像重建序列调用每个元素的Rebuild方法,在Rebuild方法里调用UpdateGeometry和UpdateMaterial更新网格和材质,布局不在Graphic类中管理而是单独通过LayoutRebuilder去管理的​​​​​

UGUI源码解析——Graphic_赋值_09

UpdateGeometry方法中首先调用了OnPopulateMesh方法将元素的顶点、颜色、UV等信息暂存到m_VertexHelper中,Graphic类中的OnPopulateMesh绘制了一个矩形,我们可以在自己的UI类中,重写OnPopulateMesh方法,实现自定义的UI

接着遍历身上继承了IMeshModifier的组件(一般是实现网格的一些特效,例如Shadow、Outline),更新VertexHelper数据

然后调用s_VertexHelper.FillMesh(workerMesh)将暂存的数据填入workMesh中

最后调用canvasRenderer.SetMesh(workerMesh)将workerMesh赋值给canvasRenderer,这样下一帧canvasRenderer渲染时就会用workerMesh的数据进行绘制了(SetMesh方法最终在C++中实现,这也是UGUI的效率比NGUI高一些的原因,因为NGUI的Mesh合并是在C#中完成)


UI是如何绘制出来的?
首先要生成显示UI用的Mesh,例如一个矩形的Mesh,由4个顶点,2个三角形组成,每个顶点都包含UV坐标、顶点色等信息,调节Image或者Text的颜色,其实就是改变它们的顶点色
然后将网格和纹理信息发送给CanvasRender,CanvasRender再将渲染命令发送给GPU进行渲染,这样一个简单的UI元素就显示出来了,这个过程叫做一次批处理
其实这个流程与渲染一个普通的Cube是类似的
可以简单的理解为,所谓的UI其实就是用一个正交的Camera看着若干的平面网格,只不过单单的显示出来还远远不够,还有一些其他操作,比如DrawCall需要合并,Button需要点击等等


UGUI源码解析——Graphic_数据_10

UpdateMaterial方法中首先将自身材质赋值给canvasRenderer(遍历身上继承了IMaterialModifier的组件(例如Mask),然后将贴图赋值给canvasRenderer,这样下一帧canvasRenderer渲染时就会用materialForRendering和mainTexture的数据进行绘制了


五:SetDirty

UGUI源码解析——Graphic_图像重建_11

SetLayoutDirty调用了LayoutRebuilder.MarkLayoutForRebuild方法,此方法内部其实是调用了CanvasUpdateRegistry类中的InternalRegisterCanvasElementForLayoutRebuild方法将元素添加到了布局重建的序列中

SetMaterialDirty和SetVerticesDirty调用了CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild方法将元素添加到了图像重建的序列中


六:UGUI优化技巧


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论