1. 程式人生 > >例項介紹Cocos2d-x中Box2D物理引擎:碰撞檢測

例項介紹Cocos2d-x中Box2D物理引擎:碰撞檢測

在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檔案程式碼如下:
#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成員變數的記憶體。