Cocos2d-x采用了Box2D物理引擎和Chipmunk物理引擎来模拟真实的物理世界。Box2D几乎能模拟所有的物理效果,而chipmunk则是个更轻量的引擎等。而在Cocos2d-x3.2版本中默认采用Chipmunk,所以我就以Chip-munk来讲解。并且为了简化物理引擎和Cocos2d-x的交接,Cocos2d-x直接提供函数来设置物体参数,不需要我们采用Chip-munk原生的函数来设置,这大大简化的代码的编写。
Chipmunk是采用C语言编写的2D刚体物理仿真库,包含4种基本的对象类型,分别是:
空间:空间是Chipmunk中模拟对象的容器。你将刚体、形状、关节添加进入一个空间,然后将空间作为一个整体进行更新。空间控制着所有的刚体、形状和约束之间的相互作用。
刚体:一个刚体容纳着一个对象的物理属性(如质量、位置、角度、速度等)。默认情况下,它并不具有任何形状,直到你为它添加一个或者多个碰撞形状进去。如果你以前做过物理粒子,你会发现它们的不同之处是刚体可以旋转。在游戏中,通常刚体都是和一个精灵一一对应关联的。你应该构建你的游戏以便可以使用刚体的位置和角度来绘制你的精灵。
碰撞形状:因为形状与刚体相关联,所以你可以为一个刚体定义形状。为了定义一个复杂的形状,你可以给刚体绑定足够多的形状。形状包含着一个对象的表面属性如摩擦力、弹性等。
约束/关节:约束和关节被用来描述刚体之间是如何关联的
人们经常对Chipmunk中的刚体和碰撞形状以及两者与精灵之间的关系产生混淆。精灵是对象的可视化表现,而碰撞形状是定义对象应该如何碰撞的不可见的属性。精灵和碰撞形状两者的位置和角度都是由刚体的运动控制的。通常你应该创建一个游戏对象类型,把这些东西捆绑在一起。
在Cocos2d-x3.2中,物理世界被融入到Scene中,即当创建一个场景时,就可以指定这个场景是否使用物理引擎。
categoryBitmask:
分类掩码,定义了物体属于哪个分类。场景中的每个物理刚体可以被赋值一个多达32位的值(因为categoryBitmask为int型),每个对应32位掩码中的每一位,你在你的游戏中定义掩码值。结合collisionBitMask和contactTestBitMask属性, 你可以定义哪些物理刚体相互作用并且你的游戏何时接受这些相互作用的通知。默认值为0xFFFFFFFF(所有位都被设置)。
contactTestBitmask:
接触测试掩码,定义哪些刚体分类可以与本刚体产生相互作用的通知。当两个刚体在同一个空间,即物理世界中,每个刚体的分类掩码会和其他刚体的接触测试掩码进行逻辑与的运算。如果任意一个比较结果为非零值,产生一个PhysicsContact对象并且传递到物理世界协议中,这里协议指我们的监听器对应的回调函数。 为了最好的性能,仅设置你感兴趣的接触测试掩码中的位,也就是说通过设置接触测试掩码,你可以决定发生碰撞后,回调函数是否有响应。默认值为0x00000000(所有位都被清除)。
collisionBitmask:
碰撞掩码,定义了哪些物理刚体分类可以和这个物理刚体发生碰撞。当两个物理刚体相互接触时,可能发生碰撞。这个刚体的碰撞掩码和另一个刚体的分类掩码进行逻辑与运算比较。如果结果是一个非零值,这个刚体会发生碰撞。每个刚体独立选择接受与哪个刚体发生碰撞。例如,你可以使用此掩码来忽略那些对于本刚体的速度有影响的刚体碰撞,也就是说你可以使用此掩码使得本刚体与某些刚体碰撞不会对本刚体产生影响。默认值为0xFFFFFFFF(所有位都被设置)。
从上面三个掩码的说明中,我们可以做一个小结。假设刚体A的接触测试掩码和碰撞掩码已知,刚体B的分类掩码决定了能否和A进行碰撞和在碰撞的前提下能否发出PhysicsContact对象触发回调函数。如果B的分类掩码与A的碰撞掩码做逻辑与运算的结果为0,则不会发生碰撞,因此也不会继续和A的接触测试掩码进行逻辑与运算。如果B的分类掩码与A的碰撞掩码做逻辑与运算的结果非0,则发生碰撞,并且B的分类掩码继续与A的接触测试掩码做逻辑与运算,如果结果非0,则发出PhysicsContact对象触发回调函数。
Chipmunk是采用C语言编写的2D刚体物理仿真库,包含4种基本的对象类型,分别是:
空间:空间是Chipmunk中模拟对象的容器。你将刚体、形状、关节添加进入一个空间,然后将空间作为一个整体进行更新。空间控制着所有的刚体、形状和约束之间的相互作用。
刚体:一个刚体容纳着一个对象的物理属性(如质量、位置、角度、速度等)。默认情况下,它并不具有任何形状,直到你为它添加一个或者多个碰撞形状进去。如果你以前做过物理粒子,你会发现它们的不同之处是刚体可以旋转。在游戏中,通常刚体都是和一个精灵一一对应关联的。你应该构建你的游戏以便可以使用刚体的位置和角度来绘制你的精灵。
碰撞形状:因为形状与刚体相关联,所以你可以为一个刚体定义形状。为了定义一个复杂的形状,你可以给刚体绑定足够多的形状。形状包含着一个对象的表面属性如摩擦力、弹性等。
约束/关节:约束和关节被用来描述刚体之间是如何关联的
人们经常对Chipmunk中的刚体和碰撞形状以及两者与精灵之间的关系产生混淆。精灵是对象的可视化表现,而碰撞形状是定义对象应该如何碰撞的不可见的属性。精灵和碰撞形状两者的位置和角度都是由刚体的运动控制的。通常你应该创建一个游戏对象类型,把这些东西捆绑在一起。
在Cocos2d-x3.2中,物理世界被融入到Scene中,即当创建一个场景时,就可以指定这个场景是否使用物理引擎。
sprite自带body属性,直接设置body,而形状、约束等属性添加到body中即可。碰撞的检测通过事件分发器来监控,所以你首先要创建监听器--EventListenerPhysicsContact,再把其添加到分发器中。
- #include "HelloWorldScene.h"
- USING_NS_CC;
- //物理引擎标识
- int ball1Mask = 1 << 0;//球1
- int ball2Mask = 1 << 1;//球2
- int wallMask = 1 << 2;//地面
- Scene* HelloWorld::createScene()
- {
- //创建有物理空间的场景
- Scene* scene=Scene::createWithPhysics();
- //设置Debug模式,你会看到物体的表面被线条包围,主要为了在调试中更容易地观察
- scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
- HelloWorld* layer=HelloWorld::create();
- //把空间保持我们创建的层中,就是上面所说m_world的作用,方便后面设置空间的参数
- layer->setPhyWorld(scene->getPhysicsWorld());
- scene->addChild(layer);
- return scene;
- }
- // on "init" you need to initialize your instance
- void HelloWorld::onEnter()
- {
- Layer::onEnter();
- //添加监听器
- auto contactListener=EventListenerPhysicsContact::create();
- //设置监听器的碰撞开始函数
- contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
- //添加到事件分发器中
- _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
- }
- bool HelloWorld::init()
- {
- //
- // 1. super init first
- if ( !Layer::init() )
- {
- return false;
- }
- Size visibleSize=Director::getInstance()->getVisibleSize();
- Point origin=Director::getInstance()->getVisibleOrigin();
- ballOne=Sprite::create();
- ballOne->setContentSize(Size(50, 50));
- ballOne->setPosition(visibleSize.width/2,visibleSize.height/2);
- //创建物体,并且物体的形状为圆形,第一参数为半径,第二个参数为物体材质
- //第三个参数为边的厚度,就是在Debug模式下看到的物体外面线条的厚度,默认为0
- PhysicsBody* ballBodyOne=PhysicsBody::createCircle(ballOne->getContentSize().width/2,PHYSICSBODY_MATERIAL_DEFAULT, Vec2(1,1));
- //
- //ballBodyOne->setCategoryBitmask(1);
- //是否设置物体为静态
- //ballBody->setDynamic(false);
- //设置物体的恢复力
- ballBodyOne->getShape(0)->setRestitution(1.0f);
- //设置物体的摩擦力
- ballBodyOne->getShape(0)->setFriction(0.0f);
- //设置物体密度
- ballBodyOne->getShape(0)->setDensity(1.0f);
- //设置质量
- //ballBodyOne->getShape(0)->setMass(5000);
- //设置物体是否受重力系数影响
- ballBodyOne->setGravityEnable(false);
- //设置物体的冲力
- Vect force=Vect(500000.0f, 500000.0f);
- ballBodyOne->applyImpulse(force);
- //把物体添加到精灵中
- ballOne->setPhysicsBody(ballBodyOne);
- //设置标志
- ballOne->setTag(1);
- this->addChild(ballOne);
- //设置第二个球
- ballTwo=Sprite::create();
- ballTwo->setContentSize(Size(100, 100));
- ballTwo->setPosition(visibleSize.width/3,visibleSize.height/3);
- PhysicsBody* ballBodyTwo=PhysicsBody::createCircle(ballOne->getContentSize().width/2,PHYSICSBODY_MATERIAL_DEFAULT);
- //是否设置物体为静态
- //ballBodyTwo->setDynamic(false);
- ballBodyTwo->getShape(0)->setRestitution(1.0f);
- ballBodyTwo->getShape(0)->setFriction(0.0f);
- ballBodyTwo->getShape(0)->setDensity(1.0f);
- ballBodyTwo->setGravityEnable(false);
- force=Vect(-500000.0f, -500000.0f);
- ballBodyTwo->applyImpulse(force);
- ballTwo->setPhysicsBody(ballBodyTwo);
- ballTwo->setTag(2);
- this->addChild(ballTwo);
- //创建一个盒子,用来碰撞
- Sprite* edgeSpace=Sprite::create();
- PhysicsBody* boundBody=PhysicsBody::createEdgeBox(visibleSize,PHYSICSBODY_MATERIAL_DEFAULT,3);
- boundBody->getShape(0)->setFriction(0.0f);
- boundBody->getShape(0)->setRestitution(1.0f);
- edgeSpace->setPhysicsBody(boundBody);
- edgeSpace->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
- this->addChild(edgeSpace);
- edgeSpace->setTag(0);
- ballBodyOne->setCategoryBitmask(ball1Mask);
- ballBodyOne->setCollisionBitmask(wallMask|ball1Mask|ball2Mask);
- ballBodyOne->setContactTestBitmask(wallMask|ball1Mask|ball2Mask);
- ballBodyTwo->setCategoryBitmask(ball2Mask);
- ballBodyTwo->setCollisionBitmask(wallMask|ball1Mask|ball2Mask);
- ballBodyTwo->setContactTestBitmask(wallMask|ball1Mask|ball2Mask);
- boundBody->setCategoryBitmask(wallMask);
- boundBody->setCollisionBitmask(wallMask | ball1Mask|ball2Mask);
- boundBody->setContactTestBitmask(wallMask|ball1Mask|ball2Mask);
- return true;
- }
- bool HelloWorld::onContactBegin(const PhysicsContact& contact)
- {
- if ((contact.getShapeA()->getBody()->getCategoryBitmask() & ball1Mask) == ball1Mask) {
- CCLOG("ball1 touch something");
- }
- if ((contact.getShapeB()->getBody()->getCategoryBitmask() & ball1Mask) == ball1Mask) {
- CCLOG("ball1 touch something");
- }
- if ((contact.getShapeA()->getBody()->getCategoryBitmask() & ball2Mask) == ball2Mask) {
- CCLOG("ball2 touch something");
- }
- if ((contact.getShapeB()->getBody()->getCategoryBitmask() & ball2Mask) == ball2Mask) {
- CCLOG("ball2 touch something");
- }
- return true;
- }
- void HelloWorld::menuCloseCallback(Ref* pSender)
- {
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
- MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
- return;
- #endif
- Director::getInstance()->end();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- exit(0);
- #endif
- }
categoryBitmask:
分类掩码,定义了物体属于哪个分类。场景中的每个物理刚体可以被赋值一个多达32位的值(因为categoryBitmask为int型),每个对应32位掩码中的每一位,你在你的游戏中定义掩码值。结合collisionBitMask和contactTestBitMask属性, 你可以定义哪些物理刚体相互作用并且你的游戏何时接受这些相互作用的通知。默认值为0xFFFFFFFF(所有位都被设置)。
contactTestBitmask:
接触测试掩码,定义哪些刚体分类可以与本刚体产生相互作用的通知。当两个刚体在同一个空间,即物理世界中,每个刚体的分类掩码会和其他刚体的接触测试掩码进行逻辑与的运算。如果任意一个比较结果为非零值,产生一个PhysicsContact对象并且传递到物理世界协议中,这里协议指我们的监听器对应的回调函数。 为了最好的性能,仅设置你感兴趣的接触测试掩码中的位,也就是说通过设置接触测试掩码,你可以决定发生碰撞后,回调函数是否有响应。默认值为0x00000000(所有位都被清除)。
collisionBitmask:
碰撞掩码,定义了哪些物理刚体分类可以和这个物理刚体发生碰撞。当两个物理刚体相互接触时,可能发生碰撞。这个刚体的碰撞掩码和另一个刚体的分类掩码进行逻辑与运算比较。如果结果是一个非零值,这个刚体会发生碰撞。每个刚体独立选择接受与哪个刚体发生碰撞。例如,你可以使用此掩码来忽略那些对于本刚体的速度有影响的刚体碰撞,也就是说你可以使用此掩码使得本刚体与某些刚体碰撞不会对本刚体产生影响。默认值为0xFFFFFFFF(所有位都被设置)。
从上面三个掩码的说明中,我们可以做一个小结。假设刚体A的接触测试掩码和碰撞掩码已知,刚体B的分类掩码决定了能否和A进行碰撞和在碰撞的前提下能否发出PhysicsContact对象触发回调函数。如果B的分类掩码与A的碰撞掩码做逻辑与运算的结果为0,则不会发生碰撞,因此也不会继续和A的接触测试掩码进行逻辑与运算。如果B的分类掩码与A的碰撞掩码做逻辑与运算的结果非0,则发生碰撞,并且B的分类掩码继续与A的接触测试掩码做逻辑与运算,如果结果非0,则发出PhysicsContact对象触发回调函数。