物理引擎 - chipmunk :
生成物理世界 :
第一步 :
新建项目打开 project.json 将chipmunk模块导入 .
"modules" : ["cocos2d", "chipmunk"],
第二步 :
在 app.js 的 ctor 函数中里添加3个函数
ctor:function () {
this._super();
//初始化物理世界
this.initPhysics();
//开启计时器
this.scheduleUpdate();
//显示碰撞框体
this.showDebug();
},
初始化物理世界 :
initPhysics: function(){
var width = cc.winSize.width;
var height = cc.winSize.height;
this.space = new cp.Space();
this.space.gravity = cp.v(0, -200); //设置重力 重力向下为 200
var staticBody = this.space.staticBody;
var walls = [
new cp.SegmentShape(staticBody, cp.v(0, 0), cp.v(width, 0), 0), //最后一个参数是墙的厚度 , 很重要 , 没有厚度的墙体容易被穿透.
new cp.SegmentShape(staticBody, cp.v(0, height), cp.v(width, height), 0),
new cp.SegmentShape(staticBody, cp.v(0, 0), cp.v(0, height), 0),
new cp.SegmentShape(staticBody, cp.v(width, 0), cp.v(width, height), 0)
];
for (var i = 0; i < walls.length; i++)
{
var shape = walls[i];
shape.setElasticity(1); //设置弹性系数
shape.setFriction(1); //设置摩擦力
this.space.addStaticShape(shape);
}
},
update计时器 :
update: function (dt) {
var timeStep = 0.03;
this.space.step(timeStep); //timeStep 控制物理世界的时间流速
}
显示物理框体 :
showDebug: function () {
this._debugNode = new cc.PhysicsDebugNode(this.space);
this._debugNode.visible = true; // 为true 时, 显示物理框体
this.addChild(this._debugNode);
},
第三步 :
onEnter中添加点击事件 , onExit中移除 :
onEnter: function () {
this._super();
cc.eventManager.addListener({event: cc.EventListener.TOUCH_ONE_BY_ONE,
onTouchBegan: this.TouchBegan.bind(this)
}, this);
},
onExit: function () {
this._super();
cc.eventManager.removeListener(cc.EventListener.TOUCH_ONE_BY_ONE);
},
点击生成物理精灵 :
TouchBegan: function (touch, event) {
cc.log("TouchBegan");
var p = touch.getLocation();
var body = new cp.Body(1, cp.momentForBox(1, 50, 50)); //其中参数 1 是质量(mass) | 50是框体的 宽和高
body.setPos(p);
this.space.addBody(body);
var shape = new cp.BoxShape(body, 50, 50);
shape.setElasticity(0.5); //设置弹性系数
shape.setFriction(0.5); //设置摩擦力
this.space.addShape(shape);
//创建物理精灵
var sprite = new cc.PhysicsSprite("res/box.png");
sprite.setBody(body);
sprite.setPosition(cc.p(p.x, p.y));
this.addChild(sprite);
return true;
},
这样就可以生成一个简单的物理空间了.
物理引擎常用API :
设置冲量 :
- 只能对 Body 设置, 提供一个向上为1000的冲量 , 冲量相当于瞬间的力 , 通常做跳跃时使用.
body.applyImpulse(cp.v(0,1000),cp.v(0,0))
设置力 :
- 只能对 Body 设置, 提供一个向上为1000的力 , 和冲量不同 , 力会一直存在.
body.applyForce(cp.v(0,1000),cp.v(0,0))
设置碰撞类型 :
- 只能对 Shape 设置 , 设置了类型才可以做碰撞检测
shape.setCollisionType(1001);
设置分组、层 :
- 只能对 Shape 设置 , 设置了分组 , 同组之间相互没有碰撞 .
注意 : 同层之间才会发生碰撞 , 不同组之间会发生碰撞 .
shape.group = 5;
shape.layers= 5;
获取碰撞类型 :
- 没有get函数 直接获取属性
var type = shape.collision_type;
给两个特定碰撞类型添加碰撞事件 :
- 第1 、第2 参数是需要检测碰撞的2个碰撞类型
- 后4个参数 是回调函数 , 分别是 :
刚接触时(Begin) | 接触的每次step(Pre) | 接触并碰撞相应已经处理(Post) | 分离(Separate)
this.space.addCollisionHandler(1001, 1002, Begin, Pre, Post, Separate);
Begin: function(araiber, space)
{
cc.log("CollisionBegin")
}
//...
特别注意 :
1. 碰撞检测的回调函数参数 araiber , 它可以获取碰撞的两个 shape 和 body, 但是无法确定A 和 B 分别是哪个对象 , 需要自己判断 .
2. 在碰撞移除处理时 , 需要调用指定函数addPostStepCallback 在下一帧进行处理 , 处理方式是 移除body , shape , 物理精灵自身.
space.removeBody(body);
space.removeShape(shape);
sp.removeFromParent();
3. 移除处理的时候 容易出现A物体 碰撞 两个B物体 , A物体调用两次移除 , 第二次移除的时候会报错 , 所以我的处理是用try - catch方法 .
4. 碰撞组 和 层的关系 , 同层之间才会发生碰撞 , 不同层没有碰撞 , 优先级高于组 , 不同组之间会发生碰撞 .
5. walls数组成员的最后一个参数是墙的厚度 , 很重要 , 没有厚度的墙体容易被穿透.(强够厚,就不怕穿透)
完整碰撞处理代码 :
Begin: function(araiber, space)
{
var shapeA = araiber.getA();
var shapeB = araiber.getB();
var bodyA = araiber.body_a;
var bodyB = araiber.body_b;
var bullet_shape;
var enemy_shape;
// 假设1001是子弹 1002是敌人
// 判断shapeA的碰撞类型如果是1001 shapeA就是子弹 shapeB就是敌人
if(shapeA.collision_type == 1001)
{
bullet_shape = shapeA;
enemy_shape = shapeB;
}
else
{
bullet_shape = shapeB;
enemy_shape = shapeA;
}
var bullet_body;
var enemy_body;
//body的_type属性不是自带的, 在创建body的时候添加上去的.
if(bodyA._type == 1001)
{
bullet_body = bodyA
enemy_body = bodyB
}
else
{
bullet_body = bodyB
enemy_body = bodyA
}
// 移除处理需要调用特定函数 addPostStepCallback 意为在下一帧处理 .
space.addPostStepCallback(
function(){
try
{
space.removeBody(bullet_body);
space.removeShape(bullet_shape);
}
catch (error)
{
cc.log("已经被移除了")
}
for(var i = 0; i < gameLayer.bullet.length; i++)
{
if(gameLayer.bullet[i].getBody() == bullet_body)
{
cc.log("找到子弹 消除")
gameLayer.bullet[i].removeFromParent();
gameLayer.bullet.splice(i,1);
}
}
for(var i = 0; i < gameLayer.enemy.length; i++)
{
if(gameLayer.enemy[i].getBody() == enemy_body)
{
cc.log("找到敌人 - 掉血")
gameLayer.enemy[i].hp--
cc.log(gameLayer.enemy[i].hp)
if(gameLayer.enemy[i].hp <= 0)
{
space.removeBody(enemy_body);
space.removeShape(enemy_shape);
gameLayer.enemy[i].removeFromParent();
gameLayer.enemy.splice(i,1);
}
}
}
}.bind(this));
}