NGUI原始碼分析(五) UIPanel
阿新 • • 發佈:2019-01-26
static public List<UIPanel> list = new List<UIPanel>(); //儲存所有的panel
public RenderQueue renderQueue = RenderQueue.Automatic;//渲染次序型別
public int startingRenderQueue = 3000;//渲染順序值得
public List<UIWidget> widgets = new List<UIWidget>(); //儲存當前panel的所有widget
public List<UIDrawCall> drawCalls = new List<UIDrawCall>();//當前panel所有的drawCall
int mDepth = 0;//深度
int mSortingOrder;//佇列排序值
bool mRebuild = false;//重要屬性 如果為true需要重構所有的DrawCall. Panel中的OnEnable,RemoveWidget,AddWidget等這幾個方法和改變Widget深度會設定mRebuild為true
二.重要方法:
void LateUpdate () //核心方法 更新所有Panel和DrawCall
//每幀只執行一次
if (mUpdateFrame != Time.frameCount)
{
mUpdateFrame = Time.frameCount;
// 按順序更新每一個Panel
for (int i = 0, imax = list.Count; i < imax; ++i)
list[i].UpdateSelf();
}
//初始渲染次序
int rq = 3000;
//更新所有DrawCall
for (int i = 0, imax = list.Count; i < imax; ++i)
{
UIPanel p = list[i];
//如果渲染次序設定為Automatic 則渲染順序是從底層開始往上
if (p.renderQueue == RenderQueue.Automatic)
{
p.startingRenderQueue = rq;
//
p.UpdateDrawCalls();
rq += p.drawCalls.Count;
}
//如果渲染次序設定為StartAt 則渲染順序值為預設值3000 下一個從3000+ p.drawCalls.Count 開始
else if (p.renderQueue == RenderQueue.StartAt)
{
p.UpdateDrawCalls();
if (p.drawCalls.Count != 0)
rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count);
}
else //如果渲染次序設定為Explicit 下一個從startingRenderQueue + 1開始
{
p.UpdateDrawCalls();
if (p.drawCalls.Count != 0)
rq = Mathf.Max(rq, p.startingRenderQueue + 1);
}
}
void UpdateSelf () 方法:
//更新矩陣變化
UpdateTransformMatrix();
//更新所有層
UpdateLayers();
//更新所有Widget 負責計算
UpdateWidgets();
//是否重建
if (mRebuild)
{
mRebuild = false;
//重建所有的DrawCall
FillAllDrawCalls();
}
else
{
for (int i = 0; i < drawCalls.Count; )
{
UIDrawCall dc = drawCalls[i];
//如果DrawCall被標記為髒的 或者沒有找到任何Widget使用這個DrawCall,就銷燬這個DrawCall
if (dc.isDirty && !FillDrawCall(dc))
{
UIDrawCall.Destroy(dc);
drawCalls.RemoveAt(i);
continue;
}
++i;
}
UpdateLayers () 更新層
// 判斷當前的層 是否跟GameObject的層一致 (可以在Inspector裡設定)
// 如果不一致則設定到GameObject所在的層 Panel下所有的Widget也設定到該層
if (mLayer != cachedGameObject.layer)
{
mLayer = mGo.layer;
UICamera uic = UICamera.FindCameraForLayer(mLayer);
mCam = (uic != null) ? uic.cachedCamera : NGUITools.FindCameraForLayer(mLayer);
NGUITools.SetChildLayer(cachedTransform, mLayer);
for (int i = 0; i < drawCalls.Count; ++i)
drawCalls[i].gameObject.layer = mLayer;
}
UpdateWidgets() 更新所有屬於這個Panel的Widget
//遍歷Widget
for (int i = 0, imax = widgets.Count; i < imax; ++i)
{
UIWidget w = widgets[i];
.......中間省略
//更新Widget的頂點資訊 在這篇NGUI原始碼分析(二) UIWidget文章提到過UpdateGeometry這個方法
if (w.UpdateGeometry(frame))
{
changed = true;
if (!mRebuild)
{
if (w.drawCall != null)
{
//如果一個Widget發生改變 這個drawCall就會設定為髒 會導致FillDrawCall重新呼叫WriteToBuffers和dc.UpdateGeometry()
w.drawCall.isDirty = true;
}
else
{
//查詢材質和貼圖一樣的DrawCall
FindDrawCall(w);
}
}
}
FillAllDrawCalls()方法 重建所有的DrawCall,步驟:先清空所有的DrawCall 再建立所有Widget的DrawCall, 將Widget頂點填充到DrawCall,執行DrawCall繪製
Material,Texture,Shader都相同而且Widget的層次相同或相鄰的話DrawCall會合並。程式碼太長這裡就不貼出來了。
總結:新增和刪除Widget可能會導致所有DrawCall重構,改變Widget的深度會導致所有DrawCall重構,建議不要過於頻繁新增,刪除Widget和修改Widget的層次.一個Widget發生改變會導致其他使用相同DrawCall的Widget重新填充頂點到DrawCall,並繪製一次該DrawCall。DrawCall合併規則有兩條,一是Material,Texture,Shader都相同,二是Widget的層必須相同或相鄰。