UGUI学习手记-Graphic

关于源码

关于源码可以参考这篇博客下载或调试。

Graphic

简介

  • 继承自UIBehaviour(所有UI组件的基类,负责接收来自UnityEngine或者UnityEditor的事件)

  • 继承 ICanvasElement (负责接收Canvas重新渲染的事件)

  • UGUI的核心组件,负责显示图像

  • 抽象类

  • MaskableGraphic继承自该类,MaskableGraphic 是可遮罩图像(RawImageImageText的基类)的基类。

  • 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设置LayoutVerticesMaterialDirty)。

  • OnDisable : 从GraphicRegistryCanvasUpdateRegistry 两个管理中心中注销,并清理CanvasRenderer,完成后通知LayoutRebuilder重建布局。

  • OnRectTransformDimensionsChange : 当RectTransform维度改变时,将LayoutVertices设为Dirty

  • OnBeforeTransformParentChanged : 在父对象改变前,从GraphicRegistry注销,并通知LayoutRebuilder重建布局。

  • OnTransformParentChanged : 当父对象改变时,重新获取父对象中的Canvas,重新注册到GraphicRegistry,并SetAllDirty

  • OnDidApplyAnimationProperties : 当应用动画属性后,调用SetAllDirty

  • OnCanvasHierarchyChanged : 当画布层次改变时,如果Canvas改变,重新注册到GraphicRegistry

SetAllDirty 细节代码

SetLayoutDirty里会通知LayoutRebuilder布局需要重建。SetVerticesDirtySetMaterialDirty里会注册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渲染前被调用,在这个方法里会调用UpdateMaterialUpdateGeometry和更新材质和顶点。

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.Get();  
       GetComponents(typeof(IMaterialModifier), components);  
       var currentMat = material;  
       for (var i = 0; i < components.Count; i++)  
           currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);  
       ListPool.Release(components);  
       return currentMat;  
   }  
}  

UpdateGeometry : 根据useLegacyMeshGeneration这个bool值分别调用DoLegacyMeshGenerationDoMeshGeneration

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.Get();  
   GetComponents(typeof(IMeshModifier), components);  
   for (var i = 0; i < components.Count; i++)  
       ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);  
   ListPool.Release(components);  
   s_VertexHelper.FillMesh(workerMesh);  
   canvasRenderer.SetMesh(workerMesh);  
}  

OnPopulateMesh假装绘制了一个矩形Mesh,实际上只是把顶点和三角形信息保存到了s_VertexHelper里。然后获取IMeshModifier类型的组件(IMeshModifier是一个接口,需要依据顶点信息的组件继承自它,例如Shadow就间接继承自它),调用它们的ModifyMesh方法,修改Mesh信息。最后将s_VertexHelper里修改后的信息赋值给workerMesh,并将workerMesh设置给canvasRenderer

和其他组件的一些关系

CrossFadeColor

  1. 在 UGUI学习手记-Selectable& Button 里有提到当Selectable状态变化的时候会调用GraphicCrossFadeColor方法。

CrossFadeColor : ColorTween是实现缓变效果的类,而m_ColorTweenRunner是在构造函数里创建的,是TweenRunner的实例,是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判断射线在该组件是否有效。这个方法在GraphicRaycasterRaycast方法里被使用到(之前提到的GraphicRegistryGetGraphicsForCanvas方法也被调用到了),用于筛选出被射线照射到的Graphic。GraphicRaycaster继承自BaseRaycaster,输入模块通过Raycast来获取被影响的对象。

具体细节查看 UGUI学习手记-EventSystem

你可能感兴趣的:(Unity_UI,ui,canvas,unity,UGUI)