uGUI之graphic
所有需要绘制的ui组件的抽象基类
Graphic
继承自UIBehaviour、
ICanvesElement,可渲染的ui组件ICanvasElement
依赖Canves 的ui元素接口, 主要是
rebuild接口标记/// <summary> /// Values of 'update' called on a Canvas update. /// </summary> public enum CanvasUpdate { /// <summary> /// Called before layout. /// </summary> Prelayout = 0, /// <summary> /// Called for layout. /// </summary> Layout = 1, /// <summary> /// Called after layout. /// </summary> PostLayout = 2, /// <summary> /// Called before rendering. /// </summary> PreRender = 3, /// <summary> /// Called late, before render. /// </summary> LatePreRender = 4, /// <summary> /// Max enum value. Always last. /// </summary> MaxUpdateValue = 5 } /// <summary> /// This is an element that can live on a Canvas. /// </summary> public interface ICanvasElement { /// <summary> /// Rebuild the element for the given stage. /// </summary> /// <param name="executing">The current CanvasUpdate stage being rebuild.</param> void Rebuild(CanvasUpdate executing); /// <summary> /// Get the transform associated with the ICanvasElement. /// </summary> Transform transform { get; } /// <summary> /// Callback sent when this ICanvasElement has completed layout. /// </summary> void LayoutComplete(); /// <summary> /// Callback sent when this ICanvasElement has completed Graphic rebuild. /// </summary> void GraphicUpdateComplete(); /// <summary> /// Used if the native representation has been destroyed. /// </summary> /// <returns>Return true if the element is considered destroyed.</returns> bool IsDestroyed(); }ICanvesElement 接口实现
Rebuild
> canvasRenderer ui渲染元素依赖组件的**render**组件 > > 这里只对**渲染前**进行`顶点`和`材质`进行更新。 ```csharp 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; } } ```- transform
transform 沿用基类 `Component` 的transform 属性- LayoutComponent
```csharp //暂时为空 public virtual void LayoutComplete() {} ```GraphicUpdateComplete
//暂时为空 public virtual void GraphicUpdateComplete() {}IsDestroyed
IsDestroyed 沿用 UIBehaviour 的IsDestroyed 属性
新增和实现
作为需要渲染的控件, 必须有
Render、渲染的数据顶点数据、材质数据理论部分
Render 使用
CanvasRenderer材质以及材质属性
代码部分
- 材质以及材质属性静态公共数据 > s_WhiteTexture静态默认贴图属性作为共享的默认贴图,OnEnable 进行赋值
static protected Texture2D s_WhiteTexture = null; // 贴图赋值 protected override void OnEnable() { // 。。。 if (s_WhiteTexture == null) s_WhiteTexture = Texture2D.whiteTexture; // 。。。 }s_DefaultUI静态默认材质属性作为共享的默认贴图,Get进行单例赋值
static public Material defaultGraphicMaterial { get { if (s_DefaultUI == null) s_DefaultUI = Canvas.GetDefaultCanvasMaterial(); return s_DefaultUI; } }s_VertexHelper 顶点辅助器
[NonSerialized] private static readonly VertexHelper s_VertexHelper = new VertexHelper();渲染控制相关
材质以及材质属性
```csharp //材质对象 [FormerlySerializedAs("m_Mat")] [SerializeField] protected Material m_Material; //材质属性 颜色 [SerializeField] private Color m_Color = Color.white; ```顶点、Mesh (辅助顶点mesh类
VertexHelper)Rebuild调用
UpdateGeometry进行顶点、Mesh 计算,顶点效果IMeshModifier回调触发在
OnPopulateMesh中进行顶点属性处理,简单的顶点 和includies, 以及 uvpublic virtual void Rebuild(CanvasUpdate update) { ///... UpdateGeometry(); m_VertsDirty = false; ///... } } /// <summary> /// Call to update the geometry of the Graphic onto the CanvasRenderer. /// </summary> 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); } private void DoLegacyMeshGeneration() { if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0) { #pragma warning disable 618 OnPopulateMesh(workerMesh); #pragma warning restore 618 } else { workerMesh.Clear(); } var components = ListPool<Component>.Get(); GetComponents(typeof(IMeshModifier), components); for (var i = 0; i < components.Count; i++) { #pragma warning disable 618 ((IMeshModifier)components[i]).ModifyMesh(workerMesh); #pragma warning restore 618 } ListPool<Component>.Release(components); canvasRenderer.SetMesh(workerMesh); } // 提供虚函数,改变顶点属性 protected virtual void OnPopulateMesh(VertexHelper vh) { var r = GetPixelAdjustedRect(); var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height); Color32 color32 = color; vh.Clear(); 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); }设置Render的贴图和材质属性
protected virtual void UpdateMaterial() { if (!IsActive()) return; canvasRenderer.materialCount = 1; canvasRenderer.SetMaterial(materialForRendering, 0); canvasRenderer.SetTexture(mainTexture); }Rebuild
1. GraphicRegistry 按顺序注册缓存 > 储存渲染元素, 以`Canvas`为Key,IndexedSet<Graphic> 为value 的字典 > > 这个Canvas 是父节点遍历的第一个Canvas ```csharp private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>(); ``` 1. 注册 GraphicRegistry.RegisterGraphicForCanvas, 静态方法内部处理使用 内部静态对象 s_Instance > 按顺序添加到 Canvas 对应的Graphic 列表中 1. void RegisterGraphicForCanvas(Canvas c, Graphic graphic) 2. GetGraphicsForCanvas(Canvas c) > 或者对应的Canvas 的Graphic 的元素列表 2. CanvasUpdateRegistry > 分为两大类 : > > 1. IndexedSet<ICanvasElement> m_LayoutRebuildQueue > > 2. IndexedSet<ICanvasElement> m_GraphicRebuildQueue > > 主要提供注册和移除接口, 内部处理使用 Canvas.willRenderCanvases += PerformUpdate; 回调处理 > > ~~,因此流程时序很重要~~。 1. RegisterCanvasElementForGraphicRebuild 2. RegisterCanvasElementForLayoutRebuild 3. UnRegisterCanvasElementForRebuild, 移除layout和graphic队列 4. PerformUpdate > 主要步骤如下: ```csharp public enum CanvasUpdate { /// <summary> /// Called before layout. /// </summary> Prelayout = 0, /// <summary> /// Called for layout. /// </summary> Layout = 1, /// <summary> /// Called after layout. /// </summary> PostLayout = 2, /// <summary> /// Called before rendering. /// </summary> PreRender = 3, /// <summary> /// Called late, before render. /// </summary> LatePreRender = 4, /// <summary> /// Max enum value. Always last. /// </summary> MaxUpdateValue = 5 } ``` 验证非null的UnityObject ```csharp private bool ObjectValidForUpdate(ICanvasElement element) { var valid = element != null; var isUnityObject = element is Object; if (isUnityObject) valid = (element as Object) != null; //Here we make use of the overloaded UnityEngine.Object == null, that checks if the native object is alive. return valid; } ``` 1. CleanInvalidItems 移除量大队列的null 元素和标记为销毁的元素, **标记销毁是因为引擎设计销毁操作在下一帧的缘故** 2. layout队列 根据transform层级排序(父节点数作为的层级)。进行两次迭代验证非null的UnityObject并 执行 Rebuild回调的 Prelayout, 和Rebuild回调的 Layout. 最后遍历回调 LayoutComplete。 清理Layout 临时引用。 ```csharp m_PerformingLayoutUpdate = true; m_LayoutRebuildQueue.Sort(s_SortLayoutFunction); ``` ```csharp private static int ParentCount(Transform child) { if (child == null) return 0; var parent = child.parent; int count = 0; while (parent != null) { count++; parent = parent.parent; } return count; } ``` ```csharp private bool ObjectValidForUpdate(ICanvasElement element) { var valid = element != null; var isUnityObject = element is Object; if (isUnityObject) valid = (element as Object) != null; //Here we make use of the overloaded UnityEngine.Object == null, that checks if the native object is alive. return valid; } ``` ```csharp m_LayoutRebuildQueue.Sort(s_SortLayoutFunction); for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++) { for (int j = 0; j < m_LayoutRebuildQueue.Count; j++) { var rebuild = instance.m_LayoutRebuildQueue[j]; try { if (ObjectValidForUpdate(rebuild)) rebuild.Rebuild((CanvasUpdate)i); } catch (Exception e) { Debug.LogException(e, rebuild.transform); } } } ``` ```csharp for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i) m_LayoutRebuildQueue[i].LayoutComplete(); ``` ```csharp instance.m_LayoutRebuildQueue.Clear(); m_PerformingLayoutUpdate = false; ``` 3. 执行 Cull ```csharp // now layout is complete do culling... ClipperRegistry.instance.Cull(); ``` 4. Graphic队列 ,进行两次迭代验证非null的UnityObject并 执行 Rebuild回调的 PreRender, 和Rebuild回调的 LatePreRender. 最后遍历回调 GraphicUpdateComplete。 清理Graphic临时引用。 ```csharp m_PerformingGraphicUpdate = true; for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++) { for (var k = 0; k < instance.m_GraphicRebuildQueue.Count; k++) { try { var element = instance.m_GraphicRebuildQueue[k]; if (ObjectValidForUpdate(element)) element.Rebuild((CanvasUpdate)i); } catch (Exception e) { Debug.LogException(e, instance.m_GraphicRebuildQueue[k].transform); } } } ``` ```csharp for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i) m_GraphicRebuildQueue[i].GraphicUpdateComplete(); ``` ```csharp instance.m_GraphicRebuildQueue.Clear(); m_PerformingGraphicUpdate = false; ``` 3. LayoutRebuilder, Layout集中的 Rebuild处理 > CanvasElement 包装器,专门处理Layout的rebuild, > > 主要接口 `MarkLayoutForRebuild`, 这个包装只处理包含并激活ILayoutGroup的RectTransform > > 最后调用 `TryRegisterCanvasElementForLayoutRebuild` 处理 1. 被触发的方法 ```csharp protected override void OnBeforeTransformParentChanged() { GraphicRegistry.UnregisterGraphicForCanvas(canvas, this); LayoutRebuilder.MarkLayoutForRebuild(rectTransform); } ``` ```csharp public virtual void SetLayoutDirty() { if (!IsActive()) return; LayoutRebuilder.MarkLayoutForRebuild(rectTransform); if (m_OnDirtyLayoutCallback != null) m_OnDirtyLayoutCallback(); } ``` ```csharp protected override void OnDisable() { //。。。 LayoutRebuilder.MarkLayoutForRebuild(rectTransform); //。。。 } ``` 2. 注册的回调事件 ```csharp static LayoutRebuilder() { RectTransform.reapplyDrivenProperties += ReapplyDrivenProperties; } //... static void ReapplyDrivenProperties(RectTransform driven) { MarkLayoutForRebuild(driven); } ``` 3. MarkLayoutForRebuild 实现细节 ```csharp public static void MarkLayoutForRebuild(RectTransform rect) { if (rect == null || rect.gameObject == null) return; var comps = ListPool<Component>.Get(); bool validLayoutGroup = true; RectTransform layoutRoot = rect; var parent = layoutRoot.parent as RectTransform; //如果父节点含有 ILayoutGroup, 则 layoutRoot = parent, //parent = parent'parent while (validLayoutGroup && !(parent == null || parent.gameObject == null)) { validLayoutGroup = false; parent.GetComponents(typeof(ILayoutGroup), comps); for (int i = 0; i < comps.Count; ++i) { var cur = comps[i]; if (cur != null && cur is Behaviour && ((Behaviour)cur).isActiveAndEnabled) { validLayoutGroup = true; layoutRoot = parent; break; } } parent = parent.parent as RectTransform; } // We know the layout root is valid if it's not the same as the rect, // since we checked that above. But if they're the same we still need to check. //1. 不含有 ILayoutGroup组件的情况, 或者ILayoutGroup没有激活的情况 if (layoutRoot == rect && !ValidController(layoutRoot, comps)) { ListPool<Component>.Release(comps); return; } //2. 包含 ILayoutGroup且控件激活 MarkLayoutRootForRebuild(layoutRoot); ListPool<Component>.Release(comps); } ``` ```csharp private static void MarkLayoutRootForRebuild(RectTransform controller) { if (controller == null) return; var rebuilder = s_Rebuilders.Get(); rebuilder.Initialize(controller); if (!CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder)) s_Rebuilders.Release(rebuilder); } ``` 4. 设置回调, 对应设置徐然数据改变最后的回调方法 1. RegisterDirtyLayoutCallback 2. UnregisterDirtyLayoutCallback 3. RegisterDirtyVerticesCallback 4. UnregisterDirtyVerticesCallback 5. RegisterDirtyMaterialCallback 6. UnregisterDirtyMaterialCallback 4. ~~GraphicRebuildTracker.TrackGraphic~~(this);- 物理以及事件
Raycast、
m_RaycastTarget,启动input事件检测
Raycast 方法,用于事件监测,
Canvas ,eventsystempublic virtual bool Raycast(Vector2 sp, Camera eventCamera) { if (!isActiveAndEnabled) return false; var t = transform; var components = ListPool<Component>.Get(); bool ignoreParentGroups = false; bool continueTraversal = true; while (t != null) { t.GetComponents(components); for (var i = 0; i < components.Count; i++) { var canvas = components[i] as Canvas; if (canvas != null && canvas.overrideSorting) continueTraversal = false; var filter = components[i] as ICanvasRaycastFilter; if (filter == null) continue; var raycastValid = true; var group = components[i] as CanvasGroup; if (group != null) { if (ignoreParentGroups == false && group.ignoreParentGroups) { ignoreParentGroups = true; raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); } else if (!ignoreParentGroups) raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); } else { raycastValid = filter.IsRaycastLocationValid(sp, eventCamera); } if (!raycastValid) { ListPool<Component>.Release(components); return false; } } t = continueTraversal ? t.parent : null; } ListPool<Component>.Release(components); return true; }基础Tween动画
- CrossFadeColor、
- CrossFadeColor、
- CreateColorFromAlpha、
- CrossFadeAlpha
相对坐标像素计算
- PixelAdjustPoint、
- GetPixelAdjustedRect