问题概述
当一款游戏真正进入测试阶段时,性能优化将是一个避不开的问题。有时候你可能会发现明明一个很简单的小游戏却出现了极其严重地掉帧,先不要着急去怀疑你的电脑配置,而是观察下自己的程序是否优化到位。
Profiler是一款unity自带的良好的分析器,它位于Windows-Analysis栏下,在测试阶段我们可以痛过它来检测游戏中各个部分占用的内存。
界面如下:(非测试阶段是空白的,需要进入测试中才有显示)
这里面一共分了若干部分:
CPU usage:CPU占用情况
Rendering:渲染占用率
Memory:内存占用情况
这几点主要是关注了目前实际的运行效率与占用状况,如果你不会打印FPS的话用它来检测帧速是一个不错的选择。游戏刚开启的时候帧率趋于最低,然后会迅速趋于平缓。
这里我们重点关注一下底下的Main Thread,这里面主要记录了各个方法以及函数调用的总耗时。当我们发现某一帧掉帧十分严重时,可以定位到那一帧上,然后检测各个方法的耗时——首先能看到最长的是WaitForTargetFPS,这个一般是垂直同步最为依赖的步骤,如果垂直同步开到最大,那么程序就会在“保证帧速”的条件下,为垂直同步的操作预留出足够的空间,这也是为什么尽管系统占用很小,FPS的上限仍然不会变的一点原因。
在WaitForTargetFPS后面可以看到各个函数的调用顺序与耗时,此时可以揪出导致冗长卡顿的罪魁祸首。
将细节放大后可以观察其中每一个实例(或组件)的调用细节,角色交互阶段就是从渲染->脚本->动画逐个完成的,因此到底是哪一步出了问题一目了然。
作为一个初级者,经过几天的翻阅博客+unity自带的profiler性能分析器的使用,总结了以下的几点。
优化建议
1、将UI改用Sprite
通过实验发现,将UI换成普通的Sprite后Rendering方面的效率有所提升(虽然不明显,但是在UI较多、细节较杂的时候就会有所体现了)unity对UI的渲染会占较大的内存,使用Sprite虽然需要额外加一个视角跟随的脚本,但确实可以节省渲染时间。
2、垂直同步降至低配置
垂直同步的意义在于申请一段时间以同步相邻帧的图像防止撕裂。但是通过profiler可以看到,垂直同步将会占据大量的时间,关闭垂直同步虽然可能会使画面偶尔出现裂痕,但是可以大大提高运行的帧率。
3、减少无用的Component,尤其是Animator和rigidbody
4、对于需要用到Trigger的物体 减少能与之碰撞的物体类数
使用Trigger却不限制判定对象是件很可怕的事情。通过profiler可以看到,如果不对碰撞体的类别加以限制,有时候ontrigger()会一下检测成千上万的物体(系统自带的Defaul,water,UI,等等),这也给函数OntriggerStay的读取增加了很大的压力。
5、将GetComponent和Find放到Start函数中,尽量减少在update中出现
因为GetComponent和Find都是靠遍历来完成的方法,地图中的物体越多,他们所损耗的时间就越长,因此应该在Start中直接确定对象。
6、对于内部计算复杂的函数,使用协程来完成
有些函数中包含着大型循环或是foreach等操作,如果每次调用都会导致卡顿的话,不妨使用协程来完成,避免对其他流程的进行产生干扰。
7、将视野外无影响的物体的Active置为false
这是一个极限操作,对于较大的地图而言,如果真能做到这一项,那么引擎的压力一下就会减小特别多(前提是视野外的物体不需要做任何交互)
8、一定不要用Playerprefs来做全局变量,应当用global继承
划重点!Playerprefs虽然简单粗暴,但是它的存储速度和读取速度非常非常非常非常慢,有点像打开一个文件再检索的速度,如果你敢在update中使用Playerprefs,那它就敢给你上演什么叫爆帧。在unity中建议新建一个类Global继承MonoBehavior,然后剩下所有的类都去继承Global。
9、及时删除粒子,尽可能控制粒子系统的粒子数量和范围
粒子越多占用越大,计算越复杂压力越大,这个很显然。当然及时删除无用的粒子也是需要注意的。
10、尽量用objectpool取代instantiate object和destroy object
对象池是个好东西,它可以将所有预制的物体暂存在池中,等需要生成时直接将它们拿出来就好了,避免一次又一次新预制导致的性能下降。
11、对比tag的时候尽量使用CompareTag(“Tag”)而不是=="Tag"
这是一个特殊用法了。关于tag本身的性能并不太好,因为官方特地给了一个CompareTag方法来比较Tag,经过测试发现每帧可以节省几毫秒,但如果这个方法用在每帧调用的函数中,优化效果就非常明显了。