游戏性能优化,一直是游戏开发者需要学习掌握的课题,在移动设备硬件性能远弱于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帧。

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帧。

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打开面板,然后点击左上角的圆圈,变为红色后,刷新游戏,可以统计到游戏资源的加载信息。

根据此统计分析出资源加载的情况,然后针对性的去做一些优化。

【最后】

除了本文的经验总结,其实最好的优化方法,就是多测试,多对比,不断总结经验,选择更好的实现方式,写出更优质的代码。

LayaAir引擎

裸跑性能媲美APP的新一代HTML5引擎;

支持Canvas\WebGL模式自动切换;

支持2D\3D\VR产品开发;

支持使用ActionScript3\TypeScript\JavaScript三种语言开发;

一次开发可同时发布:手游APP、HTML5、Flash页游多端版本。

LayaAir引擎开发者交流群

QQ群号:104144216

Layabox开发者官方总群(综合交流)

QQ群号:330223972

Layabox人才招聘

Email:hr@layabox.com