1. 程式人生 > >cocos2dx-3.0 中的關於物理引擎Box2D與chipmunk

cocos2dx-3.0 中的關於物理引擎Box2D與chipmunk

~~~~我的生活,我的點點滴滴!!

           http://www.cocos2d-x.org/wiki/Physics

上面這個連結是官方的,我隨便翻譯了一下,其實大意就是:cocos2dx 3.0(只能確定3.0,不知道後面版本有沒有新增)中有兩種物理

引擎Box2D與chipmunk,但是官方只封裝了chipmunk,並沒有封裝Box2D,也就是說如果使用Box2D的話,需要直接操作Box2D的API,有

好有壞!簡單的看看下面的新特性:

(1) 物理世界被融入到Scene中,即當建立一個場景時,就可以指定這個場景是否使用物理引擎。

(2) Node自帶body屬性,也就是sprite自帶body屬性。

(3) Cocos2d-x 3.0對物理引擎的Body(PhysicsBody),Shape(PhysicsShape),Contact(PhysicsContact),Joint    (PhysicsJoint),

    World(PhysicsWorld)進行了封裝抽象,使用更簡單。

(4) 更簡單的碰撞檢測監聽EventListenerPhysicsContact。

上面所有的裡面都是用使用chipmunk引擎,不管你怎麼切換巨集。

我本意是學Box2D的物理引擎,目地也就是在cocos2dx中使用物理引擎,這裡我簡單的把官方的對於cocos2dx使用的物理引擎的方法生

搬過來,但並不打亂我們繼續學習cocos2dx中使用Box2D

的節奏。

1、建立帶物理世界的場景scene

我們來建立一個帶物理世界的場景scene,並傳遞給child layer。

在HelloWorldScene.h中新增如下程式碼:

private:
    PhysicsWorld* m_world;

public:
    void setPhyWorld(PhysicsWorld* world){ m_world = world;}

在HelloWorldScene.cpp中的createScene方法中新增如下程式碼:
auto scene = Scene::createWithPhysics();  
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);  
  
auto layer = HelloWorld::create();  
layer->setPhyWorld(scene->getPhysicsWorld());  

1.1、用Scene新的靜態工廠方法createWithPhysics()來建立物理世界的scene。

1.2、Scene的getPhysicsWorld()方法用來獲取PhysicsWorld例項。

1.3、PhysicsWorld的setDebugDrawMask()方法,在除錯物理引擎中是很有用的,它把物理世界中不可見的shape,joint,contact可視

     化。當除錯結束,遊戲釋出的時候,你需要把這個debug開關關閉。

1.4、通過setPhyWorld()方法來傳遞PhysicsWorld給ChildLayer。一個scene只有一個PhysicsWorld,其下的所有layer共用一個PhysicsWorld例項。

2、建立物理邊界

我們知道物理世界中,所有物體受重力的影響。物理引擎提供staticShape建立一個不受重力影響的形狀,在Cocos2d-x 2.0中,我們需要了

解物理引擎的staticShape相關的各種引數來完成邊界設定。在3.0中,PhysicsShape屬於Node的一個屬性,要設定PhysicsWorld的屬性,都

需要通過一個Node例項來中介傳達。下面的程式碼展示如何建立一個圍繞螢幕四周的物理邊界:

Size visibleSize = Director::getInstance()->getVisibleSize();  
auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);  
auto edgeNode = Node::create();  
edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2));  
edgeNode->setPhysicsBody(body);  
scene->addChild(edgeNode);  

2.1、PhysicsBody包含很多工廠方法,createEdgeBox建立一個矩形邊界,引數含義依次是:

     1、 矩形區域大小,這裡設定為visibleSize。

     2、 設定材質,可選引數,預設為PHYSICSBODY_MATERIAL_DEFAULT。

     3、 邊線寬度,可選引數,預設為1。

2.2、然後我們建立一個Node,把剛才建立的body附加到Node上,並設定好Node的position為螢幕中心點。

2.3、最後,把Node新增到scene。

2.4、Node的addChild方法,在3.0中,有對物理body做處理,它會自動把node的body設定到scene的PhysicsWorld上去。

2.6、PhysicsBody中的工程方法,針對引數設定的body大小,會自動建立對應的PhysicsBody和一個PhysicsShape,

     這也是通常情況下,直接使用物理引擎建立一個body需要做的事情。3.0的Physics integration極大的簡化

     了使用物理引擎的程式碼量。

3、建立受重力作用 sprite

在3.0中建立一個受重力作用的sprite其實不難。示例程式碼如下:

void HelloWorld::addNewSpriteAtPosition(Point p)  
{  
    auto sprite = Sprite::create("circle.png");  
    sprite->setTag(1);  
    auto body = PhysicsBody::createCircle(sprite->getContentSize().width / 2);  
    sprite->setPhysicsBody(body);  
    sprite->setPosition(p);  
    this->addChild(sprite);  
}  

首先建立一個sprite,然後用PhysicsBody::createCircle建立一個圓形的body附加在sprite上。整個過程和之前建立邊界的過程是一致的。

4、碰撞檢測

cocos2d-x中,事件派發機制做了重構,所有事件均有事件派發器統一管理。物理引擎的碰撞事件也不例外,下面的程式碼註冊碰撞begin回撥函式:

auto contactListener = EventListenerPhysicsContact::create();  
contactListener->onContactBegin = CC_CALLBACK_2(HelloWorld::onContactBegin, this);  
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);  

4.1、碰撞檢測的所有事件由EventListenerPhysicsContact來監聽,建立一個例項,然後設定它的onContactBegin回撥函式,CC_CALLBACK_2是

     Cocos2d-x 3.0使用C++ 11的回撥函式指標轉換助手函式,由於onContactBegin回撥有兩個引數,所有這裡使用CC_CALLBACK_2來做轉換。

4.2、_eventDispatcher是基類Node的成員,Layer初始化後就可直接使用。

你還可以使用EventListenerPhysicsContactWithBodies, EventListenerPhysicsContactWithShapes, EventListenerPhysicsContactWithGroup

來監聽你感興趣的兩個物體、兩個形狀,或者某組物體的碰撞事件,但是要注意設定物體碰撞相關的mask值(下面會詳細說明),因為物體碰撞

事件在預設情況下是不接收的,即使你建立了相應的EventListener。

PhysicsBody碰撞相關的mask設定和group設定跟Box2D的設定是一致的。

mask設定分為三種:

CategoryBitmask->表示“我是誰”;

ContactTestBitmask-->表示“在我和誰發生碰撞通知我”;

CollisionBitmask-->表示”我和誰發生碰撞“;

如果a->CategoryBitmask & b->CollisionBitmask等於0或者b->CategoryBitmask & a->CollisionBitmask等於0,不發生碰撞,可以看到物體穿

一個物體.你可以通過相關的get/set介面來獲得或者設定他們。他們是通過邏輯與來進行測試的,當一個物體的CategoryBitmask跟另一個體的

ContactTestBitmask的邏輯與測試結果不為零時,將會發送相應的事件,否則不傳送。而當一個物體的CategoryBitmask跟另一個物體的

CollisionBitmask的邏輯與測試結果不為零時,將會發生碰撞,否則不發生碰撞。注意,在預設情況下CategoryBitmask的值為0xFFFFFFFF,

ContactTestBitmask的值為0x00000000,CollisionBitmask的值為0xFFFFFFFF,也就是說預設情況下所有物體都會發生碰撞但不傳送通知。

另一個碰撞相關的設定是group(組),當它大於零時,同組的物體將發生碰撞,當它小於零時,同組的物體不碰撞。注意,當group不為零時,

他將忽略mask的碰撞設定(是否通知的設定依然有效)。在EventListenerPhysicsContact裡有四個碰撞回撥函式,他們分別是onContactBegin,

onContactPreSolve,onContactPostSolve和onContactSeperate。在碰撞剛發生時,onContactBegin會被呼叫,並且在此次碰撞中只會被呼叫

一次。你可以通過返回true或者false來決定物體是否發生碰撞。你可以通過PhysicsContact::setData()來儲存自己的資料以便用於後續的碰

撞處理。需要注意的是,當onContactBegin返回flase時,onContactPreSolve和onContactPostSolve將不會被呼叫,但onContactSeperate必

定會被呼叫。

onContactPreSolve發生在碰撞的每個step,你可以通過呼叫PhysicsContactPreSolve的設定函式來改變碰撞處理的一些引數設定,比如彈力,阻力等。

同樣你可以通過返回true或者false來決定物體是否發生碰撞。你還可以通過呼叫PhysicsContactPreSolve::ignore()來跳過後續的onContactPreSolve

和onContactPostSolve回撥事件通知(預設返回true)。onContactPostSolve發生在碰撞計算完畢的每個step,你可以在此做一些碰撞的後續處理,

如摧毀某個物體等。onContactSeperate發生在碰撞結束兩物體分離時,同樣只會被呼叫一次。它跟onContactBegin必定是成對出現的,所以你可以在此

摧毀你之前通過PhysicsContact::setData()設定的使用者資料。