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

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