~~~~我的生活,我的点点滴滴!!
http://www.cocos2d-x.org/wiki/Physics
上面这个链接是官方的,我随便翻译了一下,其实大意就是:cocos2dx 3.0(只能确定3.0,不知道后面版本有没有添加)中有两种物理
引擎Box2D与chipmunk,但是官方只封装了chipmunk,并没有封装Box2D,也就是说如果使用Box2D的话,需要直接操作Box2D的API,有
好有坏!简单的看看下面的新特性:
(1) 物理世界被融入到Scene中,即当创建一个场景时,就可以指定这个场景是否使用物理引擎。
(2) Node自带body属性,也就是sprite自带body属性。
(3) Cocos2d-x 3.0对物理引擎的Body(PhysicsBody),Shape(PhysicsShape),Contact(PhysicsContact),Joint (PhysicsJoint),
World(PhysicsWorld)进行了封装抽象,使用更简单。
(4) 更简单的碰撞检测监听EventListenerPhysicsContact。
上面所有的里面都是用使用chipmunk引擎,不管你怎么切换宏。
我本意是学Box2D的物理引擎,目地也就是在cocos2dx中使用物理引擎,这里我简单的把官方的对于cocos2dx使用的物理引擎的方法生
搬过来,但并不打乱我们继续学习cocos2dx中使用Box2D的节奏。
我们来创建一个带物理世界的场景scene,并传递给child layer。
在HelloWorldScene.h中添加如下代码:
private: PhysicsWorld* m_world; public: void setPhyWorld(PhysicsWorld* world){ m_world = world;}
auto scene = Scene::createWithPhysics(); scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL); auto layer = HelloWorld::create(); layer->setPhyWorld(scene->getPhysicsWorld());
1.1、用Scene新的静态工厂方法createWithPhysics()来创建物理世界的scene。
1.2、Scene的getPhysicsWorld()方法用来获取PhysicsWorld实例。
1.3、PhysicsWorld的setDebugDrawMask()方法,在调试物理引擎中是很有用的,它把物理世界中不可见的shape,joint,contact可视
化。当调试结束,游戏发布的时候,你需要把这个debug开关关闭。
1.4、通过setPhyWorld()方法来传递PhysicsWorld给ChildLayer。一个scene只有一个PhysicsWorld,其下的所有layer共用一个PhysicsWorld实例。
我们知道物理世界中,所有物体受重力的影响。物理引擎提供staticShape创建一个不受重力影响的形状,在Cocos2d-x 2.0中,我们需要了
解物理引擎的staticShape相关的各种参数来完成边界设置。在3.0中,PhysicsShape属于Node的一个属性,要设置PhysicsWorld的属性,都
需要通过一个Node实例来中介传达。下面的代码展示如何创建一个围绕屏幕四周的物理边界:
Size visibleSize = Director::getInstance()->getVisibleSize(); auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3); auto edgeNode = Node::create(); edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2)); edgeNode->setPhysicsBody(body); scene->addChild(edgeNode);
2.1、PhysicsBody包含很多工厂方法,createEdgeBox创建一个矩形边界,参数含义依次是:
1、 矩形区域大小,这里设置为visibleSize。
2、 设置材质,可选参数,默认为PHYSICSBODY_MATERIAL_DEFAULT。
3、 边线宽度,可选参数,默认为1。
2.2、然后我们创建一个Node,把刚才创建的body附加到Node上,并设置好Node的position为屏幕中心点。
2.3、最后,把Node添加到scene。
2.4、Node的addChild方法,在3.0中,有对物理body做处理,它会自动把node的body设置到scene的PhysicsWorld上去。
2.6、PhysicsBody中的工程方法,针对参数设置的body大小,会自动创建对应的PhysicsBody和一个PhysicsShape,
这也是通常情况下,直接使用物理引擎创建一个body需要做的事情。3.0的Physics integration极大的简化
了使用物理引擎的代码量。
在3.0中创建一个受重力作用的sprite其实不难。示例代码如下:
void HelloWorld::addNewSpriteAtPosition(Point p) { auto sprite = Sprite::create("circle.png"); sprite->setTag(1); auto body = PhysicsBody::createCircle(sprite->getContentSize().width / 2); sprite->setPhysicsBody(body); sprite->setPosition(p); this->addChild(sprite); }
cocos2d-x中,事件派发机制做了重构,所有事件均有事件派发器统一管理。物理引擎的碰撞事件也不例外,下面的代码注册碰撞begin回调函数:
auto contactListener = EventListenerPhysicsContact::create(); contactListener->onContactBegin = CC_CALLBACK_2(HelloWorld::onContactBegin, this); _eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
4.1、碰撞检测的所有事件由EventListenerPhysicsContact来监听,创建一个实例,然后设置它的onContactBegin回调函数,CC_CALLBACK_2是
Cocos2d-x 3.0使用C++ 11的回调函数指针转换助手函数,由于onContactBegin回调有两个参数,所有这里使用CC_CALLBACK_2来做转换。
4.2、_eventDispatcher是基类Node的成员,Layer初始化后就可直接使用。
你还可以使用EventListenerPhysicsContactWithBodies, EventListenerPhysicsContactWithShapes, EventListenerPhysicsContactWithGroup
来监听你感兴趣的两个物体、两个形状,或者某组物体的碰撞事件,但是要注意设置物体碰撞相关的mask值(下面会详细说明),因为物体碰撞
事件在默认情况下是不接收的,即使你创建了相应的EventListener。
PhysicsBody碰撞相关的mask设置和group设置跟Box2D的设置是一致的。
mask设置分为三种:
CategoryBitmask->表示“我是谁”;
ContactTestBitmask-->表示“在我和谁发生碰撞通知我”;
CollisionBitmask-->表示”我和谁发生碰撞“;
如果a->CategoryBitmask & b->CollisionBitmask等于0或者b->CategoryBitmask & a->CollisionBitmask等于0,不发生碰撞,可以看到物体穿越
另一个物体.你可以通过相关的get/set接口来获得或者设置他们。他们是通过逻辑与来进行测试的,当一个物体的CategoryBitmask跟另一个物体的
ContactTestBitmask的逻辑与测试结果不为零时,将会发送相应的事件,否则不发送。而当一个物体的CategoryBitmask跟另一个物体的
CollisionBitmask的逻辑与测试结果不为零时,将会发生碰撞,否则不发生碰撞。注意,在默认情况下CategoryBitmask的值为0xFFFFFFFF,
ContactTestBitmask的值为0x00000000,CollisionBitmask的值为0xFFFFFFFF,也就是说默认情况下所有物体都会发生碰撞但不发送通知。
另一个碰撞相关的设置是group(组),当它大于零时,同组的物体将发生碰撞,当它小于零时,同组的物体不碰撞。注意,当group不为零时,
他将忽略mask的碰撞设置(是否通知的设置依然有效)。在EventListenerPhysicsContact里有四个碰撞回调函数,他们分别是onContactBegin,
onContactPreSolve,onContactPostSolve和onContactSeperate。在碰撞刚发生时,onContactBegin会被调用,并且在此次碰撞中只会被调用
一次。你可以通过返回true或者false来决定物体是否发生碰撞。你可以通过PhysicsContact::setData()来保存自己的数据以便用于后续的碰
撞处理。需要注意的是,当onContactBegin返回flase时,onContactPreSolve和onContactPostSolve将不会被调用,但onContactSeperate必
定会被调用。
onContactPreSolve发生在碰撞的每个step,你可以通过调用PhysicsContactPreSolve的设置函数来改变碰撞处理的一些参数设定,比如弹力,阻力等。
同样你可以通过返回true或者false来决定物体是否发生碰撞。你还可以通过调用PhysicsContactPreSolve::ignore()来跳过后续的onContactPreSolve
和onContactPostSolve回调事件通知(默认返回true)。onContactPostSolve发生在碰撞计算完毕的每个step,你可以在此做一些碰撞的后续处理,比
如摧毁某个物体等。onContactSeperate发生在碰撞结束两物体分离时,同样只会被调用一次。它跟onContactBegin必定是成对出现的,所以你可以在此
摧毁你之前通过PhysicsContact::setData()设置的用户数据。