1. 程式人生 > >實例介紹Cocos2d-x中Box2D物理引擎:HelloBox2D

實例介紹Cocos2d-x中Box2D物理引擎:HelloBox2D

pre all align 討論 響應 算法 站點 virtual origin

我們通過一個實例介紹一下。在Cocos2d-x 3.x中使用Box2D物理引擎的開發過程,熟悉這些API的使用。

這個實例執行後的場景如圖所看到的,當場景啟動後,玩家能夠觸摸點擊屏幕,每次觸摸時候。就會在觸摸點生成一個新的精靈,精靈的執行自由落體運動。




技術分享

HelloBox2D實例使用Box2D引擎進行開發過程,如圖12-15所看到的。以下我們就依照這個步驟介紹一下代碼部分。首先看一下看HelloWorldScene.h文件。它的代碼例如以下:

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__


#include "cocos2d.h"
#include "Box2D/Box2D.h"												①


#define PTM_RATIO 32													②


class HelloWorld : public cocos2d::Layer
{
	b2World* world;														③


public:
    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__


上述第①行代碼#include "Box2D/Box2D.h"是引入使用Box2D引擎須要頭文件。第②行代碼#define PTM_RATIO 32是定義宏PTM_RATIO,PTM_RATIO是屏幕上多少像素為1米,32表示屏幕上32像素表示1米,在Box2D中單位使用MKS公制系統,即:長度單位採用米,質量單位採用千克,時間單位採用秒。


代碼第③行world是聲明物理世界b2World成員變量。第④行代碼是遊戲循環函數。第⑤行代碼是觸摸點擊響應函數。第⑥行代碼是聲明初始化物理引擎函數initPhysics。第⑦行是聲明addNewSpriteAtPosition函數,是在觸摸點創建一個精靈對象。

HelloWorldScene.cpp中HelloWorld::init()函數代碼例如以下:
bool HelloWorld::init()
{
	if ( !Layer::init() )
	{
		return false;
	}


	Size visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();


	// 初始化物理引擎
	this->initPhysics();													①


	setTouchEnabled(true);												
	//設置為單點觸摸
	setTouchMode(Touch::DispatchMode::ONE_BY_ONE);							
	//開始遊戲循環
	scheduleUpdate();													②


	return true;
}


上述代碼第①行調用initPhysics()函數初始化物理引擎。第②行代碼scheduleUpdate()是開始遊戲循環,一旦開啟遊戲循環就會回調HelloWorld::update(float dt)函數。
HelloWorldScene.cpp中初始化物理引擎HelloWorld::initPhysics()函數代碼例如以下:
void HelloWorld::initPhysics()
{
	Size s = Director::getInstance()->getVisibleSize();
	
	//重力參數
	b2Vec2 gravity; 														①
	gravity.Set(0.0f, -10.0f); 												②
	//創建世界
	world = new b2World(gravity); 											③
	// 同意物體是否休眠
	world->SetAllowSleeping(true);											④
	// 開啟連續物理測試
	world->SetContinuousPhysics(true);										⑤
	
	//地面物體定義
	b2BodyDef groundBodyDef;											⑥
	//左下角
	groundBodyDef.position.Set(0, 0);										⑦


	//創建地面物體
	b2Body* groundBody = world->CreateBody(&groundBodyDef);						⑧


	//定義一個有邊的形狀
	b2EdgeShape groundBox;												⑨


	// 底部
	groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0));						⑩
	//使用夾具固定形狀到物體上
	groundBody->CreateFixture(&groundBox,0);									?


	// 頂部
	groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), 
					b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO));					
	groundBody->CreateFixture(&groundBox,0);									


	// 左邊
	groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0));							groundBody->CreateFixture(&groundBox,0);									


	// 右邊
	groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO), 
						b2Vec2(s.width/PTM_RATIO,0)); 								
	groundBody->CreateFixture(&groundBox,0);									


}


代碼第①行b2Vec2 gravity是聲明重力變量,b2Vec2是一個二維矢量,它的兩個屬性為浮點數x和y。表示在x軸和y軸方向的矢量。第②行代碼gravity.Set(0.0f, -10.0f)是設置矢量值,當中(0.0f, -10.0f)表示僅僅有重力作用物體。-10.0f表示沿著y軸向下。


第③行代碼world = new b2World(gravity)是創建物理世界b2World對象。這裏採用了new創建物理世界對象。在C++中newkeyword分配內存,釋放內存是deletekeyword。World是成員變量。須要自己釋放成員變量通常是在析構函數中釋放。代碼例如以下:
HelloWorld::~HelloWorld()
{
CC_SAFE_DELETE(world);
}
當中CC_SAFE_DELETE(world)是安全釋放world變量,CC_SAFE_DELETE宏代表安全釋放內存。
第④行代碼world->SetAllowSleeping(true)是同意物體睡眠與否。假設同意休眠,能夠提高物理世界中物體的處理效率。僅僅有在發生碰撞時才喚醒該對象。
第⑤行代碼world->SetContinuousPhysics(true)是開啟連續物理測試[ 開啟連續物理測試。這是由於計算機僅僅能把一段連續的時間分成很多離散的時間點,再對每一個時間點之間的行為進行演算,假設時間點的切割不夠仔細,速度較快的兩個物體碰撞時就可能會產生“穿透”現象,開啟連續物理將啟用特殊的算法來避免該現象。]。
第⑥行代碼是聲明形狀定義(b2BodyDef)變量。第⑦行代碼groundBodyDef.position.Set(0, 0)是設置形狀的位置。第⑧行代碼b2Body* groundBody = world->CreateBody(&groundBodyDef)是通過形狀定義變量groundBodyDef創建地面物體。
第⑨行代碼是聲明一個有邊形狀定義b2EdgeShape變量。第⑩行代碼groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0))是設置有邊形狀的開始位置(0,0)和結束位置(s.width/PTM_RATIO,0)。s.width/PTM_RATIO是將像素換算成米。第?行代碼是使用夾具固定形狀到物體上。

用相似的函數定義頂部、左邊和右邊的物體。
HelloWorldScene.cpp中創建精靈HelloWorld::addNewSpriteAtPosition函數代碼例如以下:

void HelloWorld::addNewSpriteAtPosition(Vec2 p)
{    
	log("Add sprite %0.2f x %02.f",p.x,p.y);


	//創建物理引擎精靈對象
	auto sprite = Sprite::create("BoxA2.png");									①
	sprite->setPosition( Vec2( p.x, p.y) );
	this->addChild(sprite);


	//物體定義
	b2BodyDef bodyDef;													②
	bodyDef.type = b2_dynamicBody;											③
	bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);							④
	b2Body *body = world->CreateBody(&bodyDef);								⑤
	body->SetUserData(sprite);												⑥


	// 定義2米見方的盒子形狀
	b2PolygonShape dynamicBox;											⑦
	dynamicBox.SetAsBox(1, 1);											⑧


	// 夾具定義
	b2FixtureDef fixtureDef;												⑨
	//設置夾具的形狀
	fixtureDef.shape = &dynamicBox;											⑩
	//設置密度
	fixtureDef.density = 1.0f;												?
	//設置摩擦系數
	fixtureDef.friction = 0.3f;												?
	//使用夾具固定形狀到物體上	
	body->CreateFixture(&fixtureDef);										?


}


上述代碼第①行是創建精靈(Sprite)對象,精靈(Sprite)對象與物理引擎物體是沒有關系,我們須要在遊戲循環函數中更新。
代碼第②行是聲明動態物體定義變量,代碼第③行bodyDef.type = b2_dynamicBody是設置物體類型為動態物體。物體分為靜態和動態物體。第④行代碼是設置物體的位置。它的單位是米。

第⑤行代碼b2Body *body = world->CreateBody(&bodyDef)是創建物體對象。

第⑥行代碼body->SetUserData(sprite)是將精靈放置到物體的UserData屬性中。這樣便於我們從物體中獲取相關聯的物體。
第⑦行代碼b2PolygonShape dynamicBox是聲明多邊形形狀定義變量。第⑧行代碼dynamicBox.SetAsBox(1, 1)是設置多邊形為矩形盒子形狀,因為坐標原點在盒子的左下角。SetAsBox是設置盒子的中心為(1,1)。那麽這個盒子的就是2米見方的大小。
第⑨行代碼是b2FixtureDef fixtureDef是聲明夾具定義變量。第⑩行代碼是設置夾具的形狀。第?行代碼fixtureDef.density = 1.0f是設置形狀的密度。第?行代碼fixtureDef.friction = 0.3f是設置摩擦系數,範圍是0.0~1.0之間。

第?行代碼body->CreateFixture(&fixtureDef) 是使用夾具固定形狀到物體上,這樣物體就有了形狀。
HelloWorldScene.cpp中遊戲循環函數HelloWorld::update代碼例如以下:

void HelloWorld::update(float dt)
{
	float timeStep = 0.03f; 
	int32 velocityIterations = 8;	
	int32 positionIterations = 1;	


	world->Step(timeStep, velocityIterations, positionIterations);


	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())						①
	{
		if (b->GetUserData() != nullptr) {									
			Sprite* sprite = (Sprite*)b->GetUserData();	
			sprite->setPosition( Vec2( b->GetPosition().x * 
				PTM_RATIO, b->GetPosition().y * PTM_RATIO) );	
			sprite->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) );
		}
	}																			②
}


當中代碼①~②這段代碼能夠同步物理引擎中的物體與精靈位置和狀態。



很多其它內容請關註最新Cocos圖書《Cocos2d-x實戰 C++卷》本書交流討論站點:http://www.cocoagame.net
很多其它精彩視頻課程請關註智捷課堂Cocos課程:http://v.51work6.com
歡迎增加Cocos2d-x技術討論群:257760386


《Cocos2d-x實戰 C++卷》現已上線,各大商店均已開售:

京東:http://item.jd.com/11584534.html

亞馬遜:http://www.amazon.cn/Cocos2d-x%E5%AE%9E%E6%88%98-C-%E5%8D%B7-%E5%85%B3%E4%B8%9C%E5%8D%87/dp/B00PTYWTLU

當當:http://product.dangdang.com/23606265.html

互動出版網:http://product.china-pub.com/3770734

《Cocos2d-x實戰 C++卷》源代碼及樣章下載地址:

源代碼下載地址:http://51work6.com/forum.php?mod=viewthread&tid=1155&extra=page%3D1

樣章下載地址:http://51work6.com/forum.php?mod=viewthread&tid=1157&extra=page%3D1

歡迎關註智捷iOS課堂微信公共平臺技術分享

實例介紹Cocos2d-x中Box2D物理引擎:HelloBox2D