浅谈优化

UI的拼接要讲究章法,根据一定的效率流程去走,会提高效率,还需要遵守原则,提高性能

流程如下:

1、原画师提供效果图

    原画师一般会先给一张效果图,表示这个界面风格会做成什么样的,基本功能都会展示出来,包括按钮的大小和图片等,虽然是一个静态的界面,但是已经展示了所有的功能和一系列的变化,这个界面上所需要的图在这个界面上都能看得到,比如点击的时候的状态等,每一个按钮的颜色和图标等都会展现

2、美术切图

    美术切图师会根据效果图把这些图标都切出来,切成一些碎图(用到的资源),提供给程序

3、程序拿到碎图

    因为一个碎图都会让GPU传送数据一次,这样会增加DrawCall,因此拿到碎图以后需要打成大图,一般用TexturePacture这个软件

    RGBA8888:每一个通道占8位

    RGB565:没有透明度通道

    大图尺寸大小:

      如果要兼容所有机型:则打成1024*1024

      如果是在Iphone4以前,如果传一个比1024大的,则会发现手机屏幕是黑的,因为那时候的手机的GPU只能处理1024*1024以下的图片,超过了这个大小,GPU就不能识别和解压,因为GPU的显存比较小,所以GPU的显存决定了图片的大小,大于了则会黑屏

    2048*2048:高端机,Iphone以下会不能适配

4、在TexturePacker中打成大图

    导出,分别是.png文件和.tpsheet文件,

    在unity中导入插件texture import,做成图集后,把图集生成的这两个文件拖到Unity中后,然后自动切好碎图

    切图:根据UV坐标切的,在.tpsheet文件中,记录的切图的格式,

  QA:碎图打成大图?

    DC:CPU向GPU传输数据,没传送一次数据就是一个drawcall,传送的是两种数据:Material和Mesh

    降低Draw其实就是降低Material和Mesh的传输次数,Material和Mesh只要发生了变化,那么DrawCall就会重新传输或多次传输

    碎图没传送一次都会产生一次DrawCall,做成大图之后,只需要传输一次就可以了,会有效的降低DrawCall

  Mesh:

    网格合并:

      减少Mesh的传输次数

      Dynanic Batch:小兵和麻将

      两个image是否能合并,需要看Material是否一致,图片是否在同一个大图上

    Batch:

      条件:同类型的Mesh,同一个材质球,这两个必须是一致的,如果有一个发生了变化,就会分开发,不会合并

      Dynanic Batch:用程序的方式进行合并,

      Static Batch:把场景中的房子、石头进行静态批处理

    RPG:DrawCall要少于150

  Material:

    在做UI界面的时候,尽量用自己的材质球,容易控制,不添加材质球会自动使用unity默认的材质球

UI优化方面:

  UI层级计算:

    1、计算层级:

      unity会先按照是否同层级,如果同层级的话,会看是否同一个材质球

      在Shader中Queue会把场景中所以的物体分为几个渲染层次,分别是Background(1000)、Geometry(2000)、AlphaTest(2450)、Transparent(3000)、Overlay(4000)五个层级,相机中最先渲染Background,比如同样位置的cube,它的xyz都一样,谁的Tags标记在最前面,谁最先渲染,

      UI也是一样的,Unity会把物体分为很多层级,根据层级去划分,然后同一层级里又按材质球进行划分,比如Text和Image都是同层级的,但是不能合并,有两个DrawCall

      1.如果有一个UI元素,它所占的屏幕范围内(通常是矩形),如果没有任何UI在它的底下,那么它的层级号就是0(最底下)

      2.如果有一个UI在其底下且该UI可以和它Batch,那么它的层级号与底下的UI层级一样

      3.如果有一个UI在其底下但是无法与它Batch,那它的层级号为底下的UI的层级+1

      4.如果有多个UI都在其下面,那么按前两种方式,遍历计算所以的层级号,其中最大的那个作为自己的层级号

    2、合并批次原则:

      1、Unity会将每一层元素进行一个排序(按照材质、纹理等信息),合并掉可以Batch的元素成为一个批次

      2、Text组件会排在Image组件之前渲染

      3、Unity会再做一个优化,即如果相邻间的两个批次正好可以Batch的话就会进行Batch,

         比如说在0层级里面有text和image,在1层级里面也有text和image,渲染的时候先渲染0层级的text然后image,再渲染1层级的text,然后1层级的image,如果这两个image用的同一个材质球,这两个image也能合并,那么0和1这两个层级也能合并,产生两个DrawCall

    总结:1、有相同材质和纹理的UI元素是可以Batch的,可以Batch的UI上下叠在一块不会影响性能,但是如果不能Batch的UI元素叠在一起,就会增加DrawCall开销

       2、尽量让同一个材质球上的东西放在同一级上

         3、有些情况可以考虑人为增加层级从而减少DrawCall,比如一个Text的层级为0,另一个可Batch的Text叠在一个图片A上,层级为1,那此时2个Text因为层级不同会安排2个DrawCall,但如果在第一个Text下放一个透明的图片(与图片A可Batch),那两个Text的层级就一致了,DrawCall就可以减少一个

       4、要尽量避免使用Mask,其实Mask的功能有些时候可以变通实现,比如设计一个边框,让这个边框叠在最上面,底下的UI移动时,就会被这个边框遮住(Mask的原理采用的OPengl的模板,其下面的所有元素都不会合并)

       5、Z值保持为0(Z值不一样也会分批次传输)

  UI重建:

      1,、动静分离

        1、比如在做NPC血条的时候,当NPC动的时候,血条跟着NPC进行位移,这样的经常变动的UI单独放在一个Canvas下面,

        2、在制作UI时,一定要仔细查验UI层级,删除不必要的UI元素,这样可以减少深度排序的时间以及Rebuild的时间

        3、减少Rebuild的频率,将动态UI元素(频繁改变如顶点、Alpha、坐标和大小等的元素)与静态UI元素分离出来,放到特定的Canvas中

        4、谨慎使用UI元素的enable与disable,因为它们会触发耗时较高的rebuild,替代方案之一是enable和disable,UI元素的canvasrender.setDisable或Canvas

        5、谨慎使用Text的BestFit选项,虽然这个选项可以动态的调整字体大小以适应UI布局而不会超框,但其代价是很高的,Unity会为用到的该元素所用到的所有字体生成图元保存在Atlas里,不但增加额外的生成时间,还会使得字体对应的atlas变大

        6、谨慎使用Canvas的PixelPerfect(像素最优化)选项,该选项会使得UI元素在发生位置变化时,造成layoutRebuild。(比如ScrollRect滚动时,如果开启了Canvas的pixelPerfect,会使得Canvas.SendWillRenderCanvas消耗比较高)

        7、使用缓存池来保存ScrollView中的Item,对于移出或移进View外的元素,不要调用Disable或enable,而是把它们放到缓存池中取出服用

        8、除了rebuild过程之外,UGUI的touch处理消耗也可能会成为性能热点。因为UGUI在默认的情况下会对所有可见的Graphic组件调用raycast。对于不需要接收touch时间的graphic,一定要禁用raycast,对于unity5以上的可以关闭graphic的RaycastTarget,而对于unity4.6,可以给不需要接收touch的UI元素加上canvasgroup组件,关闭Interactable

  多层级渲染:

        同一个像素块的地方,有多个UI控件在绘制,它的像素buffer会不断的叠加累计, 

        一般来说,造成GPU性能瓶颈主要有两个原因:复杂的vertext或pixel shader计算以及overdraw造成过多的像素填充。在默认情况下UGUI中所以UI元素使用都使用UI/Default Shader,因此在优化的时候可优先考虑解决OverDraw问题,OverDraw主要是因为大量UI元素的重叠引起的,查看OverDraw比较简单,在Scene窗口中选择overDraw模式,场景中越亮的地方表示overDraw越高

      为了降低OverDraw,可以做如下优化:

        1、禁用不可见的UI,比如当打开一个系统时如果完全挡住了另外一个系统,则可以将被遮挡住的系统禁用

        2、不要使用空的Image,在Unity中,RayCast使用Graphic作为基本元素来检测touch,在笔者参与的项目中,很多人使用空的image并将alpha设置为0来接收touch事件,这样会产生不必要的overdraw

        3、只挂载Button替代空的image

    性能检测工具:

        profile

NGUI:

    原理:和UGUI的原理是一样的,UGUI是在NGUI的原理上发展出来的

    渲染原理:

      1、以Panel为单位

      2、同一材质球和同一Panel上的控件:会统一发送给GPU,不分层级

      3、同一类型的网格:一起传输

UI制作的各项指标(衡量游戏好坏的标准)

    1、首先是安卓2G低端机型要流畅运行在20到25fps,内存不超过350MB

    2、IOS1G低端机型流畅运行20到25fps,内存不超过300MB

    3、场景加载速度:启动场景速度尽量控制在5秒以内。还有优化是对性能和效果的权衡,这点是非常重要的,不能只考虑性能而不讲究美术效果,或者反过来只追求美术效果,玩都不能玩的话,游戏也是不成功的。

    4、顶点数:手机上:如果游戏能流畅运行,保持2W个顶点,角色的顶点数一般700----3000左右,主角色2000左右,npc:1000以下,最大承载量5W个

          PC上:最大承载量:10W左右,主角色:5000---8000个

  内存管理:

    1、程序代码: 

      1、unity3d使用的库:.Data都是unity3D常用的库文件

      2、程序员写的代码

      优化(PlayerSetting):

        1、API Compatibility level选择.Net 2.0 Subset(能把项目中没必要用到的dll去掉)

        2、Stripping Level:表示从build的库中剥离的力度,每一个剥离选项都将从打包好的库中去掉一部分内容,你需要保证你的代码没有用到这部分被剥离的功能,选为“Use micro mscorlib”的话,将使用最小的库

        3.string改为stringbuilder

        4、foreach改为for

    2、托管代码:

      1、是被Mono使用的一部分内存,.netmono框架里

      2、托管堆中存储的是你在你的代码中申请的内存,一般来说,无非是new或者Instantiate两种生成object的方法(事实上Instantiate中也调用了new)

      优化:

        1、减少对GameObject的Instantiate()和Destroy()的调用

        2、在合适的时候,也可以手动地调用System.GC.Collect()来建议系统进行一次垃圾回收

        3.多用缓存池

    3、本机堆:

      1、unity引擎进行申请和操作的地方,比如贴图、音效、关卡数据等

      2、资源:纹理、网格、音频等

      3、GameObject和各种组件

      4、引擎内部逻辑需要的内存:渲染器、物理系统、粒子系统等

      优化:

        1、在场景初始化的时候,我们要尽量的减少场景中摆放的物体,所有的资源都是采用动态加载的方式,都放在assetbundle包里,当你加载完成一个Unity的scene的时候,scene中的所有用到的asset(包括Hierarchy中所有GameObject上以及脚本中赋值了的材质,贴图,动画,声音等素材),都会被自动加载(这正是Unity的智能之处),在场景初始化的时候

        2、在脚本里面声明直接拖拽的,这样的物体是无法释放的,只有当这个脚本被释放了,那么这个GameObject才会被释放,因此尽量减少这样的引用

        3、尽量减少在Hierarchy对资源的直接引用,而是使用Resource.Load的方法,在需要的时候从硬盘中读取资源,在使用后用Resource.UnloadAsset()和Resource.UnusedAssets()尽快将其卸载掉

        4、尽量减少代码的耦合和对其他脚本的依赖是十分有必要的

        

原文地址:https://www.cnblogs.com/xingyunge/p/7594226.html