物理引擎經過爲剛性物體賦予真實的物理屬性的方式來計算運動、旋轉和碰撞反映。爲每一個遊戲使用物理引擎並非徹底必要的—簡單的「牛頓」物理(好比加速和減速)也能夠在必定程度上經過編程或編寫腳原本實現。然而,當遊戲須要比較複雜的物體碰撞、滾動、滑動或者彈跳的時候(好比賽車類遊戲或者保齡球遊戲),經過編程的方法就比較困難了。那麼着時候使用物理系統能夠爲遊戲帶來一些很明顯的優勢:算法
1)更加真實的對現實世界的模擬,以牛頓力學爲基礎的遊戲效果。編程
a) 遊戲中的精靈們運動起來更真實:相互碰撞,自由下落等各類效果更加真實。函數
b) 玩家操做起來隨機性增大,物理碰撞決定他操做的效果。遊戲體驗加強。優化
2) 系統化的碰撞處理機制。動畫
a) 碰撞算法最優化,提升同一場景中,大量碰撞的運算效率。spa
b) 能夠處理複雜形狀的碰撞。code
c) 容許遊戲邏輯處理程序在最適合的時刻處理碰撞,實現最佳的遊戲體驗。orm
Box2D是一個用於模擬2D剛體物體的C++引擎。Box2D是一個物理引擎,模擬一個真實的物理環境,上面有重力加速度,摩擦力,剛體能概念,在這個環境裏,只要定義好相應的剛體和重力,摩擦力等外部環境,他們就能夠本身處理碰撞。由於Box2D只是一個物理引擎,因此能夠用在不少不一樣的開發平臺和不一樣的遊戲引擎。那麼在Cocos2D-x裏面是支持Box2D的使用的。對象
那麼在Box2D中有下面的一些概念,要對這些概念給理解了才能更好地去使用Box2D這個物理引擎去進行編程。blog
1.世界:世界是遵循物理的空間,以上的全部都存在於世界中,能夠建立多個世界,但不多這樣用。
建立世界須要兩個步驟,一是生成重力向量,二是根據重力生成世界對象
//生成重力向量
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
//生成世界對象
booldoSleep = true;
world = new b2World(gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
2.剛體: 便是物理學中的質點,只有位置,沒有大小。
它又能夠區分爲如下幾類1)靜態剛體:靜態剛體沒有質量,沒有速度,只能夠手動來改變他的位置;2)棱柱剛體:棱柱剛體沒有質量,可是能夠有速度,能夠本身更新位置;3)動態剛體:動態剛體有質量也有速度。
物理引擎須要首先定義一個描述類,而後再根據描述類經過世界建立某個對象。建立剛體時須要有兩個步驟,一是生成一個剛體定義,二是根據剛體定義生成剛體。在剛體建立時定義中的信息會被複制,也就是說建立完成後剛體只要沒被釋放掉,就還能夠重複使用。
//定義剛體
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(screenSize.width/2/PTM_RATIO, screenSize.height/2/PTM_RATIO);
//生成剛體
b2Body* groundBody = world->CreateBody(&groundBodyDef);
3.形狀:經過關聯添加到剛體上,碰撞能夠根據形狀來斷定,具備摩擦和恢復等材料特性。
b2PolygonShape groundBox;
// bottom
groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(0, -screenSize.height/2/PTM_RATIO), 0);
4.關聯:是一種附加在剛體上的屬性,一個剛體能夠有多個關聯,建立關聯時,須要定義關聯的信息,而後經過剛體建立關聯,當關聯被建立時關聯定義中的信息也會被保留,也能夠重用
//定義並建立關聯
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
5.連接:連接能夠聯繫多個缸體,使得剛體之間相互影響,一樣的,也須要首先定義信息,以後經過世界建立連接,一樣的,信息也能夠被保留,從而重用。另外連接還支持限制和馬達,限制就是限制物體運動的角度,馬達就是依照關節中的限制來約束物體連接有旋轉,棱柱和距離等
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
6.約束:一個約束就是消除物體自由度的物理鏈接。在 2D 中,一個物體有 3 個自由度。若是咱們把一個物體釘在牆上(像擺錘那樣),那咱們就把它約束到了牆上。這樣,此物體就只能繞着這個釘子旋轉,因此這個約束消除了它 2 個自由度。還有一種不須你建立的接觸約束,一個防止剛體穿透,以及用於模擬摩擦和恢復的特殊約束。
在WP8裏面使用Cocos2D-x裏面使用Box2D引擎咱們須要把Cocos2D-x封裝好的Box2D引擎庫引入到項目裏面。以下所示:
而後咱們須要按照下面的步驟去在Cocos2D-x裏面進行編程:
(1)建立一個world對象,這個world對象管理物理仿真中的全部對象。
一旦咱們已經建立了這個world對象,接下來須要往裏面加入一些body對象。body對象能夠隨意移動,能夠是怪物或者飛鏢什麼的,只要是參與碰撞的遊戲對象都要爲之建立一個相應的body對象。固然,也能夠建立一些靜態的body對象,用來表示遊戲中的臺階或者牆壁等不能夠移動的物體。
(2)建立body對象。
只要你把全部須要建立的body對象都建立好以後,box2d接下來就會接管工做,而且高效地進行物理仿真。
(3)運做物理引擎。
週期性地調用world對象的step函數。通常會經過scheduleUpdate()方法,在遊戲每一幀發生的時候都調用一次update函數,而後再update函數裏面處理精靈的位置更新等。
示例代碼,當點擊屏幕的時候將會產生一個精靈往下面掉落下去:
class TestLayer : public cocos2d::CCLayer { protected: cocos2d::CCSprite* cat; b2World* world; public: TestLayer(void); ~TestLayer(void); void addNewSpriteWithCoords(cocos2d::CCPoint p); void update(cocos2d::ccTime dt); virtual void ccTouchesEnded(cocos2d::CCSet* touches, cocos2d::CCEvent* event); }; TestLayer::TestLayer() { setTouchEnabled( true ); CCSize screenSize = CCDirector::sharedDirector()->getWinSize(); // 定義重力向量D b2Vec2 gravity; gravity.Set(0.0f, -10.0f); bool doSleep = true; // 經過重力構建生成世界 world = new b2World(gravity); world->SetAllowSleeping(doSleep); world->SetContinuousPhysics(true); // 構建地面及牆壁,由於要構建一個空心的物體,所以咱們不能直接定義,而是分別定義長方體裏的四個邊。 // box2d採起的現實世界的米做爲計量長度的單位,因此咱們要把咱們的像素級的長度單位轉換爲米的單位就要除以PTM_RATIO(定義32像素爲1米)。 // #define PTM_RATIO 32 b2BodyDef groundBodyDef; groundBodyDef.position.Set(screenSize.width/2/PTM_RATIO, screenSize.height/2/PTM_RATIO); // bottom-left corner //建立剛體並把剛體添加到世界上 b2Body* groundBody = world->CreateBody(&groundBodyDef); // 定義剛體的形狀 b2PolygonShape groundBox; // bottom groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(0, -screenSize.height/2/PTM_RATIO), 0); groundBody->CreateFixture(&groundBox, 0); // top groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(0, screenSize.height/2/PTM_RATIO), 0); groundBody->CreateFixture(&groundBox, 0); // left groundBox.SetAsBox(0, screenSize.height/2/PTM_RATIO, b2Vec2(-screenSize.width/2/PTM_RATIO, 0), 0); groundBody->CreateFixture(&groundBox, 0); // right groundBox.SetAsBox(0, screenSize.height/2/PTM_RATIO, b2Vec2(screenSize.width/2/PTM_RATIO, 0), 0); groundBody->CreateFixture(&groundBox, 0); //Set up sprite //CCSpriteBatchNode 中的全部CCSprite只會被渲染1次,所以能夠提升遊戲的FPS CCSpriteBatchNode *mgr = CCSpriteBatchNode::create("cat.png", 150); addChild(mgr, 0, 1); addNewSpriteWithCoords( CCPointMake(screenSize.width/2, screenSize.height/2) ); CCLabelTTF *label = CCLabelTTF::create("Tap screen", "Marker Felt", 32); addChild(label, 0); label->setColor( ccc3(0,0,255) ); label->setPosition( CCPointMake( screenSize.width/2, screenSize.height-50) ); //定時更新,每一幀都會調用一次update函數 scheduleUpdate(); } TestLayer::~TestLayer() { delete world; world = NULL; } //在當前的位置來產生一個精靈 void TestLayer::addNewSpriteWithCoords(CCPoint p) { //建立精靈放到SpriteBatchNode裏面 CCSpriteBatchNode* batch = (CCSpriteBatchNode*)getChildByTag(1); CCSprite *sprite = CCSprite::createWithTexture(batch->getTexture()); batch->addChild(sprite); //設置精靈的位置在當前的點擊位置上 sprite->setPosition( CCPointMake( p.x, p.y) ); //定義動態剛體,而後建立到世界上去 b2BodyDef bodyDef; //使剛體可以在力的做用下運行,剛體有三種:靜態的、運動的、動態的 bodyDef.type = b2_dynamicBody; //設置剛體的初始位置 bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO); //剛體所引用的數據就是咱們所生成的精靈 bodyDef.userData = sprite; b2Body *body = world->CreateBody(&bodyDef); //定義剛體的形狀 b2PolygonShape dynamicBox; dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box //定義剛體的紋理 b2FixtureDef fixtureDef; //綁定形狀 fixtureDef.shape = &dynamicBox; //設置密度 fixtureDef.density = 1.0f; //設置摩擦 fixtureDef.friction = 0.3f; body->CreateFixture(&fixtureDef); } void TestLayer::update(ccTime dt) { int velocityIterations = 8; int positionIterations = 1; //Box2d是經過按期調用step來更新動畫的,step的第一個參數是時間步,第二個參數是速度迭代次數,推薦8次,超過10次的基本看不出效果的提高,第三個參數是位置迭代 world->Step(dt, velocityIterations, positionIterations); //遍歷整個世界,找出對應精靈的剛體,進行位置更新 for (b2Body* b = world->GetBodyList(); b; b = b->GetNext()) { if (b->GetUserData() != NULL) { //Synchronize the AtlasSprites position and rotation with the corresponding body CCSprite* myActor = (CCSprite*)b->GetUserData(); myActor->setPosition( CCPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO) ); myActor->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()) ); } } } //點擊屏幕事件 void TestLayer::ccTouchesEnded(CCSet* touches, CCEvent* event) { //在點擊的位置上建立一個新的精靈Add a new body/atlas sprite at the touched location CCSetIterator it; CCTouch* touch; //循環獲取點擊的位置 for( it = touches->begin(); it != touches->end(); it++) { touch = (CCTouch*)(*it); if(!touch) break; CCPoint location = touch->getLocationInView(); location = CCDirector::sharedDirector()->convertToGL(location); //在當前的位置來產生一個精靈 addNewSpriteWithCoords( location ); } }
運行的效果: