关于源码
关于源码可以参考这篇博客下载或调试。
Graphic
简介
继承自
UIBehaviour
(所有UI
组件的基类,负责接收来自UnityEngine
或者UnityEditor
的事件)继承
ICanvasElement
(负责接收Canvas
重新渲染的事件)
UGUI
的核心组件,负责显示图像抽象类
MaskableGraphic
继承自该类,MaskableGraphic
是可遮罩图像(RawImage
、Image
和Text
的基类)的基类。为
MaskableGraphic
及其子类提供了公用的可继承的方法
特性
[DisallowMultipleComponent] [RequireComponent(typeof(CanvasRenderer))] [RequireComponent(typeof(RectTransform))] [ExecuteInEditMode]
DisallowMultipleComponent
:不允许一个对象有两个相同的组件,即不能有两个可遮罩图像。(eg.Image
或者一个RawImage
和一个Text
)。
RequireComponent
:依赖于CanvasRenderer
(画布渲染器)和RectTransform
(矩形变换)两类组件。
ExecuteInEditMode
:在编辑模式执行。
借助UIBehaviour的触发点的逻辑
OnEnable : 通过
CacheCanvas
方法,获取父对象中的Canvas
组件。并注册到GraphicRegistry
(可以看成Canvas
的渲染管理中心,可以获取指定Canvas
所包含的Graphic
等信息)。修改s_WhiteTexture
(对应属性MainTexture
)。最后SetAllDirty
(设置Layout
、Vertices
、Material
为Dirty
)。OnDisable : 从
GraphicRegistry
和CanvasUpdateRegistry
两个管理中心中注销,并清理CanvasRenderer
,完成后通知LayoutRebuilder
重建布局。OnRectTransformDimensionsChange : 当
RectTransform
维度改变时,将Layout
、Vertices
设为Dirty
。OnBeforeTransformParentChanged : 在父对象改变前,从
GraphicRegistry
注销,并通知LayoutRebuilder
重建布局。OnTransformParentChanged : 当父对象改变时,重新获取父对象中的
Canvas
,重新注册到GraphicRegistry
,并SetAllDirty
。OnDidApplyAnimationProperties : 当应用动画属性后,调用
SetAllDirty
。OnCanvasHierarchyChanged : 当画布层次改变时,如果
Canvas
改变,重新注册到GraphicRegistry
。
SetAllDirty 细节代码
SetLayoutDirty
里会通知LayoutRebuilder
布局需要重建。SetVerticesDirty
和SetMaterialDirty
里会注册CanvasUpdateRegistry
,等待重建Canvas
时重建Graphic
。
三者都会广播对应的事件(回调),可以通过RegisterDirtyLayoutCallback
等方法添加事件监听。
public virtual void SetAllDirty() { SetLayoutDirty(); SetVerticesDirty(); SetMaterialDirty(); } public virtual void SetLayoutDirty() { if (!IsActive()) return; LayoutRebuilder.MarkLayoutForRebuild(rectTransform); if (m_OnDirtyLayoutCallback != null) m_OnDirtyLayoutCallback(); } public virtual void SetVerticesDirty() { if (!IsActive()) return; m_VertsDirty = true; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyVertsCallback != null) m_OnDirtyVertsCallback(); } public virtual void SetMaterialDirty() { if (!IsActive()) return; m_MaterialDirty = true; CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); if (m_OnDirtyMaterialCallback != null) m_OnDirtyMaterialCallback(); }
渲染事件的处理逻辑
- Rebuild : 在
Canvas
渲染前被调用,在这个方法里会调用UpdateMaterial
、UpdateGeometry
和更新材质和顶点。UpdateMaterial : 重设canvasRenderer的材质和纹理
protected virtual void UpdateMaterial() { if (!IsActive()) return; canvasRenderer.materialCount = 1; canvasRenderer.SetMaterial(materialForRendering, 0); canvasRenderer.SetTexture(mainTexture); }
materialForRendering : 获取修改后的材质,例如经过Mask(遮罩)处理后的材质。
public virtual Material materialForRendering { get { var components = ListPool<Component>.Get(); GetComponents(typeof(IMaterialModifier), components); var currentMat = material; for (var i = 0; i < components.Count; i++) currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat); ListPool<Component>.Release(components); return currentMat; } }
UpdateGeometry : 根据
useLegacyMeshGeneration
这个bool
值分别调用DoLegacyMeshGeneration
或DoMeshGeneration
。以
DoMeshGeneration
为例:
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); }
OnPopulateMesh
假装绘制了一个矩形Mesh
,实际上只是把顶点和三角形信息保存到了s_VertexHelper
里。然后获取IMeshModifier
类型的组件(IMeshModifier
是一个接口,需要依据顶点信息的组件继承自它,例如Shadow
就间接继承自它),调用它们的ModifyMesh
方法,修改Mesh
信息。最后将s_VertexHelper
里修改后的信息赋值给workerMesh
,并将workerMesh
设置给canvasRenderer
。
和其他组件的一些关系
CrossFadeColor
- 在 UGUI学习手记-Selectable& Button 里有提到当
Selectable
状态变化的时候会调用Graphic
的CrossFadeColor
方法。CrossFadeColor :
ColorTween
是实现缓变效果的类,而m_ColorTweenRunner
是在构造函数里创建的,是TweenRunner<ColorTween>
的实例,是ColorTween
的载体,通过协程来运行。
private void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha, bool useRGB) { if (canvasRenderer == null || (!useRGB && !useAlpha)) return; Color currentColor = canvasRenderer.GetColor(); if (currentColor.Equals(targetColor)) return; ColorTween.ColorTweenMode mode = (useRGB && useAlpha ? ColorTween.ColorTweenMode.All : (useRGB ? ColorTween.ColorTweenMode.RGB : ColorTween.ColorTweenMode.Alpha)); var colorTween = new ColorTween {duration = duration, startColor = canvasRenderer.GetColor(), targetColor = targetColor}; colorTween.AddOnChangedCallback(canvasRenderer.SetColor); colorTween.ignoreTimeScale = ignoreTimeScale; colorTween.tweenMode = mode; m_ColorTweenRunner.StartTween(colorTween); }
Image
继承了
ICanvasRaycastFilter
,通过Raycast
判断射线在该组件是否有效。这个方法在GraphicRaycaster
的Raycast
方法里被使用到(之前提到的GraphicRegistry
的GetGraphicsForCanvas
方法也被调用到了),用于筛选出被射线照射到的Graphic
。GraphicRaycaster继承自BaseRaycaster
,输入模块通过Raycast
来获取被影响的对象。具体细节查看 UGUI学习手记-EventSystem