例項介紹Cocos2d-x中Box2D物理引擎:碰撞檢測
阿新 • • 發佈:2019-02-16
在Box2D中碰撞事件通過實現b2ContactListener類函式實現,b2ContactListener是Box2D提供的抽象類,它的抽象函式:
virtual void BeginContact(b2Contact* contact)。兩個物體開始接觸時會響應,但只調用一次。
virtual void EndContact(b2Contact* contact)。分離時響應。但只調用一次。
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)。持續接觸時響應,它會被多次呼叫。
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)。持續接觸時響應,呼叫完preSolve後呼叫。
下面通過將12.2.3一節的例項採用Box2D技術重構,瞭解一下Box2d物理引擎中如何檢測碰撞。
首先我們需要在工程中新增一個新類。使用Visual Studio 2012中新增一個新類,需要分別新增C++原始檔和標頭檔案。具體操作,如圖所示,右鍵點選工程HelloBox2D下的Classes資料夾,在右鍵選單中選擇,“新增”→ “新專案”。彈出如後面的圖所示新增新項對話方塊,我們在對話方塊中選擇檔案的種類,在“名稱”中輸入檔名ContactListener,然後點選“新增”按鈕新增檔案。
Visual Studio 2012中新增新類
新增新項對話方塊新增完成新類ContactListener,我們還需要修改它的程式碼,ContactListener.h檔案程式碼如下:
上述程式碼第①行是實現碰撞事件BeginContact函式,第②程式碼和第③行程式碼是獲得接觸雙方物體物件。第④程式碼和第⑤行程式碼是從物體物件的UserData屬性中確定精靈物件,UserData屬性可以放置任何物件,這裡我們能夠通過bodyA->GetUserData()語句取得精靈,那是因為在定義物體的時候通過body->SetUserData(sprite)語句,將精靈放入到物體的UserData屬性。第⑥行程式碼是判斷兩個精靈是否存在。
程式碼第⑦行是實現碰撞事件EndContact函式,函式的實現與BeginContact函式類似。第⑧和第⑨行程式碼是實現碰撞事件PreSolve和PostSolve函式,這兩個函式通常用的不多。
我們還需要在要監聽事件的層中,新增相關碰撞檢測程式碼。在HelloWorld.h的程式碼如下:
上述程式碼第①行是引入標頭檔案ContactListener.h。第②行程式碼是宣告ContactListener型別的成員變數contactListener。
我們還需要修改HelloWorld.cpp中的HelloWorld::initPhysics()程式碼如下:
函式中的contactListener = new ContactListener()語句是建立ContactListener物件,採用了new關鍵字分配記憶體,建立成員變數contactListener,需要自己釋放contactListener物件。這些釋放過程是在解構函式中進行,它的解構函式程式碼如下:
HelloWorld::~HelloWorld()
{
CC_SAFE_DELETE(world);
CC_SAFE_DELETE(contactListener);
}
virtual void BeginContact(b2Contact* contact)。兩個物體開始接觸時會響應,但只調用一次。
virtual void EndContact(b2Contact* contact)。分離時響應。但只調用一次。
virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)。持續接觸時響應,它會被多次呼叫。
virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)。持續接觸時響應,呼叫完preSolve後呼叫。
下面通過將12.2.3一節的例項採用Box2D技術重構,瞭解一下Box2d物理引擎中如何檢測碰撞。
首先我們需要在工程中新增一個新類。使用Visual Studio 2012中新增一個新類,需要分別新增C++原始檔和標頭檔案。具體操作,如圖所示,右鍵點選工程HelloBox2D下的Classes資料夾,在右鍵選單中選擇,“新增”→ “新專案”。彈出如後面的圖所示新增新項對話方塊,我們在對話方塊中選擇檔案的種類,在“名稱”中輸入檔名ContactListener,然後點選“新增”按鈕新增檔案。
Visual Studio 2012中新增新類
新增新項對話方塊新增完成新類ContactListener,我們還需要修改它的程式碼,ContactListener.h檔案程式碼如下:
#include "cocos2d.h" #include "Box2D/Box2D.h" USING_NS_CC; class ContactListener : public b2ContactListener { private: //兩個物體開始接觸時會響應 virtual void BeginContact(b2Contact* contact); //持續接觸時響應 virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold); //持續接觸時響應,呼叫完preSolve後呼叫 virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse); //分離時響應 virtual void EndContact(b2Contact* contact); }; 在標頭檔案中需要引入cocos2d.h和Box2D/Box2D.h標頭檔案,否則會有編譯錯誤。ContactListener採用共有繼承b2ContactListener。 ContactListener.cpp檔案程式碼如下: #include "ContactListener.h" void ContactListener::BeginContact(b2Contact* contact) ① { log("BeginContact"); b2Body* bodyA = contact->GetFixtureA()->GetBody(); ② b2Body* bodyB = contact->GetFixtureB()->GetBody(); ③ auto spriteA = (Sprite*)bodyA->GetUserData(); ④ auto spriteB = (Sprite*)bodyB->GetUserData(); ⑤ if (spriteA != nullptr && spriteB != nullptr) ⑥ { spriteA->setColor(Color3B::YELLOW); spriteB->setColor(Color3B::YELLOW); } } void ContactListener::EndContact(b2Contact* contact) ⑦ { log("EndContact"); b2Body* bodyA = contact->GetFixtureA()->GetBody(); b2Body* bodyB = contact->GetFixtureB()->GetBody(); auto spriteA = (Sprite*)bodyA->GetUserData(); auto spriteB = (Sprite*)bodyB->GetUserData(); if (spriteA != nullptr && spriteB != nullptr) { spriteA->setColor(Color3B::WHITE); spriteB->setColor(Color3B::WHITE); } } void ContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) ⑧ { log("PreSolve"); } void ContactListener::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) ⑨ { log("PostSolve"); }
上述程式碼第①行是實現碰撞事件BeginContact函式,第②程式碼和第③行程式碼是獲得接觸雙方物體物件。第④程式碼和第⑤行程式碼是從物體物件的UserData屬性中確定精靈物件,UserData屬性可以放置任何物件,這裡我們能夠通過bodyA->GetUserData()語句取得精靈,那是因為在定義物體的時候通過body->SetUserData(sprite)語句,將精靈放入到物體的UserData屬性。第⑥行程式碼是判斷兩個精靈是否存在。
程式碼第⑦行是實現碰撞事件EndContact函式,函式的實現與BeginContact函式類似。第⑧和第⑨行程式碼是實現碰撞事件PreSolve和PostSolve函式,這兩個函式通常用的不多。
我們還需要在要監聽事件的層中,新增相關碰撞檢測程式碼。在HelloWorld.h的程式碼如下:
#ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" #include "Box2D/Box2D.h" #include "ContactListener.h" ① #define PTM_RATIO 32 class HelloWorld : public cocos2d::Layer { b2World* world; ContactListener* contactListener; ② public: ~HelloWorld(); static cocos2d::Scene* createScene(); virtual bool init(); virtual void update(float dt); virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event); CREATE_FUNC(HelloWorld); void initPhysics(); void addNewSpriteAtPosition(cocos2d::Vec2 p); }; #endif // __HELLOWORLD_SCENE_H__
上述程式碼第①行是引入標頭檔案ContactListener.h。第②行程式碼是宣告ContactListener型別的成員變數contactListener。
我們還需要修改HelloWorld.cpp中的HelloWorld::initPhysics()程式碼如下:
void HelloWorld::initPhysics()
{
… …
contactListener = new ContactListener();
world->SetContactListener(contactListener);
… …
}
函式中的contactListener = new ContactListener()語句是建立ContactListener物件,採用了new關鍵字分配記憶體,建立成員變數contactListener,需要自己釋放contactListener物件。這些釋放過程是在解構函式中進行,它的解構函式程式碼如下:
HelloWorld::~HelloWorld()
{
CC_SAFE_DELETE(world);
CC_SAFE_DELETE(contactListener);
}
使用CC_SAFE_DELETE(contactListener)安全釋放contactListener成員變數的記憶體。