1. 程式人生 > >cocos2d 物理引擎簡介

cocos2d 物理引擎簡介

材質(Material)

材質(Material) 描述了抽象物體的材料屬性:

  • density:密度,用於計算物體的質量
  • friction:摩擦,用於模擬物體間的接觸滑動
  • restitution:恢復係數,模擬物體反彈的一個係數,係數一般設為 0 到 1 之間。0 代表不反彈,1 代表完全反彈。

形狀(Shape)

形狀(Shape) 描述了抽象物體的幾何屬性,將形狀關聯到剛體,剛體才具有幾何形狀。如果需要剛體具有複雜的形狀,可以為它關聯多個形狀,每個形狀物件都與一個 PhysicsMaterial 相關,並且擁有以下屬性:type, area, mass, moment, offset 和 tag。其中有一些你可能還不熟悉,我們來逐一介紹:

  • type:描述了形狀的類別,如圓形,矩形,多邊形等
  • area:用於計算剛體的質量,密度和麵積決定了剛體的質量
  • mass:剛體的質量,影響物體在給定的力下獲得的加速度大小,物體在一個引力場中物體受到力的大小
  • moment:剛體獲得特定角加速度所需要的扭矩
  • offset:在剛體的當前座標中,相對於剛體重心的偏移量
  • tag:形狀物件的一個標籤,你可能還記得,所有的 Node 物件都可以被分配一個 tag,用來進行辨識,實現更容易的訪問。形狀物件的 tag 作用也一樣。

Cocos2d-x 中預定義了這些形狀物件:

  • PhysicsShape:物理形狀的基類
  • PhysicsShapeCircle:實心的圓形,無法用它實現一個空心圓
  • PhysicsShapePolygon:實心且外凸的多邊形
  • PhysicsShapeBox:矩形,它是一種特殊的外凸多邊形
  • PhysicsShapeEdgeSegment:表示一種線段.
  • PhysicsShapeEdgePolygon:空心多邊形,由多個線段構成的多邊形邊緣。
  • PhysicsShapeEdgeBox:空心矩形,由四個線段組成的矩形邊緣
  • PhysicsShapeEdgeChain: 鍊形,它可以有效的把許多邊緣連線起來

連線/關節

連線(Contacts) 和 關節(joint) 物件描述了剛體相互關聯的方式。

世界(World)

世界(World) 是現實物理世界的一個遊戲模擬,容納著所有被新增進去的抽象物體。你可以將剛體,形狀,約束都新增到物理世界中,然後將整個世界作為一個整體進行更新。物理世界控制著所有元素的相互作用。其中,用物理 API 實現的許多互動都與 世界(World)

 有關。

以上有許多需要記住的東西,請對這些都有個大概印象,以便一會用到的時候隨時回看。

PhysicsWorld

物理世界(PhysicsWorld) 是 Cocos2d-x 進行物理模擬的核心物件。物理世界會同時發生很多事情,就像我們生活的世界一樣。來想象一個簡單的現實場景——廚房,你在思考的時候,就在腦中描繪出了一個廚房的物理世界!廚房世界裡擁有一些物體,比如食物,刀具,電器,在這個世界中,這些物體會相互作用。它們會相互接觸,並對接觸做出反應。比如:用刀子切開食物,並把它放到電器中,做這樣一件事。刀子切到食物了嗎?可能切到了,也可能還沒有,還可能這個刀子根本就不適合做這個。

物理世界(PhysicsWorld)與場景(Scene)進行了深入的整合,只需要呼叫 Scene 物件的 initWithPhysics() 方法,就可以建立一個包含物理世界的場景,注意在初始化的時候要進行函式返回值的判斷。initWithPhysics() 初始化成功返回 true,失敗返回 false

C++
if( !Scene::initWithPhysics() )
{

}

每一個 物理世界(PhysicsWorld) 都有與之相關的屬性:

  • gravity:全域性重力,應用於整個物理世界,預設值為 Vec2(0.0f, -98.0f)
  • speed:物理世界的速度,這裡的速度指的是這個模擬世界執行的一種比率,預設值是 1.0
  • updateRate:物理世界的重新整理率,這裡的重新整理率指的是 遊戲引擎重新整理時間與物理世界重新整理時間的比值
  • substeps:物理世界中每次重新整理的子步數量

重新整理物理世界的過程被稱為步進,按照預設設定,物理世界會不停地進行自動重新整理,這被稱為自動步進。每一幀,都會不停地重新整理,你可以通過 setAutoStep(false) 禁用一個物理世界的自動步進,然後通過 PhysicsWorld::step(time) 設定步進時間來手動重新整理物理世界。遊戲世界是按幀重新整理的,物理世界可以通過子步(substeps)的設定,獲得更加頻繁的重新整理,從而進行更精細的步進控制。

物理剛體(PhysicsBody) 物件具有位置和速度,你可以在物理剛體上應用力(forces),運動(movement),阻尼(damping),衝量(impulses)等等。剛體可以是靜態的,也可以是動態的,靜態的剛體在模擬世界中不會移動,看起來就好像擁有無限大的質量一樣,動態的剛體則是一種完全模擬的模擬。剛體可以被玩家手動移動,更常見的是它們受到力的作用而移動。動態剛體可以與所有型別的剛體發生碰撞。Cocos2d-x 提供了 Node::setPhysicsbody() 方法實現節點物件和物理剛體物件的關聯。

Scene* Chapter11::createScene()

{

//創造物理世界場景

auto scene=Scene::createWithPhysics();

//邊框紅線

    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);

//設定重力

    scene->getPhysicsWorld()->setGravity(Vec2(0,-100));

//設定更新迭代時間

    scene->getPhysicsWorld()->step(0.03);

auto layer =Chapter11::create();

    scene->addChild(layer);

return scene;

}

boolChapter11::init()

{

if (!Layer::init())

    {

returnfalse

    }

auto visibleSize =Director ::getInstance()->getVisibleSize();

//建立一個物理世界,大小和螢幕的尺寸相同,使用預設材質,debug框的寬度為3個畫素

autobody = PhysicsBody::createEdgeBox(visibleSize,PHYSICSBODY_MATERIAL_DEFAULT,3);

//恢復力和摩擦力

    body->getShape(0)->setRestitution(1.0f);

//0號形狀

    body->getShape(0)->setFriction(0);//2個為0 ,沒有摩擦力

//建立一個碰撞圖形

auto edgeShape = Node::create();

//將圖形和剛剛建立的世界繫結

//addComponent一樣效果

    edgeShape->setPhysicsBody(body);

//設定圖形的位置在螢幕正中間

    edgeShape->setPosition(visibleSize.width / 2,visibleSize.height/2);

//新增進圖層

this->addChild(edgeShape);

PhysicsWorld有很多工廠方法,如createEdgeBox建立一個矩形的邊框,所有的引數是: 
a.矩形區域,設定作為visibleSize。  
b.可選引數,紋理,預設為PHYSICSBODY_MATERIAL_DEFAULT。  
c.可選引數,邊框大小,預設為1。

然後,我們建立節點,並連線剛剛建立的節點的身體。螢幕為節點的位置設定好的中心,最後加入節點到場景。

在Cocos2d-x3.0節點的addChild方法可以處理物理剛體。它會自動新增節點的身體到場景的PhysicsWorld。

PhysicsBody的工程方法可以根據引數設定剛體的大小,建立相應的PhysicsBody和PhysicsShape。這是一個常見的做法由物理引擎直接建立一個剛體。然而在Cocos2d-x 3.0物理整合簡化了這個過程,所以我們並不需要編寫大量的程式碼。


設定靜態剛體

//設定精靈圓圖

    auto sp=Sprite::create ("ball.png");

//剛體的圖形圓的 半徑

auto body2=PhysicsBody::createCircle(100);

    body2->setDynamic(false);//設定動態剛體  true是,false否  否就是靜態剛體

    //設定剛體的密度

    body2->getShape(0)->setDensity(0.7);

       //設定剛體的質量

    body2->setMass(10);

      //設定剛體的恢復力和摩擦力   //0號形狀    

    body2->getShape(0)->setRestitution(1.0f);

    body2->getShape(0)->setFriction(5.0f);

    body2->setGroup(1) ;//分類掩碼

    body2->setCollisionBitmask(1);//碰撞掩碼

    body2->setContactTestBitmask(1);//接觸測試掩碼

    sp->setPhysicsBody(body2);

    sp->setPosition(Vec2(visibleSize.width / 2,visibleSize.height/2));

this->addChild(sp);

returntrue;

}

class PhysicsSprite_:publicSprite{

public:

bool init();

CREATE_FUNC(PhysicsSprite_);

private:

PhysicsBody* body;

};


boolPhysicsSprite_::init()

{

if (!Sprite::init())

    {

returnfalse;

    }

//arc4random()隨機數

int idx = arc4random( )%2;

int idy = arc4random( )%2;

//擷取矩形Rect(float x, float y, float width, float height);

      初始化init

this->initWithFile("blocks.png",Rect(32*idx,32*idy,32,32));

body = PhysicsBody::createBox(Size(32,32));//Box盒子

//        this->initWithFile("CloseNormal.png") ;

//    body = PhysicsBody::createCircle(this->getContentSize().width/2);

//質量KG

body->setMass(1);

body->getShape(0)->setRestitution(1.0f);

body->getShape(0)->setFriction(0);

body->setGroup(1) ;//分類掩碼

body->setCollisionBitmask(1);//碰撞掩碼

body->setContactTestBitmask(1);//接觸測試掩碼

this->setPhysicsBody(body);

this->setTag(1);

returntrue;

}

voidChapter11::onExit()

{

Layer::onExit();

}

voidChapter11::onEnter()

{

Layer::onEnter();

    //觸控事件

auto dispatcher = Director ::getInstance()->getEventDispatcher();

    //新增加監聽器

auto touchListener =EventListenerTouchOneByOne::create();

    touchListener->onTouchBegan=[=](Touch*_touch,Event*_touchEvent)->bool{

returntrue;

    };

    touchListener->onTouchMoved=[=](Touch *_touch,Event *_touchEvent){};

    touchListener->onTouchEnded=[=](Touch*_touch,Event*_touchEvent){

auto sp =PhysicsSprite_::create();

        sp->setPosition(_touch->getLocation( )) ;

this->addChild(sp);

    };

//

//註冊該監聽

    dispatcher->addEventListenerWithSceneGraphPriority(touchListener,this);

//碰撞檢測

//新增加監聽器

auto  contactListener=EventListenerPhysicsContact::create();

    contactListener->onContactBegin=[=](constPhysicsContact& contact)->bool{

//接觸,聯絡                     剛體               節點

Sprite* SpriteA=(Sprite*)contact.getShapeA()->getBody()->getNode();

Sprite* SpriteB=(Sprite*)contact.getShapeB()->getBody()->getNode();

if(SpriteA&&SpriteB)

        {

int tagA=SpriteA->getTag();//形狀物件的一個標籤

int tagB=SpriteB->getTag();

if (tagA==1&&tagB==1)

            { //從物理世界刪除剛體,就變成普通圖片

                SpriteA->getPhysicsBody()->removeFromWorld();

                SpriteB->getPhysicsBody()->removeFromWorld();

//從父節點刪除精靈

                 SpriteA->removeFromParent();

                 SpriteB->removeFromParent();

            }

        }

cout<<"onContactBegin"<<endl;

returntrue;

    };

contactListener->onContactPreSolve=[=](PhysicsContact&contact,PhysicsContactPreSolve&solve)->bool{

cout<<"onContactPreSolve"<<endl;

returntrue;

    };

    contactListener->onContactPostSolve=[=](PhysicsContact& contact,constPhysicsContactPostSolve&solve){

cout<<"onContactPostSolve"<<endl;

    };

    contactListener->onContactSeparate=[=](PhysicsContact& contact){

cout<<"onContactSeparate"<<endl;

    };

///新增到事件分發器中

//註冊該監聽

Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(contactListener, this);

}