type
status
date
summary
tags
category
icon
一、概述
无论是在商业还是个人项目中,Image和Text组件都是我们最常接触的组件之一,它们间接继承自Graphic类。Graphic类中实现了构建网格,更新材质等重要的方法,这篇文章想要对Graphic类和Graphic类组件的重建流程进行讨论。
文章内的Unity源码版本基于2019.1,链接地址如下:
二、Graphic类
2.1 定义与继承关系
Graphic类是所有可视化UI组件(即有带材质的)的基类,当我们想要创建一个可视化的UI组件,我们应该让组件继承Graphic。需要注意的是,一个对象上只能绑定一个Graphic组件。
在源码中,官方例举了一个简单的示例来展示如何自定义一个可视化的组件,感兴趣可以点击上方的源码链接查看。
2.2 数据成员
Graphic类的主要数据包括贴图、材质、颜色、网格数据和所属的Canvas以及CanvasRenderer
===
组件所属的Canvas和CanvasRenderer
===
贴图(Texture),包括一张公共的白色贴图和此UI元素的图片
===
材质(Material),包括一个公共的默认材质和当前材质
===
颜色(Color),内置UI组件使用该颜色作为顶点颜色,我们能够通过改变该颜色来控制Graphic组件(Text,Image,RawImage)的颜色
===
网格数据(Mesh),s_Mesh用来设置给canvasRenderer,s_VertexHelper是辅助设置mesh数据的中间结构
三、UI重建流程
在上一篇文章对Canvas进行分析后,我们知道Canvas在渲染前的每帧会调用
PerformUpdate
方法,对重建队列中的UI元素进行遍历重构,那么一个值得探究的问题是:UI元素会在什么时机,通过什么方式被添加进队列中呢?===
继承自Graphic类的UI元素在初始化或关键属性被改变后,元素会调用对应的回调方法,然后在方法内部将自身注册进CanvasUpdateRegistry的重建队列中。我们首先将目光放在这些特定方法的调用时机上。
将UI元素添加进重建队列的方法的调用时机
OnRectTransformDimensionsChange
:当UI元素的RectTransform尺寸被改变时调用,需要注意的是,当任一子对象的RectTransform尺寸改变时也会调用(判断依赖于锚点)
OnTransformParentChanged
:当关联的Transform的父物体变化后方法被调用,指Hierarchy上的父子层级关系变化。由面板上拖拽调整父子关系或调用transform.SetParent()触发。
OnDidApplyAnimationProperties
:当通过animation clip属性改变时调用
OnCullingChanged
:当CanvasRenderer的cull属性被改变时调用
SetMaterialDirty
:当设置元素的材质时调用
OnEnable
:
OnValidate
:编辑器下,脚本被加载或 Inspector 中的任何值被修改时,方法被调用
Reset
:编辑器下,脚本被加载或 Inspector 上的Reset被点击时,方法被调用
===
在上述方法的内部,程序会调用不同类型
SetXXXDirty()
方法,将UI元素添加进CanvasUpdateRegistry对应的重建队列中,我们这里以OnRectTransformDimensionsChange
为例进行查看===
通过代码可以看到,
SetXXXDirty()
一共有三种类型,分别是布局,顶点和材质。===
在CanvasUpdateRegistry中,管线会在每帧的
willRenderCanvases
事件中对重建队列中的ICanvasElement对象执行更新操作,调用重建队列中对象的Rebuild
方法(Graphic类继承了ICanvasElement接口并实现了Rebuild
方法),执行流程可以参阅 Unity UGUI模块 Canvas ===
继承自Graphic的对象在
Rebuild
方法中通过UpdateMaterial
接口中更新CanvasRenderer中的材质和贴图,通过UpdateGeometry
接口将网格数据设置到CanvasRenderer中在上面的代码中,有两个接口起到了重要的作用,一个是IMeshModifier,另一个则是IMaterialModifier。
IMeshModifier接口中定义两个重载的
ModifyMesh
方法,通过重写该方法,我们能够自定义的修改UI元素的网格,去实现一些我们想要的UI特效,例如常见的Outline描边、Shadow阴影和PositionAsUV1镂空。这三个效果都派生自BaseMeshEffect类,BaseMeshEffect就继承了IMeshModifier接口。IMaterialModifier接口则提供了一个可以修改材质的
GetModifiedMaterial
方法,Mask组件则是通过实现该方法实现遮挡效果从上面的代码可以看出,CanvasRenderer 和 Canvas 才是合并和渲染网格的关键,但可惜的是CanvasRenderer 和 Canvas 并没有在官网的UGUI代码中开源出来,于是我们只能猜测其内部做了什么。一个合理的猜想是,每次重构时获取 Canvas 下面所有的 CanvasRenderer 实例,将它们的 Mesh 合并起来然后进行渲染。
四、引用
- 作者:Felix
- 链接:felix1125.com/article/66152ec9-f65b-47d5-a242-ceacb5d31ce6
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章