1. 程式人生 > >NGUI原始碼分析(五) UIPanel

NGUI原始碼分析(五) UIPanel

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的層必須相同或相鄰。