UI模块:

NGUI:UIRect.Start()函数在耗时监测时绿色线条很多的话,说明UI界面在频繁Instantiate/Destory。 [

](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032101-300x131.png)
背包等UI需要大量切换时,不推荐使用Instantiate/Destory或Active/Deactive来频繁切换;
改为将对象移回屏幕内。这样性能就好多了。 UGUI:如何定位Mesh重建是否合理? 1.动态元素和静态元素分离; 2.合理配置UI Canvas/Panel,一个Canvas下的Widget不宜过多 UGUI和NGUI的性能区别: 如果都合理搭配,UGUI在性能上还是有一点优势的,在Unity5.2、5.3之后UGUI有一部分网格合并的操作是放在多线程中进行,相对来讲,性能有一定的提升; 但NGUI的优化相对更简单一些,主要是因为NGUI的Drawcall相对容易估计,可以知道Drawcall从哪里来的,也更容易定位网格的刷新是由什么元素引起的, 但UGUI的Drawcall难以估计,且网格重建的开销因为引入了多线程,也会被隐藏起来,所以优化起来,UGUI的难度相对更高一些; UI被多线程渲染影响的原因: 无论是UGUI,还是NGUI,都有一个特点,就是他的UI mesh可能会因为没有动静分离或别的原因产生一个网格的重建, 在网格重建之后,我们会发现它的半透明会非常的高,WaitingForJob会开销比较大,具体原因没有一个理论上的解释。 解决方案: 1.解决动静分离的问题 2.优化DrawCall 如何进行UI的动静分离: 首先要区分动态元素和静态元素的区别,静态元素就是比如主页面,都是静止的一些icon等,当你不去点击的时候都是精致的 动态的就是你改变了它的Active/Deactive或Instantiate/Destory 从UGUI或NGUI的底层来讲的话,就是要看哪些东西是引起了UI DrawCall的变化, 如果这个panel或canvas引起了DrwaCall的变化,那么可以认为是动态的 如果在一些帧中不变,那么可以认为是静态的; 对于NGUI中UIPanel.LateUpdate的优化,主要着眼于UIPanel的布局,其原则如下: 1.尽可能将动态UI元素和静态UI元素分离到不同的UIPanel中(UI的重建以UIPanel为单位),从而尽可能将因为变动的UI元素引起的重构控制在较小的范围内; 2.尽可能让动态UI元素按照同步性进行划分,即运动频率不同的UI元素尽可能分离放在不同的UIPanel中; 3.控制同一个UIPanel中动态UI元素的数量,数量越多,所创建的Mesh越大,从而使得重构的开销显著增加。比如,战斗过程中的HUD运动血条可能会出现较多,此时,建议研发团队将运动血条分离成不同的UIPanel,每组UIPanel下5~10个动态UI为宜。这种做法,其本质是从概率上尽可能降低单帧中UIPanel的重建开销。

物理模块:

物理模块的耗时跟Update其实是没有太大关系的,举个例子,比如物理模块的timestep默认为0.02,就是20毫秒更新一次, 如果游戏比较卡的话,比如一帧用了100ms,那么物理模块就要执行5次,耗时直接就上去了; 如果对物理模块完全没有使用的话,可以把0.02直接变成0.1,也就是100毫秒更新一次,对游戏运行不会有特别大的影响 如果对物理模块没有使用,并且FixedUpdate也没有使用的话,timestep值可以调的更高; Contacts数量:碰撞对数量 NGUI将UI控件都当做3D世界中的物体来做碰撞体,当碰撞对中出现UI碰撞对的时候,是因为NGUI将带有collider和rigidbody的同一深度的UI物体都当成了碰撞体。 解决方案:Edit/Project Setting/Physics,把ui和ui的碰撞去掉; Active Rigidbody数量小于50是比较好的,因为大家经常调用Rigidbody里面的API; 少量调用没有问题,如果某一帧里面大量Rigidbody大量在调用,会造成一些物理上的开销, 因为Rigidbody的physics是自己计算的,由于重力的原因,当我们移动时,跟mesh collider地表会有一个大的开销; [

](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032102-300x250.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032102.png) 碰撞体数量建议是100以下。但是三四百也没问题,这个要取决于Contacts数量,也就是碰撞对数量, 如果碰撞对数量为0,那么碰撞体数量高一点也没关系; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032103-300x129.jpg)
](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032102-300x250.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032102.png) 碰撞体数量建议是100以下。但是三四百也没问题,这个要取决于Contacts数量,也就是碰撞对数量, 如果碰撞对数量为0,那么碰撞体数量高一点也没关系; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032103-300x129.jpg)
影响物理系统耗时的因素主要为Contacts数量(碰撞对数量)、Rigidbody API的使用情况和每帧的调用次数。 1.第一种情况是最为常见的引发物理模块耗时较大的原因,因此,我们在UWA性能报告中对其进行了详细的分析,如果你的报告中Contacts数量较高,切记要验证其合理性。 2.第二情况造成较大CPU开销的情况不多,不过如果你的项目是多角色游戏(比如MMO、MOBA、ARPG割草游戏等),那么你需要注意了。在我们优化过的一些项目中,通过Rigidbody API来移动GameObject位置(设置velocity、改变center等)确实会存在较高的性能开销。如果你的项目也有类似的做法,那么要时刻关注物理模块的开销了。 3.第三种情况同样也是目前引发物理模块耗时较高的原因。因为Unity引擎默认情况下,物理的更新频率是0.02s,即每20ms更新一次,所以,当你的项目比较卡时(开发过程中的项目在中低端设备上恐怕没几个是不卡的),物理模块会让你的项目更卡。举个例子,如果上一帧CPU耗时为100ms的话,那么物理模块会执行5次,从而进一步加大物理系统的耗时。这种情况下,物理模块的耗时是很有欺骗性的,你花了好长时间去研究物理的耗时,最后发现原来这个“锅”不是它的…所以,如果你的项目也遇到了这种情况,切记不要再上当了。

动画模块:

MeshSkinning.Update耗时越低越好,蒙皮网格的顶点的面片尽可能的降低,1500面片左右就非常合理; [

](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032104-300x127.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032104.png) Animation.Update and Animator.Update: Animator.Update会有一些峰值,一般是在从新开始或者切镜头的时候可能会产生,都是属于一些离散点; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032105-300x131.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032105.png) 代码效率中会有Animators.Update: Animator.ApplyOnAnimatorMove比较高的原因是项目组重写了AvatarController.OnAnimatorMove(), 调用次数高说明每一帧都有大量的角色进行cpu的开销 所以优化cpu的话有两点,第一点看看逻辑代码是否OK,第二点看是不是调用的次数太高了; Animators.ProcessAnimationsJob里面是一些对AnimatonClip的采样和读取, unity5.3之后放到了子线程,WaitingForJob就是等待子线程处理, 根据角色的多少以及AnimationClip的复杂程度,会有一个比较高的耗时; Animators.FireAnimationEventsAndBehaviours占用比较高的一般都是动画事件, ActionEventListener.SoundActivity(),ActionEventListener.effectActivity(),ActionEventListener.bulletActivity()是动画脚本事件 Animators.DirtySceneObjects是经常会比较高的, [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032106-300x177.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032104-300x127.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032104.png) Animation.Update and Animator.Update: Animator.Update会有一些峰值,一般是在从新开始或者切镜头的时候可能会产生,都是属于一些离散点; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032105-300x131.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032105.png) 代码效率中会有Animators.Update: Animator.ApplyOnAnimatorMove比较高的原因是项目组重写了AvatarController.OnAnimatorMove(), 调用次数高说明每一帧都有大量的角色进行cpu的开销 所以优化cpu的话有两点,第一点看看逻辑代码是否OK,第二点看是不是调用的次数太高了; Animators.ProcessAnimationsJob里面是一些对AnimatonClip的采样和读取, unity5.3之后放到了子线程,WaitingForJob就是等待子线程处理, 根据角色的多少以及AnimationClip的复杂程度,会有一个比较高的耗时; Animators.FireAnimationEventsAndBehaviours占用比较高的一般都是动画事件, ActionEventListener.SoundActivity(),ActionEventListener.effectActivity(),ActionEventListener.bulletActivity()是动画脚本事件 Animators.DirtySceneObjects是经常会比较高的, [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032106-300x177.png)

粒子系统:

unity5.3之后,粒子系统由主线程调到了子线程 ParticleSystem.ScheduleGeometryJobs 粒子系统的计算和准备 [

](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032107-300x128.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032107-300x128.png)
ParticleSystem.Update的优化建议: 1.严格控制粒子系统的active的数量,每一帧里面的数量越多,ParticleSystem.Update就越高,无论这个粒子系统是否在视域体里面 2.对于视域体外面的,可以根据一些具体情况看看ParticleSystem是否可以进行关闭,可以通过一些方式, 一.比如说直接就计算粒子系统它的一个距离,当距离比较远时,可以进行关闭; 二.其次就是算一个它的UV,就是往屏幕上做投影,发现它在屏幕外测的时候,也是可以尝试着进行关闭 三.在unity5.2之后,unity有一个很好的功能就是CullingGroup, CullingGroup可以告许你每一帧它是否在视域体外或者是它跟相机的一个距离 这样就会有效的降低ParticleSystem.Update的cpu性能开销。 根据移动设备对粒子系统进行管理,对于低端设备尽可能降低粒子系统的复杂程度和屏幕覆盖面积,从而降低其渲染方面的开销,提升低端设备的运行流畅性。具体做法如下: 1.在中低端机型上降低粒子数、同屏粒子数,比如仅显示“关键”粒子特效或自身角色释放的粒子特效等,从而降低Update的CPU开销; 2.尝试关闭离当前视域体或当前相机较远的粒子系统,离近后再进行开启,从而避免不必要的粒子系统Update的开销; 3.尽可能降低粒子特效在屏幕中的覆盖面积,覆盖面积越大,层叠数越多,其渲染开销越大; 4.对于Unity 5.x项目,可尝试升级到5.3以后版本,因为Unity在5.3版本后对粒子系统底层进行了深入的优化。

加载模块:

当GameObject数量和资源数量较高时,会造成RUUA CPU开销比较高,耗时会增高; [

](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032108-300x257.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032108.png) RUUA CPU就是Resources.UnloadUnusedAssets CPU; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032109-300x258.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032109.png) Resources.UnloadUnusedAssets CPU耗时主要是判断asset是否是Unused,那么如何判断呢, Unity引擎并不是按照引用计数的,因为这样会出现很多问题,比如相互引用等,他已经游离了,但是引用计数不为0; unity主要是还是通过查看asset是否被索引,如果没有被索引的话,才会被认为时Unused, 所以当我们的GameObject数量和资源数量较高时,RUUA CPU的循环就比较多,开销会比较多; Loading.UpdatePreloading CPU耗时是unity引擎最为耗时的主要的一个加载模块开销,小于1500毫秒就可以了; Loading.UpdatePreloading 在代码效率里的堆栈信息: GarbageCollectAssetsProfile比较高,因为它就是RUUA CPU里的Unused的开销; Application.LoadLevelAsync Integrate是加载场景; GC.Collect是垃圾回收; UnloadScene就是收集上一个场景的gameobject和以及它身上的component进行卸载,就是Destory; Loading.AwakeFromLoad是加载场景后做的一些其它事情,比如Mesh.AwakeFromLoad将Mesh从cpu传到gpu; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032110-300x188.png)
](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032108-300x257.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032108.png) RUUA CPU就是Resources.UnloadUnusedAssets CPU; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032109-300x258.png)](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032109.png) Resources.UnloadUnusedAssets CPU耗时主要是判断asset是否是Unused,那么如何判断呢, Unity引擎并不是按照引用计数的,因为这样会出现很多问题,比如相互引用等,他已经游离了,但是引用计数不为0; unity主要是还是通过查看asset是否被索引,如果没有被索引的话,才会被认为时Unused, 所以当我们的GameObject数量和资源数量较高时,RUUA CPU的循环就比较多,开销会比较多; Loading.UpdatePreloading CPU耗时是unity引擎最为耗时的主要的一个加载模块开销,小于1500毫秒就可以了; Loading.UpdatePreloading 在代码效率里的堆栈信息: GarbageCollectAssetsProfile比较高,因为它就是RUUA CPU里的Unused的开销; Application.LoadLevelAsync Integrate是加载场景; GC.Collect是垃圾回收; UnloadScene就是收集上一个场景的gameobject和以及它身上的component进行卸载,就是Destory; Loading.AwakeFromLoad是加载场景后做的一些其它事情,比如Mesh.AwakeFromLoad将Mesh从cpu传到gpu; [![](http://www.wjgbaby.com/wp-content/uploads/2018/03/18032110-300x188.png)
加载资源是放在Update里面合适还是放在协程里面: 一般是用协程,不过放在Update里面也没什么问题 目前大部分手游可以使用2048*2048的贴图;

GC调用:

调用频率是一个很重要的标准, 1.要尽可能的优化代码的堆内存,可以减少foreach,link的使用,减少stream关联等,这些问题并不是最主要的 最主要的是如果项目里的运行一万帧超过了20M,那么GC回来的很快,实际项目中会把代码都拆开, 然后定位到底哪个函数在这一万帧中分配了多少堆内存,所以其实并不是特别在意foreach,link使用,stream关联等 XML文件或者配置文件或者解密加密这一块经常会有几十M的堆内存分配,大部分的堆内存都是配置文件造成的 所以尽可能的要在1万帧以下小于20M,最高不要超过30M; 2.当堆内存降下来后,可以尝试放开手动的GC调用; GC释放策略: 在场景切换的时候进行一些GC的,会耗费100毫秒左右; 不建议在UI开启或关闭的时候进行 UWA视频链接:https://v.qq.com/x/page/x0378dxq4m3.html