游戏性能优化,一直是游戏开发者需要学习掌握的课题,在移动设备硬件性能远弱于PC的背景下,我们对性能的需求显得更加重要。
如果一个游戏只有10帧的体验,即使具有优秀的游戏潜质,也会被性能毁掉。LayaAir引擎设计之初,就以性能为第一目标,在引擎内做了大量的性能优化,以保障游戏不在性能上存在瓶颈。
尽管LayAir引擎性能很高,如果开发者不能发挥好引擎的优势,游戏最终的性能体验或将无从谈起。因此,在制作游戏过程中,掌握游戏以及引擎的优化技巧还是非常有必要的。
打开游戏的性能统计面板
想优化性能,必须先知道性能瓶颈在什么地方,LayaAir引擎提供了一个性能统计面板,里面有很多性能参数可供查看。
由于LayaAir引擎支持三种开发语言(AS3、TypeScript、JavaScript),我们分别给出三种不同的统计面板调用方法,请参照下面的写法:
Stat.show(0,0); //AS3的面板调用写法
laya.utils.Stat.show(0,0); //TS与JS通用写法
Laya.Stat.show(0,0); //JS的面板调用写法
性能统计面板的参数
LayaAir引擎支持Canvas渲染模式与WebGL渲染模式,以下将分别对两种不同模式的参数进行逐一解释。
Canvas模式统计面板如下:
FPS(2D)
面板中FPS表示游戏每秒帧率,(2D)表示是Canvas模式,满帧60,这个参数的数字越高,表明游戏性能越好,游戏的体验越流畅。
Sprite 面板中Sprite表示渲染的节点数量,即每次渲染精灵的个数(包括容器),这个数会影响引擎遍历,组织数据和渲染,越少越好。
DrawCall
面板中DrawCall在Canvas模式表示每帧的绘制次数,包括图片、文字、矢量图,这个参数的数字也是越少越好。最多的时候建议不要超过100个。
Canvas
面板中Canvas表示缓存画布的数量,分别代表 (每帧重绘的画布数量 / 缓存类型为”normal”类型的画布数量 / 缓存类型为”bitmap”类型的画布数量”)。
WebGL模式统计面板如下:
FPS(3D)
面板中FPS表示游戏每秒帧率,(3D)表示是WebGL模式,满帧60,这个参数的数字越高,表明游戏性能越好,游戏的体验越流畅。
Sprite 面板中Sprite表示渲染的节点数量,即每次渲染精灵的个数(包括容器),这个数会影响引擎遍历,组织数据和渲染,越少越好。
DrawCall
面板中DrawCall在WebGL模式下表示渲染提交批次,每次准备数据并通知GPU渲染绘制的过程称为1次DrawCall,在每1次DrawCall中除了在通知GPU的渲染上比较耗时之外,切换材质与shader也是非常耗时的操作。 DrawCall的次数是决定性能的重要指标。 Canvas
面板中Canvas表示缓存画布的数量,分别代表 (每帧重绘的画布数量 / 缓存类型为”normal”类型的画布数量 / 缓存类型为”bitmap”类型的画布数量”)
CurMem 面板中CurMem表示当前使用的内存与显存总占用大小。
Shader
面板中Shader表示shader的提交次数。
无论是Canvas模式还是WebGL模式,我们都需要重点关注DrawCall,Sprite,Canvas这三个参数,然后针对性的进行优化。
针对Sprite的优化
1)尽量减少不必要的层次嵌套,减少Sprite数量
2)非可见区域的对象尽量从显示列表移除或者设置visible=false
3)对于容器内有大量静态内容或者不经常变化的内容(比如按钮),可以对整个容器设置cacheAs属性,能大量减少Sprite的数量,显著提高性能。如果有动态内容,最好和静态内容分开,这样就可以只缓存静态内容。
4)Panel内,会针对panel区域外的直接子对象(子对象的子对象判断不了)进行不渲染处理,超出panel区域的子对象是不产生消耗的。
针对DrawCall的优化
1)对复杂静态内容设置cacheAs,能大量减少DrawCall。
2)尽量保证同图集的图片渲染顺序是挨着的,如果不同图集交叉渲染,会增加DrawCall数量。
3)尽量保证同一个面板中的所有资源用一个图集,这样能减少提交批次。
针对Canvas的优化
在对Canvas优化时,我们需要注意,在以下场合不要使用cacheAs:
1)对象非常简单,比如一个字或者一个图片,设置cacheAs=bitmap不但不提高性能,反而会损失性能。
2)容器内有经常变化的内容,比如容器内有一个动画或者倒计时,如果再对这个容器设置cacheAs=bitmap,会损失性能。
可以通过查看Canvas统计信息的第一个值,判断是否一直在刷新Canvas缓存;
针对cacheAs的相关介绍
设置cacheAs可将显示对象缓存为静态图像,当cacheAs时,子对象发生变化,会自动重新缓存,同时也可以手动调用reCache方法更新缓存。 建议把不经常变化的复杂内容,缓存为静态图像,能极大提高渲染性能,cacheAs有"none","normal"和"bitmap"三个值可选。
默认为"none",不做任何缓存。
当值为"normal"时,canvas下进行画布缓存,webgl模式下进行命令缓存。
当值为"bitmap"时,canvas下进行依然是画布缓存,webGL模式下使用renderTarget缓存。这里需要注意的是,webGL下renderTarget缓存模式有2048大小限制,超出2048会额外增加内存开销。另外,不断重绘时开销也比较大,但是会减少drawcall,渲染性能最高。 webGL下命令缓存模式只会减少节点遍历及命令组织,不会减少drawcall,性能中等。
前面多次提到cacheAs,cacheAs是引擎优化性能的利器,一定要好好应用,他主要在两方面提高性能,一是能减少节点遍历和顶点及三角形计算,二是减少drawCall,合理利用cacheAs能大大提高游戏性能。
在以下例子里,实现绘制8000个文字的DEMO,我们通过运行后截图看到,FPS是45帧。
代码语言:javascript复制Laya.init(550, 400,Laya.WebGL);
Laya.stage.scaleMode = Laya.Stage.SCALE_SHOWALL;
Laya.Stat.show();
var textBox = new Laya.Sprite();
// 5000个随机摆放的文本
var text;
for (var i = 0; i < 8000; i )
{
text = new Laya.Text();
text.text = (Math.random() * 100).toFixed(0);
text.color = "#CCCCCC";
text.x = Math.random() * 550;
text.y = Math.random() * 400;
textBox.addChild(text);
}
Laya.stage.addChild(textBox);
当我们对文字所在的容器设置为cacheAs之后,如下面的例子所示,性能获得较大的提升,FPS达到到了60帧。
代码语言:javascript复制Laya.init(550, 400,Laya.WebGL);
Laya.stage.scaleMode = Laya.Stage.SCALE_SHOWALL;
Laya.Stat.show();
var textBox = new Laya.Sprite();
// 5000个随机摆放的文本
var text;
for (var i = 0; i < 8000; i )
{
text = new Laya.Text();
text.text = (Math.random() * 100).toFixed(0);
text.color = "#CCCCCC";
text.x = Math.random() * 550;
text.y = Math.random() * 400;
textBox.addChild(text);
}
//缓存为静态图像
textBox.cacheAs = "normal";
Laya.stage.addChild(textBox);
其他通用优化策略
1、尽量减少对象重复创建,可以使用LayaAir引擎提供的对象池类(Pool类),复用已经创建的对象;
2、Handler尽量用Handler.create创建,通过此方法创建使用后会立即回收,或者自己手动调用recover()方法回收;
3、尽量减少滤镜,遮罩的使用,虽然LayaAir引擎对这些做了大量优化,但是还是不推荐大量使用;特别说明一下,在webGL模式下颜色滤镜消耗很小,可以使用。另外,场景中不重绘的对象使用滤镜,也可以达到几乎无损耗的程度。
4、减少粒子使用数量,在Canvas模式下,尽量不用粒子,否则性能会有损耗;
5、对象不显示的时候,尽量停掉内部的Timer,减少不必要的计算;
6、在Canvas模式下,尽量减少旋转,缩放,alpha等属性的使用,这些属性会对性能产生消耗。(在WebGL模式可以使用);
7、减少文本描边的使用,适量使用位图字体代替;
8、设置Laya.stage.frameRate = “mouse”,在设置后,引擎默认会以30帧运行,只有鼠标活动后才会自动提速到60帧,这样既能保证鼠标操作的流畅性,又能减少不操作的性能消耗;
9、还可以设置Laya.stage.frameRate = “slow”,默认以30帧运行,来降低性能消耗,30帧的帧率已经能保证大多数游戏友好的体验;
10、删除对象时,确保外部没有对他进行引用,否则会造成内存泄漏,还可以手动调用destory方法销毁此对象;
11、不用的资源可以通过Loader.clearRes方法销毁;
12、如果多个属性都需要导致某个函数调用,可以使用callLater函数来延迟处理函数调用,减少函数计算开销,如果函数计算开销不大,建议不要使用;
13、同时加载大量图片会导致性能下降,尽量把加载分摊开;
14、设置cacheAs后,还可以设置staticCache=true,来阻止自动更新缓存,同时可以手动调用reCache方法更新缓存;
15、不要在timeloop里面创建对象及复杂计算;
16、尽量减少对容器的autoSize的使用,减少getBounds()的使用,因为这些调用会产生较多计算;
17、尽量少用try catch的使用,被try catch的函数执行会变得非常慢;
18、尽量缓存属性到局部变量,比如var len = arr.length;这样能减少属性查询及计算开销
19、使用Text类的changeText方法更改文本,可以减少排版消耗,对于不需要更改排版信息的内容更改,建议使用此方法修改内容;
20、多学习js代码书写优化策略,多测试对比性能,选择更好的方案;
使用Profiles分析游戏
在chrome打开游戏后,按快捷键F12,就打开了chrome开发工具,切换到Profiles面板,可以分析游戏性能开销
CPU占用分析
按上图所示,点击start开始统计。
这里可以看到哪些函数照成多少开销,然后有针对性的进行优化
内存实例分析及内存泄漏分析
点击Take Snapshot 开始统计内存快照,我们可以看到如同下图的详细对象实例信息
再等一会,通过点击左上角圆点,统计一个新的内存快照。如下图所示,点击选中第二个快照,然后通过Comparison进行内存对比。
这样就能分析出当前时间和上一次事件新创建的对象,如果有大量对象创建,是需要通过性能优化来解决的。
资源加载分析
先点击Network打开面板,然后点击左上角的圆圈,变为红色后,刷新游戏,可以统计到游戏资源的加载信息。
根据此统计分析出资源加载的情况,然后针对性的去做一些优化。
【最后】
除了本文的经验总结,其实最好的优化方法,就是多测试,多对比,不断总结经验,选择更好的实现方式,写出更优质的代码。