UIGU源码分析12:Graphic

源码12:Graphic

Grahic是UGUI的核心组件,负责显示图像,继承了UIBehaviour,ICanvasElement。它是一个抽象类,是MaskableGraphic(可遮罩图像)的基类,而后者是RawImage,Image和Text的基类

GraphicRegistry

GraphicRegistry的作用主要是记录每个Canvas画布上激活的Graphic。

public class GraphicRegistry
{
	private static GraphicRegistry s_Instance;
	
	//每个Canvas对应一个IndexedSet,这个数据结构能够保证元素唯一且有序。
	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>>();
    
    
    protected GraphicRegistry()
    {
    // Avoid runtime generation of these types. Some platforms are AOT only and do not support
    // JIT. What's more we actually create a instance of the required types instead of
    // just declaring an unused variable which may be optimized away by some compilers (Mono vs MS).

// See: 877060

		System.GC.KeepAlive(new Dictionary<Graphic, int>());
		System.GC.KeepAlive(new Dictionary<ICanvasElement, int>());
		System.GC.KeepAlive(new Dictionary<IClipper, int>());
}
        
    ...
}

注册移除和移除对应Canvas的 IndexedSet结构上的Graphic

public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic)
{
	...
}

public static void UnregisterGraphicForCanvas(Canvas c, Graphic graphic)
{
	...
}

获取Canvas对应的所有Graphic元素

public static IList<Graphic> GetGraphicsForCanvas(Canvas canvas)
{
    IndexedSet<Graphic> graphics;
    if (instance.m_Graphics.TryGetValue(canvas, out graphics))
        return graphics;

    return s_EmptyList;
}

Graphic

public abstract class Graphic
    : UIBehaviour,
      ICanvasElement
{
		[FormerlySerializedAs("m_Mat")]
        [SerializeField] protected Material m_Material;

        [SerializeField] private Color m_Color = Color.white;

        [NonSerialized] protected bool m_SkipLayoutUpdate;
        [NonSerialized] protected bool m_SkipMaterialUpdate;
         [SerializeField]
        private Vector4 m_RaycastPadding = new Vector4();
        
        [NonSerialized] private RectTransform m_RectTransform;
        [NonSerialized] private CanvasRenderer m_CanvasRenderer;
        [NonSerialized] private Canvas m_Canvas;

        [NonSerialized] private bool m_VertsDirty;
        [NonSerialized] private bool m_MaterialDirty;

        [NonSerialized] protected UnityAction m_OnDirtyLayoutCallback;
        [NonSerialized] protected UnityAction m_OnDirtyVertsCallback;
        [NonSerialized] protected UnityAction m_OnDirtyMaterialCallback;

        [NonSerialized] protected static Mesh s_Mesh;
        [NonSerialized] private static readonly VertexHelper s_VertexHelper = new VertexHelper();

        [NonSerialized] protected Mesh m_CachedMesh;
        [NonSerialized] protected Vector2[] m_CachedUvs;
        
        ...
	
}

Graphic 继承UIBehaviour 和 ICanvasElement。前面将CanvasUpdateRegistry提过 ,任何需要在Canvas上生效的组件都要实现ICanvasElement接口

每个Graphic都会对应一个CanvasRenderer,在往一个物体添加Graphic组件的 时候,CanvasRenderer也会被添加到物体上。CanvasRenderer将负责Graphic的渲染

当Graphic 受到布局影响 ,显示内容变化 材质变化等 相关内容会标记为Dirty后进行重建

//标记布局重建 注册到LayoutRebuilder中 等待Rebuilt (注册到LayoutRebuilder中 后面讲)
public virtual void SetLayoutDirty()
{
    if (!IsActive())
    	return;

    LayoutRebuilder.MarkLayoutForRebuild(rectTransform);

    if (m_OnDirtyLayoutCallback != null)
    	m_OnDirtyLayoutCallback();
}
        
//标记顶点Dirty  注册到CanvasUpdateRegistry  中等待Rebuilt  
public virtual void SetVerticesDirty()
{
	if (!IsActive())
        return;

	m_VertsDirty = true;
	CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);

	if (m_OnDirtyVertsCallback != null)
	m_OnDirtyVertsCallback();
}
    

//标记材质Dirty  注册到CanvasUpdateRegistry  中等待Rebuilt  
public virtual void SetMaterialDirty()
{
	if (!IsActive())
        return;

	m_MaterialDirty = true;
	CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);

	if (m_OnDirtyMaterialCallback != null)
	m_OnDirtyMaterialCallback();
}    

ICanvasElement的Rebuild过程

    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()
        {
        	//此情况子类基本不存在就不分析
            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.

            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 把顶点等相关数据保存到s_VertexHelper 中
  • 获取对应的IMeshModifier 类型组件 调用ModifyMesh 方法 修改Mesh信息
  • 将修改的填充到workerMesh中
  • 最后将workerMesh应用到canvasRenderer中

UpdateMaterial 更新材质

  protected virtual void UpdateMaterial()
    {
        if (!IsActive())
            return;

        canvasRenderer.materialCount = 1;
        canvasRenderer.SetMaterial(materialForRendering, 0);
        canvasRenderer.SetTexture(mainTexture);
    }
    
    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;
        }
	}

在给canvasRenderer设置材质时,会遍历所有IMaterialModifier类型的组件,调用IMaterialModifier.GetModifiedMaterial方法,用于重建图像时,获取修改后的Material,来实现遮罩效果


补充

OnEnable方法

调用了CacheCanvas方法,从父节点中获取Canvas列表,取得第一个激活并且可用的Canvas赋值给m_Canvas,把m_Canvas注册到GraphicRegistry中(一个注册处,可以从m_Graphics获取指定Canvas所包含的Graphic,如果m_Graphics不包含,则把当前Graphic和Canvas注册添加到m_Graphics中),接着设置s_WhiteTexture(对应属性MainTexture,用来绘制图形的纹理),最后SetAllDirty(分别设置Layout布局、Vertices顶点和Material材质为Dirty)。

OnDisable方法

从GraphicRegistry和CanvasUpdateRegistry解除注册,并清空canvasRenderer,通知LayoutRebuilder重建Layout。

OnRectTransformDimensionsChange方法(当RectTransform尺寸发生变化时),如果在重建中,只设置SetVerticesDirty,否则调用SetVerticesDirty和SetLayoutDirty。

OnBeforeTransformParentChanged方法,从GraphicRegistry解除注册,并通知LayoutRebuilder重建Layout。

OnTransformParentChanged方法,设置m_Canvas为空,调用CacheCanvas方法。从父节点中获取Canvas列表,取得第一个激活并且可用的Canvas赋值给m_Canvas,把m_Canvas注册到GraphicRegistry中,最后SetAllDirty(分别设置Layout布局、Vertices顶点和Material材质为Dirty)。

OnDidApplyAnimationProperties方法,调用SetAllDirty(分别设置Layout布局、Vertices顶点和Material材质为Dirty)。

OnCanvasHierarchyChanged方法,当父Canvas的状态发生改变时调用(当父Canvas被启用,禁用或嵌套Canvas的OverrideSorting被改变时,该函数被调用),把m_Canvas设置到currentCanvas,设置m_Canvas为空,调用CacheCanvas方法。从父节点中获取Canvas列表,取得第一个激活并且可用的Canvas赋值给m_Canvas,如果currentCanvas与m_Canvas不同,从GraphicRegistry解除注册,如果Graphic是激活并且可用时,重新注册到GraphicRegistry。

Raycast方法

通过ICanvasRaycastFilter(Image继承了这个接口)的IsRaycastLocationValid方法,判断射线在该组件是否有效。这个方法在GraphicRaycaster的Raycast方法里也被使用到,用于筛选出被射线 照射到的Graphic。GraphicRaycaster继承自BaseRaycaster,输入模块通过Raycast来获取被影响的对象。

猜你喜欢

转载自blog.csdn.net/NippyLi/article/details/123468201