實例介紹Cocos2d-x物理引擎:碰撞檢測
來源:程序員人生 發(fā)布時間:2014-10-10 08:00:00 閱讀次數(shù):2674次
碰撞檢測是使用物理引擎的一個重要目的,使用物理引擎可以進行精確的碰撞檢測,而且執(zhí)行的效率也很高。
在Cocos2d-x 3.x中使用事件派發(fā)機制管理碰撞事件,EventListenerPhysicsContact是碰撞事件監(jiān)聽器。碰撞檢測相關(guān)的API我們在前面一節(jié)介紹過了,下面通過一個實例介紹碰撞檢測的實現(xiàn)。這個實例的運行后的場景如圖所示,當場景啟動后,玩家可以觸摸點擊屏幕,每次觸摸時候,就會在觸摸點生成一個新的精靈,精靈的運行是自由落體運動。當這些精靈之間發(fā)生接觸時候,它們的顏色被設(shè)置為黃色,分離后顏色又恢復(fù)到原來狀態(tài)了。

檢測碰撞實例
本實例涉及到物理引擎中物體之間的檢測碰撞,當兩個物體接觸到兩個物體分離過程中,會發(fā)生一些事件,我們可以通過注冊監(jiān)聽器EventListenerPhysicsContact來響應(yīng)這些事件。
首先看一下看HelloWorldScene.h文件,它的代碼如下:
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
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函數(shù),用來處理層進入和退出回調(diào)函數(shù)。我們會在onEnter函數(shù)注冊EventListenerPhysicsContact監(jiān)聽器,以便于響應(yīng)碰撞檢測事件,在onExit函數(shù)中注銷這些監(jiān)聽器。
HelloWorldScene.cpp中創(chuàng)建物理世界和指定世界的邊界語句是在HelloWorld::createScene()和HelloWorld::init()函數(shù)中,這兩個函數(shù)類似于上一節(jié)的HelloPhysicsWorld實例,這里不再解釋這些函數(shù)代碼了。
HelloWorldScene.cpp中與碰撞檢測相關(guān)的代碼是在onEnter和onExit函數(shù)中,代碼如下:
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()函數(shù)是進入場景時候回調(diào)的函數(shù),我們可以在這里通過auto listener = EventListenerPhysicsContact::create()語句創(chuàng)建物體碰撞檢測事件監(jiān)聽器對象。接下來通過第①、⑥、⑤、⑦行使用Lambda表達式定義了事件處理的匿名函數(shù)。
代碼第②和第③行是從接觸點中取出互相接觸的兩個節(jié)點對象,它的取值過程有點復(fù)雜,首先接觸點使用getShapeA()和getShapeB()函數(shù)獲得物體形狀,在通過形狀的getBody()函數(shù)獲得物體,通過物體的getNode()函數(shù)獲得與形狀相關(guān)的節(jié)點對象。第④行代碼是進行判斷,判斷從接觸點取出的節(jié)點對象是否存在,并且判斷是否tag屬性為1。
上面代碼第⑧行addEventListenerWithFixedPriority是指定固定的事件優(yōu)先級注冊監(jiān)聽器,事件優(yōu)先級決定事件響應(yīng)的優(yōu)先級別,值越小優(yōu)先級越高。
代碼第⑨行是在退出層回調(diào)函數(shù)onExit()中注銷所有的監(jiān)聽事件。
HelloWorldScene.cpp中還有onTouchBegan和addNewSpriteAtPosition兩個函數(shù),它們的代碼如下。
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);
}
這兩個函數(shù)的代碼與上一節(jié)介紹的實例基本一致,但是需要注意的是我們在第①行添加了body->setContactTestBitmask(0xFFFFFFFF)代碼,它的作用是設(shè)置物體接觸時候能否觸發(fā)EventListenerPhysicsContact中定義的碰撞檢測事件。如果兩個物體的接觸測試掩碼(ContactTestBitmask)執(zhí)行“邏輯與”運算,如果結(jié)果為非零值,表明這兩個物體會觸發(fā)碰撞檢測事件。默認值是0x00000000,表示清除所有掩碼位,0xFFFFFFFF表示所有掩碼位都設(shè)置為1。
假設(shè)有三個物體(body1、body2和body3),設(shè)置接觸測試掩碼如下:
body1->setContactTestBitmask (0x01);
//0001
body2->setContactTestBitmask (0x03);
//0011
body3>setContactTestBitmask (0x02);
//0010
那么body1和body2,以及body2和body3是可以觸發(fā)EventListenerPhysicsContact的碰撞檢測事件的,而body1和body3是不能的。
另外,除了接觸測試掩碼(ContactTestBitmask)外,物理引擎中還定義了類別掩碼(CategoryBitmask)和碰撞掩碼(CollisionBitmask),它們的作用是當兩個物體接觸時候是否發(fā)生“碰撞反應(yīng)”,“碰撞反應(yīng)”會表現(xiàn)為一個物體受到另外物體的碰撞,而改變運動方向。由于兩個物體是“剛體”,在碰撞的時候兩個物體不會交叉。
那么類別掩碼(CategoryBitmask)與碰撞掩碼(CollisionBitmask)究竟是什么呢?
1、類別掩碼
定義了一個物體所屬類別,每一個物體在場景中能被分配到多達32個不同的類別。通過body->setCategoryBitmask(int bitmask)函數(shù)設(shè)置類別掩碼。
2、碰撞掩碼
當兩個物體相互接觸時,該物體的碰撞掩碼與另一個物體的類別掩碼執(zhí)行“邏輯與”運算,如果結(jié)果為非零值,該物體能夠?qū)α硪粋€物體的碰撞發(fā)生反應(yīng)。通過body->setCollisionBitmask(int bitmask) 函數(shù)設(shè)置的碰撞掩碼。
綜上所述,類別掩碼(CategoryBitmask)與碰撞掩碼(CollisionBitmask)決定了物體能否發(fā)生“碰撞反應(yīng)”。而接觸測試掩碼(ContactTestBitmask)的設(shè)置,能夠檢測是否發(fā)生接觸發(fā)生,并且觸發(fā)EventListenerPhysicsContact監(jiān)聽事件。 接觸測試掩碼與類別掩碼和碰撞掩碼沒有什么關(guān)聯(lián)。
假設(shè)有三個物體(body1、body2和body3),它們設(shè)置如下:
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和 body2、body3和 body3能夠互相發(fā)生碰撞反應(yīng),body1和body3不能發(fā)生碰撞反應(yīng)。box 2不能對box3的碰撞發(fā)生反應(yīng),但box 3能夠?qū)ox2的碰撞發(fā)生反應(yīng)。
更多內(nèi)容請關(guān)注國內(nèi)第一本Cocos2d-x 3.2版本圖書《Cocos2d-x實戰(zhàn):C++卷》
本書交流討論網(wǎng)站:http://www.cocoagame.net
更多精彩視頻課程請關(guān)注智捷課堂Cocos課程:http://v.51work6.com
歡迎加入Cocos2d-x技術(shù)討論群:257760386
歡迎關(guān)注智捷iOS課堂微信公共平臺
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學習有所幫助,可以手機掃描二維碼進行捐贈