原文链接:https://blog.csdn.net/zzx023/article/details/88991314
这篇文章是优化系列的最后一篇了,内容会长一些,更多的是提供一些方向以及思路,具体的一些细节由于篇幅有限,就不一一说明了,只要找对方向,就可以慢慢研究。希望大家可以耐着性子看完。
使用Performance定位问题
针对CPU占用的情况,我们有很多的优化方案,但实际的项目开发过程中,更重要的是找到哪个地方是问题点。只要我们能够快速的找到问题点,那么解决方案也就应运而生了。
通过google开发者工具中的Preformance工具,可以截取一段时间内,程序运行的情况进行分析。如果在某一段时间内,CPU占用过高,导致帧率下降卡顿,那么我们可以在结果中像上图一样看到存在红色的热点帧。
通过针对这些热点帧的调用栈的分析,我们可以找到是哪一些接口的CPU占用情况比较严重。在找到热点函数后,我们就可以针对具体的函数做优化。
总之需要注意的是:
1、观察整体性能
2、观察分析局部热点帧
3、通过调用栈分析热点
具体的使用方法可以参考google的文档
使用ESLint
很多时候我们的CPU占用过高,经常是由于我们在编码过程中对于语法的不注意,或者是疏忽导致的。包括一些内存泄漏的问题,闭包的处理等等。
这里推荐一款插件,可以帮助我们强制写出高质量且整洁的代码,使得一些语法规范上面导致的问题可以被我们提前预防。
ESLint这是一款js语法规则和代码风格的检查工具,类似的工具还有TSLint,JSHint这些。
使用的话只需要通过npm进行安装
npm install -g eslint
在安装完成后,通过
eslint --init
生成配置文件,在配置文件中可以根据项目的语法规范进行相应的配置。
具体可以参考:https://cn.eslint.org/docs/user-guide/configuring
-控制游戏帧率
分离逻辑帧以及渲染帧
参考上面mainloop的代码,在默认的情况下面,我们的逻辑帧和渲染帧是相同的帧率,默认都是60帧。
但实际的项目开发过程中,像一些回合制的游戏,卡牌类游戏,或者是一些不需要太实时的游戏,实际上并不需要逻辑保持60帧的帧率在运行,从而造成性能的浪费,导致手机的电量消耗以及发热严重。
这时我们就可以将这一段代码进行改造,定制一下引擎,利用fixUpdate的方式,我们可以很轻松的将逻辑帧以及渲染帧进行分离。比如渲染帧保持60帧,而逻辑帧按照30帧的帧率运行。
这样逻辑帧和渲染帧就分离开来了,在运行时,逻辑帧的帧率我们可以统一设置,也可动态控制。
比如在需要创建大量对象时,我们就可以动态的提高逻辑帧的帧率,配合之前说过的async,从而加速对象的创建。
又比如在一些需要网络同步的游戏中,我们可以通过渲染帧帧率比逻辑帧高的特性,将一些因为网络问题导致的堆积数据取出来,采用一些插值算法,让渲染帧进行平滑的过渡渲染。这样就不会发生突然的瞬移同步。
-控制物理系统以及碰撞系统的帧率
如果有使用到物理系统,也可以去控制物理系统的帧率。参考官方文档:https://docs.cocos.com/creator/manual/zh/physics/physics/physics-manager.html#%E8%AE%BE%E7%BD%AE%E7%89%A9%E7%90%86%E6%AD%A5%E9%95%BF
默认物理系统的帧率与游戏帧率是一致的,但有些时候我们如果只是做粗略模拟的话可能并不需要这么高的帧率。这时我们可以通过一下的一些参数设置来控制物理系统的帧率:
对于碰撞系统来说,我们就需要使用fixUpdate的方法去定制一下引擎,修改CCCollisionManager.js中的这段代码。
需要注意的是,当我们降低了物理系统的帧率或者碰撞系统的帧率时,有可能会发生漏判的情况。这时由于物体的运动速度较大导致的,所以在调整帧率时,我们需要确认在游戏的设计中,物体的最大速度是多少?需要判断的物体最小体积是多大?再根据这些信息去调出合适的帧率,这是个需要反复调整的过程。
- JS代码级优化
很多时候我们对于CPU的优化都会从算法的方面着手,关于算法这一块优化方案多种多样,很多都需要根据项目的实际情况以及游戏的设计出发的。
我们经常会聚焦于算法级的优化上,而忽略掉代码级的优化。
这里带来的就是代码级的优化
1、数组操作
增加数组元素时,更推荐使用:
array[array.length] = 0;
相比较push的方法,代码执行效率上我们可以看下对比:
会有一定的提升,但提升的空间有限,因此不是强烈推荐,只是针对需要反复大量执行的代码时,更推荐使用。比如大量的a星寻路计算时,可以在编码时随手注意一下
2、for循环
常用的for循环我们有几种方式
for(let i = 0;i < arr.length; i++) for (const key in arr) for (const key of arr) arr.forEach(element => {})
除了for-in这种方式外,其他三种for循环方式没有什么太大的差距
强烈不推荐使用for-in的方式进行for循环,效率极其低下,同时随着游戏不断的运行,for-in还不能被jit优化。因此千万不要使用for-in
3、arguments
js中如果要实现类似C++中的多态,可以通过arguments去达到。
这样我们可以通过相同的接口,只是参数的不一致从而达到不同的逻辑运行效果
function argumentsTest () { if (arguments.length === 1) { //...... } else if (arguments.length === 2) { //...... } }
类似上面这样的使用。
这种方式虽然使用上看起来很秀,但要注意的是,性能也是极其低下的,需要这种情况,不要偷懒,多写几行代码,拆分成几个不同的函数进行调用会更好。
强烈推荐不要在工程中使用arguments
4、try-catch or try-finally 以及 eval
强烈建议不要使用任何的try-catch or try-finally 以及 eval,执行效率极其低下,很容易造成游戏的卡顿。
例如try-catch or try-finally系列,如果没有错误抛出,那就还好。一旦有错误抛出,效率直线下降。
5、global value
在使用全局变量时,类似下面这样
gIndex = 0; for (let i = 0; i < n; i++) { gIndex += i; }
执行结果:
不要直接使用gIndex,使用局部变量进行一下转换,效率会快很多:
var localIndex = gIndex; localIndex = 0; for (let i = 0; i < n; i++) { localIndex += i; }
执行结果:
以上就是一些代码级优化上需要注意的地方,可以看到,如果时一些热点函数,需要大量重复执行的话,如果使用这些优化方案,通常会带来很大的提升。这也是从另一个角度去进行CPU性能的优化