Cocos Creator 2.1.1 性能优化之分帧加载
一.原理
1.为什么要用分帧加载
如果一帧内执行了大量的同步耗时操作,会导致该帧出现卡顿。在低端设备上尤为明显。
2.如何优化
我们可以将原本在同一帧的操作,分散在之后的每一帧中完成,降低该帧的计算压力。
二、项目分析
1.cocos creator中不使用分帧加载的情况
假设我们需要在点击按钮的时候创建1000个预制体,并且获取该预制体上的脚本并赋值。
for (let i = 0; i < 1000; i++) {
let temp = cc.instantiate(this.hello);
temp.parent = this.content;
temp.getComponent('test2').label.string = i;
}
为了让我们的实验效果更明显,在开始创建第一个预制体时播放一段加载动画,最后一个预制体创建完毕时关闭该动画。
this.mask.active = true;
this.animation.node.active = true;
this.animation.play();
for (let i = 0; i < 1000; i++) {
let temp = cc.instantiate(this.hello);
temp.parent = this.content;
temp.getComponent('test2').label.string = i;
}
this.mask.active = false;
this.animation.stop();
this.animation.node.active = false;
完整代码
// Learn cc.Class:
// - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/class.html
// - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/class.html
// Learn Attribute:
// - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/reference/attributes.html
// - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
// - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html
// - [English] https://www.cocos2d-x.org/docs/creator/manual/en/scripting/life-cycle-callbacks.html
cc.Class({
extends: cc.Component,
properties: {
hello: cc.Prefab,
content: cc.Node,
mask: cc.Node,
animation: cc.Animation,
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
this.mask.active = false;
this.animation.node.active = false;
},
click () {
this.mask.active = true;
this.animation.node.active = true;
this.animation.play();
for (let i = 0; i < 1000; i++) {
let temp = cc.instantiate(this.hello);
temp.parent = this.content;
temp.getComponent('test2').label.string = i;
}
this.mask.active = false;
this.animation.stop();
this.animation.node.active = false;
},
start () {
},
// update (dt) {},
});
节点摆放:
效果展示:
从上图中可以看到我们预先制作的animation动画并未看到,并且点击按钮后卡了很久很影响体验。
2.使用分帧加载的情况
本示例中我们使用计时器的方法进行分帧加载,原理是将1000个for循环拆成一个一个的放进计时器中,每帧运行一次。
/**
*
* @param {string} name 给计时器分配一个name
* @param {number} limit 每帧至少加载几个prefab
* @param {number} max 最大加载的prefab数量
* @param {function} cb 加载代码
*/
//分帧加载
_loadPrefabFrame (name, limit, max, cb) {
let count = 0;
this[name] = () => {
if (count <= max) {
for (let i = 0; i < limit; i++) {
count++;
cb && cb(count);
//关闭动画
if (count === max - 1) {
this.mask.active = false;
this.animation.stop();
this.animation.node.active = false;
}
}
}
}
this.schedule(this[name], 1 / cc.game.getFrameRate(), (max - 1) / limit, 0);
},
完整代码
// Learn cc.Class:
// - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/class.html
// - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/class.html
// Learn Attribute:
// - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/reference/attributes.html
// - [English] http://docs.cocos2d-x.org/creator/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
// - [Chinese] https://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html
// - [English] https://www.cocos2d-x.org/docs/creator/manual/en/scripting/life-cycle-callbacks.html
cc.Class({
extends: cc.Component,
properties: {
hello: cc.Prefab,
content: cc.Node,
mask: cc.Node,
animation: cc.Animation,
},
// LIFE-CYCLE CALLBACKS:
onLoad () {
this.mask.active = false;
this.animation.node.active = false;
},
click1 () {
let cb = (i) => {
let temp = cc.instantiate(this.hello);
temp.parent = this.content;
temp.getComponent('test2').label.string = i;
}
this.mask.active = true;
this.animation.node.active = true;
this.animation.play();
this._loadPrefabFrame('test', 1, 1000, cb);
},
click () {
this.mask.active = true;
this.animation.node.active = true;
this.animation.play();
for (let i = 0; i < 1000; i++) {
let temp = cc.instantiate(this.hello);
temp.parent = this.content;
temp.getComponent('test2').label.string = i;
}
this.mask.active = false;
this.animation.stop();
this.animation.node.active = false;
},
start () {
},
/**
*
* @param {string} name 给计时器分配一个name
* @param {number} limit 每帧至少加载几个prefab
* @param {number} max 最大加载的prefab数量
* @param {function} cb 加载代码
*/
//分帧加载小实验
_loadPrefabFrame (name, limit, max, cb) {
let count = 0;
this[name] = () => {
if (count <= max) {
for (let i = 0; i < limit; i++) {
count++;
cb && cb(count);
//关闭动画
if (count === max - 1) {
this.mask.active = false;
this.animation.stop();
this.animation.node.active = false;
}
}
}
}
this.schedule(this[name], 1 / cc.game.getFrameRate(), (max - 1) / limit, 0);
},
// update (dt) {},
});
效果展示
就结果而言比一次性加载效果好多了。但是分帧加载因为开启了计时器的缘故导致在加载少量prefab时所消耗的性能要大于一次性加载,所以应根据实际需求进行选择使用。