UGUI中与显示图像相关的功能的承载者,由其衍生出的MaskableGraphic类是Image、Text等类的父类。
Attribute
| 12
 3
 4
 
 | [DisallowMultipleComponent][RequireComponent(typeof(CanvasRenderer))]
 [RequireComponent(typeof(RectTransform))]
 [ExecuteInEditMode]
 
 | 
不支持同一个对象上挂载多个此组件,需要组件CanvasRenderer和RectTransform,会在编辑器模式下执行。
基类和实现的接口
| 12
 3
 4
 5
 6
 
 | public abstract class Graphic: UIBehaviour,
 ICanvasElement
 {
 //...
 }
 
 | 
继承自UIBehaviour和ICanvasElement:
UIBehaviour
UIBehaviour是所有UI组件的抽象基类,提供了接收UnityEngine或UnityEditor的事件的接口,部分接口如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 
 | //...
 protected virtual void Awake()
 {}
 
 protected virtual void OnEnable()
 {}
 
 protected virtual void Start()
 {}
 
 protected virtual void OnDisable()
 {}
 
 protected virtual void OnDestroy()
 {}
 
 //...
 
 protected virtual void OnRectTransformDimensionsChange()
 {}
 
 protected virtual void OnBeforeTransformParentChanged()
 {}
 
 protected virtual void OnTransformParentChanged()
 {}
 
 protected virtual void OnDidApplyAnimationProperties()
 {}
 
 protected virtual void OnCanvasGroupChanged()
 {}
 
 protected virtual void OnCanvasHierarchyChanged()
 {}
 
 //...
 
 | 
在OnEnable()和OnDisable()以及涉及到父节点或Canvas变化的方法时,会调用GraphicRegistry注册或注销的方法,将自己注册至对应的Canvas:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 
 | protected override void OnEnable(){
 //...
 GraphicRegistry.RegisterGraphicForCanvas(canvas, this);
 //...
 }
 
 protected override void OnBeforeTransformParentChanged()
 {
 GraphicRegistry.UnregisterGraphicForCanvas(canvas, this);
 //...
 }
 
 protected override void OnTransformParentChanged()
 {
 //...
 GraphicRegistry.RegisterGraphicForCanvas(canvas, this);
 //...
 }
 
 protected override void OnDisable()
 {
 //...
 LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
 //...
 }
 
 protected override void OnCanvasHierarchyChanged()
 {
 //...
 if (currentCanvas != m_Canvas)
 {
 GraphicRegistry.UnregisterGraphicForCanvas(currentCanvas, this);
 
 if (IsActive())
 GraphicRegistry.RegisterGraphicForCanvas(canvas, this);
 }
 }
 
 | 
ICanvasElement
ICanvasElement提供了Canvas对其管理的元素的更新事件的接口,Canvas对于UI组件的更新是由CanvasUpdateRegistry来管理的,稍后介绍。只有实现了ICanvasElement接口的类才可以通过CanvasUpdateRegistry 注册更新事件。ICanvasElement的部分接口如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | //...
 void Rebuild(CanvasUpdate executing);
 
 //...
 
 void LayoutComplete();
 void GraphicUpdateComplete();
 
 //...
 
 | 
其中Rebuild是当Canvas上的元素需要更新时,由CanvasUpdateRegistry调用的用于重建UI组件的方法,而LayoutComplete和GraphicUpdateComplete会在重建完成时按需调用。
GraphicRegistry
单例类,用于保存Canvas和与其关联的Graphic。并可以通过其获取到某个Canvas关联的所有Graphic。在其内部每个Canvas对应的所有Graphic是通过一种名为IndexedSet的数据结构来保存的。IndexedSet<T>,在UGUI其它的地方也会遇到,是一个有序集合,它支持以下特性:
- 元素唯一
- 快速随机删除
- 快速在尾部插入(唯一)
- 顺序访问
m_xxxDirty
Graphic通过脏标记(Dirty flag)来判断是否需要重建,以及哪一种信息需要重新绘制。脏标记m_VertsDirty和m_MaterialDirty(其实还有一个布局相关的脏标记)会在响应指定的事件时置为true,例如以下的事件回调:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | protected override void OnRectTransformDimensionsChange(){
 //...
 if (CanvasUpdateRegistry.IsRebuildingLayout())
 SetVerticesDirty();
 else
 {
 SetVerticesDirty();
 SetLayoutDirty();
 }
 }
 
 protected override void OnTransformParentChanged()
 {
 //...
 SetAllDirty();
 }
 
 
 protected override void OnEnable()
 {
 //...
 SetAllDirty();
 }
 
 | 
CanvasUpdateRegistry
CanvasUpdateRegistry是一个单例,Canvas和各UI组件沟通的桥梁,UI组件通过其来向Canvas注册更新事件。
在Graphic的一些设置脏标记的方法中:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | public virtual void SetVerticesDirty(){
 //...
 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
 //...
 }
 
 public virtual void SetMaterialDirty()
 {
 //...
 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
 //...
 }
 
 | 
会调用RegisterCanvasElementForGraphicRebuild来注册更新的回调方法,然后在CanvasUpdateRegistry.PerformUpdate()中调用ICanvasElement.Rebuild(...)。
Rebuild()
Graphic类的重建方法,根据传入的CanvasUpdate的枚举值来更新,实际上对于Graphic类,只响应CanvasUpdate.PreRender序列的重建。并且根据自身的脏标记m_VertsDirty或m_MaterialDirty,判断更新几何或者材质。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | public virtual void Rebuild(CanvasUpdate update){
 //...
 switch (update)
 {
 case CanvasUpdate.PreRender:
 if (m_VertsDirty)
 {
 UpdateGeometry();
 m_VertsDirty = false;
 }
 if (m_MaterialDirty)
 {
 UpdateMaterial();
 m_MaterialDirty = false;
 }
 break;
 }
 }
 
 | 
更新几何和更新材质的逻辑是Graphic类承担显示图像职责最核心的内容,UpdateGeometry()用于处理网格和顶点,UpdateMaterial()用于更新材质和纹理。
UpdateGeometry()
| 12
 3
 4
 5
 6
 7
 
 | protected virtual void UpdateGeometry(){
 if (useLegacyMeshGeneration)
 DoLegacyMeshGeneration();
 else
 DoMeshGeneration();
 }
 
 | 
其中DoLegacyMeshGeneration和DoMeshGeneration都是重新生成网格的方法,所执行的逻辑相似,DoLegacyMeshGeneration中直接操作workerMesh,而后者通过VertexHelper来完成网格生成与绘制的部分逻辑,最后通过VertexHelper.FillMesh(Mesh mesh)来完成网格的生成。这里以DoMeshGeneration()为例:
DoMeshGeneration()
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | private void DoMeshGeneration(){
 if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0)
 OnPopulateMesh(s_VertexHelper);
 else
 s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.
 
 var components = ListPool<Component>.Get();
 GetComponents(typeof(IMeshModifier), components);
 
 for (var i = 0; i < components.Count; i++)
 ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);
 
 ListPool<Component>.Release(components);
 
 s_VertexHelper.FillMesh(workerMesh);
 canvasRenderer.SetMesh(workerMesh);
 }
 
 | 
在DoMeshGeneration()中主要完成了以下的工作:
- OnPopulateMesh - 获取当前的- RectTransform的尺寸构建两个三角形网格(组成一个矩形),临时保存在- s_VertexHelper内。
 
- ModifyMesh - 获取所有实现- IMeshModifier接口的组件,调用其- ModifyMesh(VertexHelper verts)方法,来实现对网格的修改变化
 
- SetMesh - 将- Graphic此次重建的网格信息提交给- canvasRenderer;
 
UpdateMaterial()
除了更新几何之外,材质信息通过UpdateMaterial()来更新,内容很简单,重设材质和纹理:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | protected virtual void UpdateMaterial(){
 if (!IsActive())
 return;
 
 canvasRenderer.materialCount = 1;
 canvasRenderer.SetMaterial(materialForRendering, 0);
 canvasRenderer.SetTexture(mainTexture);
 }
 
 | 
以上就是Graphic实现显示图像功能的核心逻辑代码,除了这些之外,还有两个很重要的方法CrossFadeColor()和CrossFadeAlpha(),它使用UGUI中的TweenRunner和ColorTween来完成一个基于协程的改变颜色(或透明度)的动画。如CrossFadeColor():
CrossFadeColor()
| 12
 3
 4
 5
 6
 7
 8
 
 | public virtual void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha, bool useRGB){
 //...
 var colorTween = new ColorTween {duration = duration, startColor = canvasRenderer.GetColor(), targetColor = targetColor};
 //...
 m_ColorTweenRunner.StartTween(colorTween);
 
 }
 
 | 
本系列其它文章详见Unity3D UGUI 源码学习
REFERENCE
https://bitbucket.org/Unity-Technologies/ui
http://blog.csdn.net/ecidevilin/article/details/52548747