cocos2d-x 3.2 物理碰撞機制

cocos2d-x 3.2 物理碰撞機制


cocos2d-x 3.0+ 中全新的封裝的物理引擎給了開發者最大的便捷,你不用再繁瑣與各種物理引擎的細節,完全的封裝讓開發者可以更快更好的將物理引擎的機制添加到自己的遊戲中,簡化的設計是從2.0到3.0+的一個質的飛躍。

下面同樣以一個小demo來展示一下物理引擎的運用,同時說一下我在運用物理引擎中遇到的一些小小的問題。

cocos2d-x 3.0+中的物理屬性:

1、物理世界被集成到場景中,當你創建一個場景,你可以直接創建基於物理世界或者不使用物理世界的場景。

2、Node擁有它自己的body屬性。(sprite也是node)‘

3、cocos2d-x 3.0 已經封裝了物理屬性Body(PhysicsBody),Shape(PhysicsShape),Contact(PhysicsContact),Joint(PhysicsJoint)和World(PhysicsWorld),更加方便使用。

4、方便的使用listener-EventListenerPhysicsContact進行碰撞檢測。

當然,封裝好的物理引擎可以簡化開發難度,如果有能力的話也可以直接使用Box2D和Chipmunk的原生的物理引擎進行開發,這樣難度會有所提升。

下面的代碼創建一個帶物理世界的場景,並傳遞到場景中的層上。

PhysicsLayer.h中

?
1
2
3
4
5
6
7
8
9
10
class PhysicsLayer : public cocos2d::Layer
{
    ...
    // add following codes設置層中的物理世界
    void setPhyWorld(PhysicsWorld* world){m_world = world;}
  
    private:
        PhysicsWorld* m_world;
    ...
}
PhysicsLayer.cpp中的createScene()方法中添加下面代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
Scene* PhysicsLayer::createScene()
{
    ...
    // add following codes
    auto scene = Scene::createWithPhysics();
    scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);//調試
  
    auto layer = HelloWorld::create();
    layer->setPhyWorld(scene->getPhysicsWorld());//將創建的物理世界傳遞到子層中
    ...
    return scene;
}

Scene類有一個新的static工廠方法createWithPhysics()創建一個帶物理世界的場景。可以通過getPhysicsWorld()來獲取PhysicsWorld的實例。

上述代碼中註釋爲調試的代碼在調試中非常有用,它會顯示遊戲中物體所帶有的物理邊界,便於觀察碰撞中的細節等。

同時一個場景中只能有一個物理世界,所有屬於這個場景的子層都共享這一個物理世界,所以在子層中用到物理世界時都會有這個定義的函數

?
1
void setPhyWorld(PhysicsWorld* world){m_world = world;}
進而來設置子層中的物理世界。

PhysicsWorld擁有默認的重力設置,Vector(0.0f,-98.0f),當然你也可以隨意設置你想要的重力加速度,setGravity(Vect(0.0f,-200.0f)),設置重力加速度爲20米每二次方秒。

創建物理邊界

下面的代碼創建一個物理邊界
?
1
2
3
4
5
6
Size visibleSize = Director::getInstance()->getVisibleSize();
auto body = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, 3);//設置要添加到節點中的物理body
auto edgeNode = Node::create();
edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
edgeNode->setPhysicsBody(body);//將物理body加入到創建的節點中
scene->addChild(edgeNode);場景中添加創建的物理節點
PhysicsWorld有很多工廠方法,如createEdgeBox創建一個矩形的邊框,參數: 1、矩形區域,設置作爲VisibleSize 2、可選參數,物理材料,默認爲PHYSICSBODY_MATERIAL_DEFAULT。 3、可選參數,邊框大小,默認爲1.

創建受重力影響的精靈

下面的代碼創建一個受重力影響的精靈,3.0中的創建精靈代碼也大大簡化
?
1
2
3
4
5
6
7
8
9
void HelloWorld::addNewSpriteAtPosition(Point p)
{
    auto sprite = Sprite::create("circle.png");//創建精靈
    sprite->setTag(1);//設置精靈的便籤值
    auto body = PhysicsBody::createCircle(sprite->getContentSize().width / 2);//創建一個附加在精靈身體上的圓形物理body
    sprite->setPhysicsBody(body);//將創建的body加到精靈身上
    sprite->setPosition(p);
    this->addChild(sprite);//添加精靈
}

下面講一下真正的重點所在——物理碰撞檢測

談到物理碰撞檢測,真是感慨良多,在第一次運用碰撞檢測時,遇到各種問題,最大的問題就是註冊完物理碰撞響應時間,寫好碰撞響應函數後,居然沒反應,找各種論壇貼,各種google,最後都沒有解決問題,最後還是在官網找到的解決方法,下面先介紹下如何設置碰撞檢測。 前面的博客講過了3.0中的事件分發機制,所以物理引擎的碰撞事件也不例外,統一的都有事件派發器來管理。 下面代碼註冊碰撞響應事件和回調函數
?
1
2
3
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
每一次碰撞檢測事件是有EventListenerPhysicsContact來進行監聽的。監聽到碰撞事件時,會回調響應事件onContactBegin()來進行碰撞事件的處理。_eventDispatcher是事件派發器,由它管理所有的註冊事件。 EventListenerPhysicsContact是碰撞檢測中的一種,也可以運用下面的來進行碰撞事件的註冊EventListenerPhysicsContactWithBodies,EventListenerPhysicsContactWithShapes,EventListenerPhysicsContactWithGroup來進行你感興趣的bodys,shape和group事件監聽。 在上面說了這麼多的東西,最重要的東西就是下面的,沒有下面的東西,碰撞事件根本不起作用,這就是我第一次運用碰撞時遇到的問題。也就是設置物理接觸相關的位掩碼值,默認的接觸事件不會被接受,需要設置一定的掩碼值來使接觸事件響應。 接觸掩碼值有三個值,分別是: 1、CategoryBitmask,默認值爲0xFFFFFFFF 2、ContactTestBitmask,默認值爲 0x00000000 3、CollisionBitmask,默認值爲0xFFFFFFFF 這三個掩碼值都有對應的set/get方法來設置和獲取。 這三個掩碼值由邏輯與來進行操作測試。 
一個body的CategoryBitmask和另一個body的ContactTestBitmask的邏輯與的結果不等於0時,接觸事件將被髮出,否則不發送。 
一個body的CategoryBitmask和另一個body的CollisionBitmask的邏輯與結果不等於0時,他們將碰撞,否則不碰撞 
默認情況下的body屬性會進行物理碰撞,但不會發送碰撞檢測的信號,也就不會響應碰撞回調函數,這個可以看下默認情況下的掩碼值的邏輯與
?
1
2
3
4
5
6
7
8
CategoryBitmask = 0xFFFFFFFF
ContactTestBitmask = 0x00000000
CategoryBitmask & ContactTestBitmask = 0,所以不會發送碰撞信號
 
CollisionBitmask = 0xFFFFFFFF
 
CategoryBitmask & CollisionBitmask = 0xFFFFFFFF
所以物體會碰撞,但是不會響應碰撞回調函數。
上面介紹的掩碼值是碰撞檢測回調中最重要的,沒有上面的掩碼值,所有的碰撞回調函數都不會發生。 EventListenerPhysicsContact有四個接觸回調函數: 1、onContactBegin,在接觸開始時被調用,僅調用一次,通過放回true或者false來決定兩個物體是否有碰撞。同時可以使用PhysicsContact::setData()來設置接觸操作的用戶數據。當返回false時,onContactPreSolve和onContactPostSolve將不會被調用,但是onContactSeperate將被調用一次。 2、onContactPreSlove ,會在每一次被調用,通過放回true或者false來決定兩個物體是否有碰撞,同樣可以用ignore()來跳過後續的onContactPreSolve和onContactPostSolve回調函數。(默認返回true) 3、onContactPostSolve,在兩個物體碰撞反應中的每個步驟中被處理調用。可以在裏面做一些後續的接觸操作。如銷燬body 4、onContactSeperate,在兩個物體分開時被調用,在每次接觸時只調用一次,和onContactBegin配對使用。 上述中最重要的就是碰撞檢測事件的講解,這是遊戲中用到碰撞經常要用到的。 好了,這篇講解了遊戲中的物理碰撞機制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章