碰撞檢測是使用物理引擎的一個重要目的,使用物理引擎能夠進行精確的碰撞檢測,並且執行的效率也很高。
在Cocos2d-x 3.x中使用事件派發機制管理碰撞事件,EventListenerPhysicsContact是碰撞事件監聽器。碰撞檢測相關的API咱們在前面一節介紹過了,下面經過一個實例介紹碰撞檢測的實現。這個實例的運行後的場景如圖所示,當場景啓動後,玩家能夠觸摸點擊屏幕,每次觸摸時候,就會在觸摸點生成一個新的精靈,精靈的運行是自由落體運動。當這些精靈之間發生接觸時候,它們的顏色被設置爲黃色,分離後顏色又恢復到原來狀態了。
html
檢測碰撞實例微信
本實例涉及到物理引擎中物體之間的檢測碰撞,當兩個物體接觸到兩個物體分離過程當中,會發生一些事件,咱們能夠經過註冊監聽器EventListenerPhysicsContact來響應這些事件。
首先看一下看HelloWorldScene.h文件,它的代碼以下:函數
[html] view plaincopy測試
#ifndef __HELLOWORLD_SCENE_H__ 網站
#define __HELLOWORLD_SCENE_H__ this
#include "cocos2d.h" spa
USING_NS_CC; .net
class HelloWorld : public cocos2d::Layer code
{ orm
public:
static cocos2d::Scene* createScene();
virtual bool init();
virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
virtual void onEnter();
virtual void onExit();
CREATE_FUNC(HelloWorld);
void addNewSpriteAtPosition(Vec2 p);
};
#endif // __HELLOWORLD_SCENE_H__
上述代碼聲明瞭onEnter和onExit函數,用來處理層進入和退出回調函數。咱們會在onEnter函數註冊EventListenerPhysicsContact監聽器,以便於響應碰撞檢測事件,在onExit函數中註銷這些監聽器。
HelloWorldScene.cpp中建立物理世界和指定世界的邊界語句是在HelloWorld::createScene()和HelloWorld::init()函數中,這兩個函數相似於上一節的HelloPhysicsWorld實例,這裏再也不解釋這些函數代碼了。
HelloWorldScene.cpp中與碰撞檢測相關的代碼是在onEnter和onExit函數中,代碼以下:
[html] view plaincopy
void HelloWorld::onEnter()
{
Layer::onEnter();
auto listener = EventListenerPhysicsContact::create();
listener->onContactBegin = [](PhysicsContact& contact) ①
{
auto spriteA = (Sprite*)contact.getShapeA()->getBody()->getNode(); ②
auto spriteB = (Sprite*)contact.getShapeB()->getBody()->getNode(); ③
if (spriteA && spriteA->getTag() == 1
&& spriteB && spriteB->getTag() == 1) ④
{
spriteA->setColor(Color3B::YELLOW);
spriteB->setColor(Color3B::YELLOW);
}
log("onContactBegin");
return true;
};
listener->onContactPreSolve = [] (PhysicsContact& contact,
PhysicsContactPreSolve& solve) { ⑤
log("onContactPreSolve");
return true;
};
listener->onContactPostSolve = [] (PhysicsContact& contact,
const PhysicsContactPostSolve& solve) ⑥
log("onContactPostSolve");
};
listener->onContactSeperate = [](PhysicsContact& contact) { ⑦
auto spriteA = (Sprite*)contact.getShapeA()->getBody()->getNode();
auto spriteB = (Sprite*)contact.getShapeB()->getBody()->getNode();
if (spriteA && spriteA->getTag() == 1
&& spriteB && spriteB->getTag() == 1)
{
spriteA->setColor(Color3B::WHITE);
spriteB->setColor(Color3B::WHITE);
}
log("onContactSeperate");
};
Director::getInstance()->getEventDispatcher()->
addEventListenerWithFixedPriority(listener,1); ⑧
}
void HelloWorld::onExit()
{
Layer::onExit();
log("HelloWorld onExit");
Director::getInstance()->getEventDispatcher()->removeAllEventListeners(); ⑨
}
上述代碼的onEnter()函數是進入場景時候回調的函數,咱們能夠在這裏經過auto listener = EventListenerPhysicsContact::create()語句建立物體碰撞檢測事件監聽器對象。接下來經過第①、⑥、⑤、⑦行使用Lambda表達式定義了事件處理的匿名函數。
代碼第②和第③行是從接觸點中取出互相接觸的兩個節點對象,它的取值過程有點複雜,首先接觸點使用getShapeA()和getShapeB()函數得到物體形狀,在經過形狀的getBody()函數得到物體,經過物體的getNode()函數得到與形狀相關的節點對象。第④行代碼是進行判斷,判斷從接觸點取出的節點對象是否存在,而且判斷是否tag屬性爲1。
上面代碼第⑧行addEventListenerWithFixedPriority是指定固定的事件優先級註冊監聽器,事件優先級決定事件響應的優先級別,值越小優先級越高。
代碼第⑨行是在退出層回調函數onExit()中註銷全部的監聽事件。
HelloWorldScene.cpp中還有onTouchBegan和addNewSpriteAtPosition兩個函數,它們的代碼以下。
[html] view plaincopy
bool HelloWorld::onTouchBegan(Touch* touch, Event* event)
{
Vec2 location = touch->getLocation();
addNewSpriteAtPosition(location);
return false;
}
void HelloWorld::addNewSpriteAtPosition(Vec2 p)
{
auto sp = Sprite::create("BoxA2.png");
sp->setTag(1);
auto body = PhysicsBody::createBox(sp->getContentSize());
body->setContactTestBitmask(0xFFFFFFFF); ①
sp->setPhysicsBody(body);
sp->setPosition(p);
this->addChild(sp);
}
這兩個函數的代碼與上一節介紹的實例基本一致,可是須要注意的是咱們在第①行添加了body->setContactTestBitmask(0xFFFFFFFF)代碼,它的做用是設置物體接觸時候可否觸發EventListenerPhysicsContact中定義的碰撞檢測事件。若是兩個物體的接觸測試掩碼(ContactTestBitmask)執行「邏輯與」運算,若是結果爲非零值,代表這兩個物體會觸發碰撞檢測事件。默認值是0x00000000,表示清除全部掩碼位,0xFFFFFFFF表示全部掩碼位都設置爲1。
假設有三個物體(body一、body2和body3),設置接觸測試掩碼以下:
body1->setContactTestBitmask (0x01);//0001
body2->setContactTestBitmask (0x03);//0011
body3>setContactTestBitmask (0x02);//0010
那麼body1和body2,以及body2和body3是能夠觸發EventListenerPhysicsContact的碰撞檢測事件的,而body1和body3是不能的。
另外,除了接觸測試掩碼(ContactTestBitmask)外,物理引擎中還定義了類別掩碼(CategoryBitmask)和碰撞掩碼(CollisionBitmask),它們的做用是當兩個物體接觸時候是否發生「碰撞反應」,「碰撞反應」會表現爲一個物體受到另外物體的碰撞,而改變運動方向。因爲兩個物體是「剛體」,在碰撞的時候兩個物體不會交叉。
那麼類別掩碼(CategoryBitmask)與碰撞掩碼(CollisionBitmask)到底是什麼呢?
一、類別掩碼
定義了一個物體所屬類別,每個物體在場景中能被分配到多達32個不一樣的類別。經過body->setCategoryBitmask(int bitmask)函數設置類別掩碼。
二、碰撞掩碼
當兩個物體相互接觸時,該物體的碰撞掩碼與另外一個物體的類別掩碼執行「邏輯與」運算,若是結果爲非零值,該物體可以對另外一個物體的碰撞發生反應。經過body->setCollisionBitmask(int bitmask) 函數設置的碰撞掩碼。
綜上所述,類別掩碼(CategoryBitmask)與碰撞掩碼(CollisionBitmask)決定了物體可否發生「碰撞反應」。而接觸測試掩碼(ContactTestBitmask)的設置,可以檢測是否發生接觸發生,而且觸發EventListenerPhysicsContact監聽事件。 接觸測試掩碼與類別掩碼和碰撞掩碼沒有什麼關聯。
假設有三個物體(body一、body2和body3),它們設置以下:
[html] view plaincopy
body1->setCategoryBitmask(0x01); //0001
body1->setCollisionBitmask(0x03); //0011
body2->setCategoryBitmask(0x02); //0010
body2->setCollisionBitmask(0x01); //0001
body3->setCategoryBitmask(0x04); //0100
body3->setCollisionBitmask(0x06); //0110
body1和 body1之間、body1和 body二、body3和 body3可以互相發生碰撞反應,body1和body3不能發生碰撞反應。box 2不能對box3的碰撞發生反應,但box 3可以對box2的碰撞發生反應。
更多內容請關注國內第一本Cocos2d-x 3.2版本圖書《Cocos2d-x實戰:C++卷》
本書交流討論網站:http://www.cocoagame.net
更多精彩視頻課程請關注智捷課堂Cocos課程:http://v.51work6.com
歡迎加入Cocos2d-x技術討論羣:257760386
歡迎關注智捷iOS課堂微信公共平臺