NGUI开发优化技巧(上)
UGUI和NGUI的区别:
使用率:NGUI占大多数,NGUI和UGUI的使用占比为81% : 19%;
易用性:NGUI占一定优势,UGUI仍在慢慢改善;
性能:如果都合理搭配,UGUI在性能上还是有一点优势的,在Unity5.2、5.3之后UGUI有一部分网格合并的操作是放在多线程中进行,相对来讲,性能有一定的提升;
但NGUI的优化相对更简单一些,主要是因为NGUI的Drawcall相对容易估计,可以知道Drawcall从哪里来的,也更容易定位网格的刷新是由什么元素引起的, 但UGUI的Drawcall难以估计,且网格重建的开销因为引入了多线程,也会被隐藏起来,所以优化起来,UGUI的难度相对更高一些;
屏幕自适应
1:UIRoot/Scaling Style
— Flexible(PixelPerfect)
— Constrained(FixedSize)
可以让用户指定一个区间,当真实屏幕的像素高度介于最大、最小高度之间,那么UI元素将保持原有的分辨率,也就是说,在这个范围之间,UI元素的像素和设备的像素是可以匹配的;在分辨率高的设配上UI会相对小一些,在分辨率低的设备上这些UI图片会相对大一些;这种模式较为复杂,使用不多;
不同的设备上看到的画面只是等比的缩放,布局更加容易,使用较多;
2:UIAnchor 在旧的NGUI版本上就存在,但现在使用较少,因为使用后,UI的层次会非常多;
3:UIRect/AnchorPoint:在较新的NGUI中,Anchor放在了UIRect这个类中;
Execute的模式会影响性能,对于静态的UI,不要使用On Update模式;
如果设置为On Update,则会在Profiler中看到UIRect.Update会比较高;
事件处理
1:UIButton
2:UIEventListener
NGUI和UGUI相差很大,UGUI中的事件处理没有使用到物理中的Physics.Raycast,而是通过
射线跟四边形去做一些检查,而NGUI会使用物理系统Physics或Physics2D;
在Unity4.X上UGUI事件检测的开销有时候可能会有比较高的持续开销,因为在Unity4.X中默认所有Graphics的元素(包含Image和Texture)都会作为事件检查的目标,除非把Camera给禁用掉,在Unity5.2中才出现对每一个Graphics可以设置RaycastTarget这个属性,去掉这个属性之后,可以不参与检测;
在NGUI中就不会出现这个问题,因为NGUI只会在需要检测的地方(Button、Toggle)挂上碰撞体,所以只有在挂了碰撞体的元素才会参与事件的检测;
可以借助UIEventListener组件,在按钮或UI元素上挂上这个组件,然后在一个统一的地方去检查,去处理所有Button;
如下代码所示:
自定义材质
1:Gray
2:ETC Split
比如:做一个会变灰的按钮,自定义的Shader放到UI元素上可以正常显示,但是把它放到ScrollView里面的滚动区域时,材质丢失或完全变黑,这是因为NGUI在处理滚动框里面的内容时,为了实现遮罩效果,会去动态替换里面元素的Shader;
把Texturefen采用ETC1分离为RGB和A图片后,替换原有的Shader,当放入ScrollView的裁剪区域后,图片变黑,想要恢复原状,需要把原有的Shader替换成NGUI可以识别的Shader;
比如:把Shader “UI/UI_ETC”替换为:Shader “Hidden/UI/UI_ETC 1”(只用替换Shader名字,1代表被1个Panel做了clipping,以此类推)
当我们需要提供一个自定义的材质时,需要附带3个配套的版本,以保证UI元素放到滚动区域后依然能够正常显示;
ParticleSystem交互
1:前后遮挡
2:ScrollView裁剪
如何把一个ParticleSystem放在两个UI元素之间?
通过脚本把ParticleSystem的Renderer的RenderQueue控制在两个UI元素之间;
当ParticleSystem放到了滚动区域之后(比如背包),有一些图标需要高亮或者特殊效果,当滚动背包时,希望背包区域把这些粒子裁减掉,但默认情况下,并不会被裁剪,因为粒子系统的Shader是另一种渲染方式,跟NGUI的不一样,NGUI不仅自身有一种UI的Shader,而且还需要去替换做clip裁剪的Shader版本;
一般可以考虑的会有以下几种方式:
1:转成序列帧,做成UI的方式去做,但是表现力会大打折扣,并且图片量会有所提升;
2:设计自定义的ParticleSystem的Shader,并且NGUI中裁剪的机制放到自定义的Shader中去,相对复杂一些,毕竟ParticleSystem的坐标系和产生的Mesh时候的坐标系的对应不一样;
3:通过额外的Camera去做,就是说Particle的裁剪是通过Camera的显示区域去做,这种限制比较大,多加的Camera使UI层级的管理会比较复杂,所以还是使用第二种方式;
代码如下:
UIDrawCall:管理UI的显示,任何材质的替换都是在这个类里面做的,包括在裁剪前替换材质,并把裁剪区域做一个限定,也都是在这个脚本里面进行的;
DrawCall优化:
1:Panel Tool
2:Draw Call Tool
优化时,可以看到每个Panel产生了多少个Drawcall;
如果Drawcall使用UISprite,材质一样,并且相邻,则会自动合并成一个Drawcall,如果没有合并,说明使用了UITexture,因为每个UITexture都会产生一个Drawcall;
多个Drawcall合成一个时,并不会增大内存,因为多个UI元素,每个UI元素都对应一个Mesh,对应的Mesh顶点固定,如果不合并Drawcall,则他们的Mesh是分开的,合并后Mesh是合并的,但定点数和顶点属性是固定的;