UGUI源码分析|06 Graphic
public abstract class Graphic: UIBehaviour,ICanvasElement
抽象类,继承自UIBehaviour,实现了一个接口
这里主要看一下他的生命周期
OnEnable
protected override void OnEnable()
{
base.OnEnable();
// 从父节点中获取Canvas,并缓存
CacheCanvas();
// 把自身缓存到Graphic管理器
GraphicRegistry.RegisterGraphicForCanvas(canvas, this);
#if UNITY_EDITOR
// 编辑器方法,忽略忽略
GraphicRebuildTracker.TrackGraphic(this);
#endif
if (s_WhiteTexture == null)
s_WhiteTexture = Texture2D.whiteTexture;
SetAllDirty();
}
Graphic管理器,Grphic的生命周期中,会把自身缓存到这两个字典,其中key是Graphic所属的Canvas
public class GraphicRegistry
{
private static GraphicRegistry s_Instance;
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_RaycastableGraphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
...
}
s_WhiteTexture是一个静态Tex2D,就是公共的默认贴图,比如Image默认会是一张白图,就是它
static protected Texture2D s_WhiteTexture = null;
OnDisable
protected override void OnDisable()
{
#if UNITY_EDITOR
GraphicRebuildTracker.UnTrackGraphic(this);
#endif
// 从相关管理器中删除自身
GraphicRegistry.UnregisterGraphicForCanvas(canvas, this);
CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
if (canvasRenderer != null)
canvasRenderer.Clear();
// 标记自身的rect为重构,不需要标记graphic,因为已经disable了
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
base.OnDisable();
}
OnRectTransformDimensionsChange
当rect的大小发生变化时,如果Canvas正在重建,那么只重建顶点,否则重建顶点和布局
protected override void OnRectTransformDimensionsChange()
{
if (gameObject.activeInHierarchy)
{
if (CanvasUpdateRegistry.IsRebuildingLayout())
SetVerticesDirty();
else
{
SetVerticesDirty();
SetLayoutDirty();
}
}
}
TransformParentChanged
父节点改变,类似于先Disable再Enable
protected override void OnBeforeTransformParentChanged()
{
GraphicRegistry.UnregisterGraphicForCanvas(canvas, this);
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}
protected override void OnTransformParentChanged()
{
base.OnTransformParentChanged();
m_Canvas = null;
if (!IsActive())
return;
CacheCanvas();
GraphicRegistry.RegisterGraphicForCanvas(canvas, this);
SetAllDirty();
}
OnCanvasHierarchyChanged
当Canvas的状态发生变化时候,重新注册一下就行了
protected override void OnCanvasHierarchyChanged()
{
// Use m_Cavas so we dont auto call CacheCanvas
Canvas currentCanvas = m_Canvas;
// Clear the cached canvas. Will be fetched below if active.
m_Canvas = null;
if (!IsActive())
return;
CacheCanvas();
if (currentCanvas != m_Canvas)
{
GraphicRegistry.UnregisterGraphicForCanvas(currentCanvas, this);
// Only register if we are active and enabled as OnCanvasHierarchyChanged can get called
// during object destruction and we dont want to register ourself and then become null.
if (IsActive())
GraphicRegistry.RegisterGraphicForCanvas(canvas, this);
}
}
Rebuild
实现自接口ICanvasElement,会在CanvasUpdateRegistry的PerformUpdate中被调用
根据渲染阶段进行重构
public virtual void Rebuild(CanvasUpdate update)
{
if (canvasRenderer == null || canvasRenderer.cull)
return;
switch (update)
{
case CanvasUpdate.PreRender:
if (m_VertsDirty)
{
UpdateGeometry();
m_VertsDirty = false;
}
if (m_MaterialDirty)
{
UpdateMaterial();
m_MaterialDirty = false;
}
break;
}
}
UpdateGeometry
protected virtual void UpdateGeometry()
{
// 这个bool会在不同的类里自己赋值,比如Image会在创建时设置为false,我们就看else里的方法就行
if (useLegacyMeshGeneration)
{
DoLegacyMeshGeneration();
}
else
{
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.
// 拿到IMeshModifier接口并调用,需要修改顶点信息的组件会实现这个接口,比如Outline
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);
// 将数据暂存到Mesh里
s_VertexHelper.FillMesh(workerMesh);
// 把Mesh交给Render,下一帧渲染,这是个C++方法
canvasRenderer.SetMesh(workerMesh);
}
OnPopulateMesh
protected virtual void OnPopulateMesh(VertexHelper vh)
{
// 返回一个rect
var r = GetPixelAdjustedRect();
var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);
Color32 color32 = color;
vh.Clear();
// 添加4个顶点,就是rect的四个角,第三个参数是UV
vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f));
vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f));
vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f));
vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f));
// 添加三角形面片
vh.AddTriangle(0, 1, 2);
vh.AddTriangle(2, 3, 0);
}
UpdateMaterial
这个就比较简单了,只是把自己的材质和贴图都赋值给Render
protected virtual void UpdateMaterial()
{
if (!IsActive())
return;
canvasRenderer.materialCount = 1;
canvasRenderer.SetMaterial(materialForRendering, 0);
canvasRenderer.SetTexture(mainTexture);
}