Box2D 和 Chipmunk2D 是以往 cocos2d 中两款常用的物理引擎,这两个物理引擎精确的说是刚体物理仿真库,这里的物理就是刚体动力学。而我们在谈到这物理引擎的时候,经常会听到刚体(Rigid body)。那什么是刚体?刚体就是在任何外力的作用下,体积和形状都不发生改变的物体。动力学又是什么?动力学是计算刚体在受力作用下随时间移动并相互作用的一个过程。Box2D和Chipmunk2D两个引擎的建构理论基础当然基于此。
- PhysicsWorld 是 cocos2d 封装的一个用于模拟真实“物理世界”的类。
- 一个 PhysicsWorld 对象,用于模拟物理碰撞和其他物理行为。你不需要直接创建PhysicsWorld对象;相反,你可以从一个场景对象获取它。
通常,我们通过Scene::create()创建的场景不是基于物理世界的场景。而使用Scene::createWithPhysics()创建场景时,那么这样创建的场景将是基于物理世界的。
void setGravity(const Vec2 &gravity);
void setSubsteps(int steps);
void setSpeed(float speed);
PhysicsBody * getBody(int tag);
const Vector< PhysicsBody * > & getAllBodies();
virtual void removeBody(PhysicsBody *body);
virtual void removeBody(int tag);
Vector< PhysicsShape * > getShapes (const Vec2 &point);
PhysicsShape * getShape(const Vec2 &point);
void setDebugDrawMask(int mask);
DEBUGDRAW_NONE :什么都不绘制
DEBUGDRAW_SHAPE :绘制形状
DEBUGDRAW_JOINT :绘制关节
DEBUGDRAW_CONTACT :绘制碰撞
DEBUGDRAW_ALL : 都绘制
void setAutoStep(bool autoStep);
// 如果设置False的话,那么setSpeed和setSubsteps,以及setUpdateRate函数都不能正常工作了
// 如果为false,那么刚体将停止不动。
_gravity(Vec2(0.0f, -98.0f)) : 98cm/s2
, _speed(1.0f)
, _updateRate(1)
, _updateRateCount(0)
, _updateTime(0.0f)
, _substeps(1)
, _cpSpace(nullptr)
, _updateBodyTransform(false)
, _scene(nullptr)
, _autoStep(true)
, _debugDraw(nullptr)
, _debugDrawMask(DEBUGDRAW_NONE)
, _eventDispatcher(nullptr)
创建的物理世界,默认是向下为98个像素每秒的加速度,且对于物理世界中的刚体是默认自动执行物理效果。可以通过setGravity()改变物理世界的重力值,setAutoStep()设置是否对刚体执行物理迭代。
通常使用方法:
// 创建基于物理世界的场景
Scene * scene = Scene::createWithPhysics();
// 设置物理世界的重力值
scene->getPhysicsWorld()->setGravity(Vec2(0, 10));
//创建一个物理刚体
PhysicsBody * body = PhysicsBody::create();
// 设置刚体的运动速度
body->setVelocity(Vec2(0,500));
// 设置该刚体是否受物理世界的重力值影响
body->setGravityEnable(true);
Sprite * sp = Sprite("xxx.png");
scene->addChild(sp);
// 让精灵对象绑定刚体:该精灵对象受物理世界影响
sp->setPhysicsBody(body);
刚体:在任何外力的作用下,体积和形状都不发生改变的物体。
: _world(nullptr)
, _cpBody(nullptr)
, _dynamic(true)
, _rotationEnabled(true)
, _gravityEnabled(true) //默认是受物理世界的重力值影响的,且刚体的初始速度是0
, _massDefault(true)
, _momentDefault(true)
, _mass(MASS_DEFAULT)
, _area(0.0f)
, _density(0.0f)
, _moment(MOMENT_DEFAULT)
, _isDamping(false)
, _linearDamping(0.0f)
, _angularDamping(0.0f)
, _tag(0)
, _rotationOffset(0)
, _recordedRotation(0.0f)
, _recordedAngle(0.0)
, _massSetByUser(false)
, _momentSetByUser(false)
, _recordScaleX(1.f)
, _recordScaleY(1.f)
void setVelocity(const Vec2 &velocity);
void setVelocityLimit(float limit);
void setAngularVelocity(float velocity);
void setAngularVelocityLimit(float limit);
void applyImpulse(const Vect &impulse);
PhysicsWorld * getWorld();
刚体的掩码
CategoryBitmask 类型掩码 : 默认值#ffffffff
每一个场景中的物理刚体被会分配32个种类,每一个对应一个掩码值。你可以在游戏中自定义掩码值。与collisionBitMask和contactTestBitMask属性结合,你可以定义哪些物理刚体以及什么时候会互相影响。
ContactTestBitmask 接触分组掩码 : 默认值0
setCollisionBitmask 碰撞分组掩码 : 默认值#ffffffff
例子
1 接触有效
只要接触了就会调用设置的回掉函数。
/* 物理边界 */
Size sz = Director::getInstance()->getVisibleSize();
/* 创建 多边形形状 的物理边界 */
Vec2 pos1 = Vec2(-sz.width / 2, 0);
Vec2 pos2 = Vec2(sz.width / 2, sz.height / 2);
Vec2 pos3 = Vec2(sz.width / 2, -sz.height / 2);
Vec2 posVec[3] = { pos1, pos2, pos3 };
PhysicsBody * body = PhysicsBody::createEdgePolygon(posVec, 3);
body->setCollisionBitmask(3);
/* 物理世界边界 */
Node * node = Node::create();
node->setPosition(Point(sz.width / 2, sz.height / 2));
node->setPhysicsBody(body);
scene->addChild(node);
Size sz = Director::getInstance()->getVisibleSize();
Vec2 center = Vec2(sz.width / 2, sz.height / 2);
Sprite * sp1 = Sprite::create("CloseNormal.png");
sp1->setPosition(sz.width / 4, sz.height/2);
PhysicsBody * body1 = PhysicsBody::createCircle(sp1->getContentSize().width / 2);
body1->setVelocity(Vec2(0, -98));
body1->setGravityEnable(true);
/* 设置刚体的掩码属性 */
body1->setContactTestBitmask(1);
sp1->setPhysicsBody(body1);
this->addChild(sp1);
Sprite * sp2 = Sprite::create("CloseNormal.png");
sp2->setPosition(sz.width * 3 / 4, sz.height/2);
PhysicsBody * body2 = PhysicsBody::createCircle(sp2->getContentSize().width / 2);
body2->setVelocity(Vec2(0, 90));
sp2->setPhysicsBody(body2);
this->addChild(sp2);
body2->setContactTestBitmask(3); // 因为 1 与 3 不为0,所以body1和body2接触后将触发回掉函数
/* 两个刚体接触的第一次就触发 */
auto listener = EventListenerPhysicsContactWithBodies::create(body2, body1);
listener->onContactBegin = [sp1](PhysicsContact& contact){ log("yes...."); sp1->setVisible(false); //接触后,一个球消失 sp1->removeFromParent(); return true; }; Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
2 碰撞有效
只要碰撞了就会调用回调函数。
/* 物理边界 */
Size sz = Director::getInstance()->getVisibleSize();
/* 创建 多边形形状 的物理边界 */
Vec2 pos1 = Vec2(-sz.width / 2, 0);
Vec2 pos2 = Vec2(sz.width / 2, sz.height / 2);
Vec2 pos3 = Vec2(sz.width / 2, -sz.height / 2);
Vec2 posVec[3] = { pos1, pos2, pos3 };
PhysicsBody * body = PhysicsBody::createEdgePolygon(posVec, 3);
body->setCollisionBitmask(3); //边界的碰撞掩码是3
/* 物理世界边界 */
Node * node = Node::create();
node->setPosition(Point(sz.width / 2, sz.height / 2));
node->setPhysicsBody(body);
scene->addChild(node);
Size sz = Director::getInstance()->getVisibleSize();
Vec2 center = Vec2(sz.width / 2, sz.height / 2);
Sprite * sp1 = Sprite::create("CloseNormal.png");
sp1->setPosition(sz.width / 4, sz.height/2);
PhysicsBody * body1 = PhysicsBody::createCircle(sp1->getContentSize().width / 2);
body1->setVelocity(Vec2(0, -98));
body1->setGravityEnable(true);
/* 设置刚体的掩码属性 */
body1->setCollisionBitmask(0); // 因为3 与 0 为0,所以body和body1即使碰撞了,也不会调用碰撞回调函数,也不会产生碰撞效果。
sp1->setPhysicsBody(body1);
this->addChild(sp1);