NGUI开发优化技巧(下)

[+]

NGUI开发优化技巧(下)


NGUI更新开销:NGUI进行自身的数据更新以及生成网格的过程中所产生的开销,目前UI部分的CPU耗时大部分都是这部分的开销;

NGUI的更新开销几乎全部来源于UIPanel.LateUpdate中;一般超过3~4ms,说明这部分更新开销较高;

UI更新的数量和频率通过合理的布局,可以把这部分的开销限制在很多小的范围里面;

在关卡的切换或点击一些UI界面时,可以出现一些较高的峰值;

但在战斗部分要保证UIPanel.LateUpdate在很小的范围内波动,一般在2~3ms

 

UIPanel.LateUpdate在进行完全的重建时,才会出现较高的堆内存开销;

NGUI开发优化技巧(下)


NGUI更新的对渲染的影响:

NGUI的更新在渲染时的耗时出现在MeshRenderer.RenderMesh.DrawVBOMesh.CreateVBO有一部分是由NGUI造成的,NGUIUIPanel的刷新和UI元素的变动导致网格发生了变化或更新,都会使Mesh.Create产生开销;

NGUI开发优化技巧(下)

UIPanel.LateUpdate的总堆内存分配在10~20M之间,是合理的;(10000~20000帧)

UIPanel的堆内存是有UIPanel的刷新引起的,堆内存越高说明UIPanel的刷新频率越高,所涉及的UI元素的数量可能越大;


UILabel的更新开销

1Font.CacheFontForText

2Shadow

3Outline

NGUI开发优化技巧(下)


NGUI开发优化技巧(下)

在手机上,纹理的大小会有所限制,纹理的内容会不停地变动,纹理不停地刷新,会导致

Font.CacheFontForText不停地被触发;

出现文本比较大,首次出现,并且用的是动态字体,就会产生比较高的Font.CacheFontForText函数开销,对于频繁地、反复使用的文本,尽可能地做成静态字体;

NGUI开发优化技巧(下)

NGUI开发优化技巧(下)

开启和不开启Shadow的区别;

NGUI开发优化技巧(下)

NGUI开发优化技巧(下)

开启和不开启Outline8的区别;(比Shadow的性能开销还大

 

对于频繁移动的文本,开启Effect效果,对性能开销比较大;

 

对于动态的文本(会移动或会变颜色),尽可能少用这些特效,对于静态的不影响;(不会每一帧去更新网格)

 

UISprite的更新开销

NGUI开发优化技巧(下)

(Size越大,平铺地越多,三角面片和网格数越多,造成开销越大

NGUI开发优化技巧(下)

使用和不使用Tiled模式的区别;

 

UISprite或文本的UI元素本身在移动或变颜色时,才会真正产生开销;静止时则不会更新,

不产生开销;

 

 

UIPanel更新机制

1:更新单个UIDrawCall

2:更新所有的UIDrawCall

 

UIDrawCall:一个UIPanel中可能会有各种各样的UI元素,这些UI元素可能来自于不同的图集,对于来自于不同图集的元素最终渲染时不能合在同一个DrawCall里面,所以一个UIPanel里面会包含很多个UIDrawCall,这些UIDrawCall首先按照UI元素的深度(Depth)进行排序,

然后再根据相邻元素是否来自于同一个图集来进行合并,这里的每一个UIDrawCall都包含了若干个UI元素,这些UI元素来自于同一个图集,且深度值在排序里面是相邻的;

 

进行Panel更新时,有两种模式:

1:更新单个UIDrawCall:在更新过程中,某个UI元素进行了移动,只会更新自己所在的UI元素里面的网格,不会影响其他的DrawCall

2:更新所有UIDrawCall:一旦满足某种条件,会直接重建UIPanel里面所有的DrawCall

 NGUI开发优化技巧(下)

NGUI开发优化技巧(下)

NGUI开发优化技巧(下)

NGUI开发优化技巧(下)

NGUI开发优化技巧(下)

并不是把DrawCall降低地越低越好,当一个顶点发生变化时,因为这个顶点和其他的顶点在同一个Mesh上面,所以一个顶点的更新会导致所有Mesh的更新;

 

如果添加的界面并不是和变动的UI元素在同一个DrawCall里面,那么在更新元素时,并不会产生影响;

总结

对于频繁变动的UI元素,其所在的UIDrawCall面片数越少越好;

 

更新所有的UIDrawCall

主要原因:UIPanel中的UIDrawCall发生了变化;(在UIPanel里面添加了DrawCall或是把UIPanel里面原有的DrawCall拆分成多个(动态地添加或删除一些UI元素))

NGUI开发优化技巧(下)

NGUI开发优化技巧(下)


在这一帧内,有很多网格进行了重建,会影响到Mesh.CreateVBO

动态添加窗口,穿插了NGUI Panel,导致UIPanel的完全重建;

NGUI开发优化技巧(下)

将其中一个UIDrawCall分离到其他的UIPanel中后

 

不同UIPanel下的UI元素是不能进行合并的;

调整深度,不要打断原有的DrawCall

动态的UI元素中少放东西,或直接把它独立成一个UIPanel

 

更新所有UIDrawCall的条件

1:添加/删除元素时,穿插了其他的UIDrawCall

2:添加/删除的元素自成一个UIDrawCall

 

总结:

1:对于频繁变动的UI元素,其所在的UIDrawCall面片数越少越好;

2:动态添加UI元素时,注意Depth的设置,尽可能合入已有的UIDrawCall

(在添加UI元素时,这个Depth是包含在现有的DrawCall里面的,并且这个DrawCall的材质和所添加的元素的材质是一致的,这样就不会产生完全的重建)

3:对于需要动态添加UI元素的UIPanel,减少其复杂度,从而避免在更新所有

UIDrawCall时开销过大;(战斗过程中出现的连击提示,应尽可能地减小这种界面的复杂度,

不要把静态的东西(头像、置顶的怪物的血条)也放在UIPanel里面)

 

动态字体(字体在不停变换时):在手机上,文本所对应的字体、材质、纹理会不停地进行更新,每一次想往纹理里面添加新的文字,当发现纹理不够时,会触发刷新的操作,会根据当前创建纹理大小的限制(比如需要创建512*512,但根据某些机制发现只能创建256*256的),则会先把屏幕上**状态的文本填入纹理,对于出现过的,但是已经被禁用的文字就会全部被剔除掉,这个过程相当于完全更新了纹理里面的内容,这种操作也是耗时最大的操作;

动态字体优化的两种方式:

1:如果字体可以做成静态的,则优先做成静态的,就可以避免Font.CacheFontForText

但也会出现一些问题(比如:在一些剧情比较复杂的游戏中,涉及的剧情非常多,每一段剧情需要的文本量非常大,如果做成静态的,可能带来10多张1024*2048这种纹理,这就不同通过转成静态字体这种方式来处理这种情况);

2:文本只出现一次,后面不会出现,可以让它预先出现,比如创建一个Label,里面有这个文字,并且是处于**状态的,只是相机看不到,可以把这些峰值移动到可以卡顿的地方,在真正播放剧情时,通过移动或者修改Layer让这些文字更流畅地逐渐显示出来;

进入读条时,先把这些文本放成**状态,让动态字体需要的纹理先生成好;

 

 

多个动态的物体,放在一个Panel好,还是放在多个Panel好;

通常进行分组比较好,可以少量地提高合并网格的计算量(优化量并不多,因为网格总量是一样的,只是分成几个分组而已),影响比较大的是:Camera.Render里面的CreateVBO

 

UI元素的分离时有一个好处:

一个屏幕上存在多个UI元素,只有少量的UI元素在移动,可以做动态Panel的划分,对于静态不动的网格不应该去更新;