前言
项目是一款2D横版酷跑项目。用到了CocosCreator自带的2D物理组件。涉及到射线检测,碰撞检测。
游戏的逻辑是启动后会初始化一定数量小物件,放到缓冲池中。需要的时候根据配置表来组装成场景。
比如:路面(表面带Collider,用于和玩家检测碰撞,防止玩家持续下落)。房子(房顶有Collider,玩家可以射出一个钩子,勾中房顶,玩家会有荡秋千的效果)等等。
问题描述
在PC浏览器上运行一切OK,发布到手机浏览器上就开始卡了。微信上就更卡了。
问题原因
在文章:[CocosCreator]性能优化中,笔者提到了CC的物理系统比较坑的问题。特别是jit原因导致在ios微信小游戏上比较卡。
但是本项目,笔者在分析源码后,从代码层找到一个严重影响性能的原因。
前文中提到了游戏的大概逻辑,所以就涉及到大量对物体设置显示、隐藏的操作。本身在CC中对物体执行这种操作据官方说就相对耗。
这都不是重点。看下文:
cocos2d-js.js大概11422行,代码如下
onDisable: function() {
cc.director.getCollisionManager().removeCollider(this);
},
onEnable: function() {
cc.director.getCollisionManager().addCollider(this);
}
解读:每个附着物理组件的物体,在onEnable会被添加到系统的碰撞管理器中,在onDisable时会被移除。
再看addCollider和removeCollider的实现(大概cocos2d-js.js的11614行):
addCollider: function(collider) {
var colliders = this._colliders;
var index = colliders.indexOf(collider);
if (-1 === index) {
for (var i = 0, l = colliders.length; i < l; i++) {
var other = colliders[i];
if (this.shouldCollide(collider, other)) {
var contact = new Contact(collider, other);
this._contacts.push(contact);
}
}
colliders.push(collider);
this.initCollider(collider);
}
collider.node.on(NodeEvent.GROUP_CHANGED, this.onNodeGroupChanged, this);
},
removeCollider: function(collider) {
var colliders = this._colliders;
var index = colliders.indexOf(collider);
if (index >= 0) {
colliders.splice(index, 1);
var contacts = this._contacts;
for (var i = contacts.length - 1; i >= 0; i--) {
var contact = contacts[i];
if (contact.collider1 === collider || contact.collider2 === collider) {
contact.touching && this._doCollide(CollisionType.CollisionExit, contact);
contacts.splice(i, 1);
}
}
collider.node.off(NodeEvent.GROUP_CHANGED, this.onNodeGroupChanged, this);
} else cc.errorID(6600);
}
这就显而易见了。物体在显示的时候会往碰撞管理器添加碰撞信息,物体隐藏的时候会把碰撞信息移除。我们的游戏有大量的显示、隐藏操作。导致这部分代码(addCollider、removeCollider代码本身就有大量循环、比较)被大量执行。性能不低才怪。
解决方案
改造cocos2d-js.js文件
1,改造onDisable函数
onDisable: function() {
if (this.name.search("coin") != -1 || this.name.search("road") != -1 || this.name.search("tower") != -1) {
return;
}
cc.director.getCollisionManager().removeCollider(this);
}
对于特定类型的物体,在onDisable的时候不再移除Collider。
2,改造onEnable函数
onEnable: function() {
var colliders = cc.director.getCollisionManager()._colliders;
var isExist = false;
for (i = 0, l = colliders.length; i < l; i++) {
if (colliders[i].name == this.name) {
isExist = true;
break;
}
}
if (isExist == false) {
cc.director.getCollisionManager().addCollider(this);
}
}
已经存在于碰撞管理器中的物体,不再添加。
3,增加一个清空碰撞的函数
大概在11470行CollisionManager的构造函数下面加一个函数
RemoveAllColliders: function() {
this._colliders = [];
this._contacts = [];
}
游戏在合适的时候调用cc.director.getCollisionManager().RemoveAllColliders();
。
4,改造在CollisionManager update
大概11472行的update方法第一行时候加上一句
if(this._colliders.length==0) return;
经过以上改造,可以大大缓解我们游戏中的性能开销。