一.使用NGUI
NGUI和UGUI比起来我更喜欢NGUI,因为NGUI可将图片打成图集,而且它和texturepacker配合使用会十分的方便,高效。texturepacker导出一张合图和一个文本,使用NGUI创建一个Atlas,拖拽上去即可。
二.NGUI性能提升。
首先来一段源码:这是UIPanel里面的代码。
void FillAllDrawCalls ()
{
for (int i = 0; i < drawCalls.Count; ++i)
UIDrawCall.Destroy(drawCalls[i]);
drawCalls.Clear();
Material mat = null;
Texture tex = null;
Shader sdr = null;
UIDrawCall dc = null;
int count = 0;
if (mSortWidgets) SortWidgets();
for (int i = 0; i < widgets.Count; ++i)
{
UIWidget w = widgets[i];
if (w.isVisible && w.hasVertices)//遍历所有的widget,如果可见就获取该widget的材质、图片、shader
{
Material mt = w.material;
Texture tx = w.mainTexture;
Shader sd = w.shader;
if (mat != mt || tex != tx || sdr != sd)//如果当前的这三个属性和上次循环有一个不一样,说明是一个新的
{//Atlas,并将上次新建的DrawCall对象添加到链表里面。
if (dc != null && dc.verts.size != 0)
{
drawCalls.Add(dc);
dc.UpdateGeometry(count);//更新刚刚添加的那个drawcall信息
dc.onRender = mOnRender;//注册渲染回调
mOnRender = null;
count = 0;
dc = null;//将dc赋值为空,很关键。
}
mat = mt;//给当前drawcall做标记,好做下一次循环判断
tex = tx;
sdr = sd;
}
if (mat != null || sdr != null || tex != null)
{
if (dc == null)
{
dc = UIDrawCall.Create(this, mat, tex, sdr);//如果dc为空,说明遇到新的图集,需新建一个drawcall
dc.depthStart = w.depth;
dc.depthEnd = dc.depthStart;
dc.panel = this;
}
else
{
int rd = w.depth;//如果dc不为空,那就将他和上次循环那个共享一个
if (rd < dc.depthStart) dc.depthStart = rd;//DrawCall,不需要新建。
if (rd > dc.depthEnd) dc.depthEnd = rd;
}
w.drawCall = dc;
++count;
if (generateNormals) w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, dc.norms, dc.tans);//将信息写入缓冲区
else w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, null, null);
if (w.mOnRender != null)
{
if (mOnRender == null) mOnRender = w.mOnRender;
else mOnRender += w.mOnRender;
}
}
}
else w.drawCall = null;
}
if (dc != null && dc.verts.size != 0)
{
drawCalls.Add(dc);//将最后一个drawcall添加到渲染列表。因为如果最后一个和上一个属于同一个图集,不会进入
dc.UpdateGeometry(count);//下一个循环,也就不会将最后一个drawcall添加到渲染列表。
dc.onRender = mOnRender;
mOnRender = null;
}
}
1.通过阅读代码,我们发现NGUI的渲染算法是这样的。
比如我们的ui界面(6个Sprite)由下面几个图集组成的界面:(depth从小到大)
Atlas1/Atlas2/Atlas2/Atlas3/Atlas2/Atlas1
2.按照上面的算法,NGUI会生成多少个DrawCall对象呢(将UIDrawCall类里面的#define SHOW_HIDDEN_OBJECTS注释取消可以看到有多少个)?首先Atlas1是一个,到Atlas2时,由于两个图集使用的图片不一样,材质也不一样,因此NGUI会新建一个DrawCall对象,到了第三个,由于上一个也是Atlas2,就不需要新建一个DrawCall对象。因此就少一个Drawcall,这样下来,一共可以生成5个Drawcall对象。Drawcall对象越多,GPU消耗也就越多。所以我们可以通过以下这种排列来减少Drawcall对象:
Atlas1/Atlas1/Atlas2/Atlas2/Atlas2/Atlas3
哦?这样好像只剩下3个DrawCall了。所以争取把同一个图集的图片让他们的depth相挨着是一个简单的解决办法。但是,我还有另外一个解决办法。
Atlas1/Atlas1/Atlas1/Atlas1/Atlas1/Atlas1
3.这个办法就是将一个界面下的ui图片打到同一个图集里面。这样就只有一个DrawCall对象了。但是,这样会有一个问题。如果另外一个界面用到了相同的图片,你需要将它复制到新的图集里面,这样会造成内存浪费。所以需要将通用的图片打到一起,组成一个CommonAtlas,比如说UI的一些九宫格的通用图片。其他小图片就复制就复制了,它无非就是在内存和渲染之间找临界点。一般一个界面下来5-10个DrawCall是比较正常的(如果图集较多时)。