cocos2dx-3.0 中的關於物理引擎Box2D與chipmunk
~~~~我的生活,我的點點滴滴!!
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
1、建立帶物理世界的場景scene
我們來建立一個帶物理世界的場景scene,並傳遞給child layer。
在HelloWorldScene.h中新增如下程式碼:
private:
PhysicsWorld* m_world;
public:
void setPhyWorld(PhysicsWorld* world){ m_world = world;}
在HelloWorldScene.cpp中的createScene方法中新增如下程式碼:
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例項。
2、建立物理邊界
我們知道物理世界中,所有物體受重力的影響。物理引擎提供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、建立受重力作用 sprite
在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);
}
首先建立一個sprite,然後用PhysicsBody::createCircle建立一個圓形的body附加在sprite上。整個過程和之前建立邊界的過程是一致的。
4、碰撞檢測
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()設定的使用者資料。