天天看點

UGUI源碼解析——Graphic

一:前言

Graphic是UGUI的核心元件,負責圖像的顯示與更新,是MaskableGraphic的基類,而MaskableGraphic是Text、RawImage、Image的基類

二:源碼解析——類頭

UGUI源碼解析——Graphic

Graphic繼承自UIBehaviour和ICanvasElement

UIBehaviour是所有UGUI元件的最基類,負責接收來自UnityEngine或者UnityEditor的事件:UGUI源碼解析——UIBehaviour

ICanvasElement負責接收Canvas重新渲染的事件:UGUI源碼解析——CanvasUpdateRegistry

Graphic添加了三個特性

——DisallowMultipleComponent:不允許一個對象添加多個相同的元件

——RequireComponent:依賴于CanvasRenderer畫布渲染元件和RectTransform元件

——ExecuteAlways:編輯器模式下可以運作

另外Graphic是一個抽象類,意味着不能被執行個體化,但是它提供了很多可重寫的方法可被繼承并重寫

三:源碼解析——繼承自UIBehaviour的方法

——OnEnable

UGUI源碼解析——Graphic

首先調用CacheCanvas方法找到渲染目前元素的Canvas,之後調用GraphicRegistry.RegisterGraphicForCanvas方法将此元素注冊到m_Graphics字典中(每個Canvas對應了一個Graphic序列),接着設定s_WhiteTexture(mainTexture的預設貼圖),最後調用SetAllDirty将元素添加到布局和圖像重建序列中(這樣說有些不嚴謹,因為布局重建是通過LayoutRebuilder類管理的,如果父物體身上沒有繼承ILayoutGroup的元件是不會添加到布局重建序列中的),SetAllDirty下面會重點說

——OnDisable

UGUI源碼解析——Graphic

首先從GraphicRegistry和CanvasUpdateRegistry中将目前元素移除注冊并清理canvasRenderer,最後通過LayoutRebuilder.MarkLayoutForRebuild方法将元素添加到布局重建序列中(因為Graphic已經Disable是以不需要将元素添加到圖像重建序列中)

——OnRectTransformDimensionsChange

UGUI源碼解析——Graphic

将元素添加到圖像和布局重建序列中,如果正在進行布局重建則隻将元素添加到圖像重建序列中

——OnBeforeTransformParentChanged

UGUI源碼解析——Graphic

首先将渲染目前元素的canvas從GraphicRegistry移除注冊,并将元素添加到布局重建序列中

——OnTransformParentChanged

UGUI源碼解析——Graphic

首先調用CacheCanvas方法找到渲染目前元素的Canvas,之後調用GraphicRegistry.RegisterGraphicForCanvas方法将此元素注冊到m_Graphics字典中(每個Canvas對應了一個Graphic序列),最後使用SetAllDirty将元素添加到圖像和布局重建序列中

——OnCanvasHierarchyChanged

UGUI源碼解析——Graphic

首先調用CacheCanvas方法找到渲染目前元素的Canvas,如果此canvas與緩存的canvas不同則移除之前注冊的canvas并調用GraphicRegistry.RegisterGraphicForCanvas方法重新将此元素注冊到m_Graphics字典中(每個Canvas對應了一個Graphic序列)

四:源碼解析——繼承自ICanvasElement的方法

——Rebuild

UGUI源碼解析——Graphic

在CanvasUpdateRegistry類中給委托Canvas.willRenderCanvases注冊了PerformUpdate方法,PerformUpdate會在CanvasRender渲染之前會周遊布局和圖像重建序列調用每個元素的Rebuild方法,在Rebuild方法裡調用UpdateGeometry和UpdateMaterial更新網格和材質,布局不在Graphic類中管理而是單獨通過LayoutRebuilder去管理的​​​​​

UGUI源碼解析——Graphic

UpdateGeometry方法中首先調用了OnPopulateMesh方法将元素的頂點、顔色、UV等資訊暫存到m_VertexHelper中,Graphic類中的OnPopulateMesh繪制了一個矩形,我們可以在自己的UI類中,重寫OnPopulateMesh方法,實作自定義的UI

接着周遊身上繼承了IMeshModifier的元件(一般是實作網格的一些特效,例如Shadow、Outline),更新VertexHelper資料

然後調用s_VertexHelper.FillMesh(workerMesh)将暫存的資料填入workMesh中

最後調用canvasRenderer.SetMesh(workerMesh)将workerMesh指派給canvasRenderer,這樣下一幀canvasRenderer渲染時就會用workerMesh的資料進行繪制了(SetMesh方法最終在C++中實作,這也是UGUI的效率比NGUI高一些的原因,因為NGUI的Mesh合并是在C#中完成)

UI是如何繪制出來的?

首先要生成顯示UI用的Mesh,例如一個矩形的Mesh,由4個頂點,2個三角形組成,每個頂點都包含UV坐标、頂點色等資訊,調節Image或者Text的顔色,其實就是改變它們的頂點色

然後将網格和紋理資訊發送給CanvasRender,CanvasRender再将渲染指令發送給GPU進行渲染,這樣一個簡單的UI元素就顯示出來了,這個過程叫做一次批處理

其實這個流程與渲染一個普通的Cube是類似的

可以簡單的了解為,所謂的UI其實就是用一個正交的Camera看着若幹的平面網格,隻不過單單的顯示出來還遠遠不夠,還有一些其他操作,比如DrawCall需要合并,Button需要點選等等

UGUI源碼解析——Graphic

UpdateMaterial方法中首先将自身材質指派給canvasRenderer(周遊身上繼承了IMaterialModifier的元件(例如Mask),然後将貼圖指派給canvasRenderer,這樣下一幀canvasRenderer渲染時就會用materialForRendering和mainTexture的資料進行繪制了

五:SetDirty

UGUI源碼解析——Graphic

六:UGUI優化技巧

繼續閱讀