咱們經過一個實例介紹一下,在Cocos2d-x 3.x中使用Box2D物理引擎的開發過程,熟悉這些API的使用。這個實例運行後的場景如圖所示,當場景啓動後,玩家能夠觸摸點擊屏幕,每次觸摸時候,就會在觸摸點生成一個新的精靈,精靈的運行自由落體運動。
html
HelloBox2D實例算法
使用Box2D引擎進行開發過程,如圖12-15所示。下面咱們就按照這個步驟介紹一下代碼部分。首先看一下看HelloWorldScene.h文件,它的代碼以下:安全
[html] view plaincopy微信
#ifndef __HELLOWORLD_SCENE_H__ 函數
#define __HELLOWORLD_SCENE_H__ 測試
#include "cocos2d.h" 網站
#include "Box2D/Box2D.h" ① this
#define PTM_RATIO 32 ② spa
class HelloWorld : public cocos2d::Layer .net
{
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()函數代碼以下:
[html] view plaincopy
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()函數代碼以下:
[html] view plaincopy
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++中new關鍵字分配內存,釋放內存是delete關鍵字。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函數代碼以下:
[html] view plaincopy
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代碼以下:
[html] view plaincopy
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()) );
}
} ②
}
其中代碼①~②這段代碼能夠同步物理引擎中的物體與精靈位置和狀態。
更多內容請關注國內第一本Cocos2d-x 3.2版本圖書《Cocos2d-x實戰:C++卷》
本書交流討論網站:http://www.cocoagame.net
更多精彩視頻課程請關注智捷課堂Cocos課程:http://v.51work6.com
歡迎加入Cocos2d-x技術討論羣:257760386
歡迎關注智捷iOS課堂微信公共平臺