[Cocos2D-x For WP8]Box2D物理引擎

    物理引擎經過爲剛性物體賦予真實的物理屬性的方式來計算運動、旋轉和碰撞反映。爲每一個遊戲使用物理引擎並非徹底必要的—簡單的「牛頓」物理(好比加速和減速)也能夠在必定程度上經過編程或編寫腳原本實現。然而,當遊戲須要比較複雜的物體碰撞、滾動、滑動或者彈跳的時候(好比賽車類遊戲或者保齡球遊戲),經過編程的方法就比較困難了。那麼着時候使用物理系統能夠爲遊戲帶來一些很明顯的優勢:算法

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定義結構體,用以指定body的初始屬性,好比位置或者速度。
  • 一旦建立好body結構體後,你就能夠調用world對象來建立一個body對象了。
  • 而後,你爲body對象定義一個shape,用以指定你想要仿真的物體的幾何形狀。
  • 接着建立一個fixture定義,同時設置以前建立好的shape爲fixture的一個屬性,而且設置其它的屬性,好比質量或者摩擦力。
  • 最後,你可使用body對象來建立fixture對象,經過傳入一個fixture的定義結構就能夠了。
  • 請注意,你能夠往單個body對象裏面添加不少個fixture對象。這個功能在你建立特別複雜的對象的時候很是有用。好比自行車,你可能要建立2個輪子,車身等等,這些fixture能夠用關節鏈接起來。

  只要你把全部須要建立的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 );
    }
}

運行的效果:

相關文章
相關標籤/搜索