UIPanel可以看成一个集合,一个Panel可以包含多个Widget.负责排序和更新widget,同时控制widget在什么时候绘制图形。下面具体看看是如何实现的.
一.重要属性:
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的层必须相同或相邻。
原文地址:http://www.cnblogs.com/rocky300/articles/4681275.html