Box2D詳解2 碰撞篩選
DioysosLai 2014-5-20
相信玩過遊戲的人都知道,己方英雄釋放的技能只能對敵方英雄有傷害,而不能傷害到己方英雄(要是能傷害到自己人,那不是坑爹啊!)在一些小遊戲中,如果涉及到這些設置,一方面我們可以自己進行碰撞過濾,另一方面,如果我們使用到了Box2D,那麼,我們就可以使用Box2D自帶的碰撞過濾(Collision filtering),通過三個標誌位實現碰撞過濾:
類別標誌位(categoryBits)、遮罩標誌位(maskBits)、分組索引(groupIndex)。
類別標誌位和遮罩標誌位解釋:
所謂類別標誌位,就是指明對象類型,即告訴別人“我是誰”。所謂遮罩標誌位,就是致命碰撞對象,即告訴別人“我想和誰發生碰撞”。
例如《貓抓老鼠》類型遊戲,貓的類型就是“貓”,遮罩標誌位就是“貓|老鼠”。老鼠類型是“老鼠”,其遮罩標誌位就是“貓|老鼠”。這樣,貓和老鼠可以互相發生碰撞。但,有一點要指明就是,二者的碰撞必須是“你情我願”。如果貓的遮罩標誌位是“貓|老鼠”,即貓想和老鼠碰撞,但如果老鼠的遮罩標誌位是“老鼠”(注意:這裏沒有“貓”),即老鼠不想和貓發生碰撞,那麼老鼠和貓最終結果是不能發生碰撞的。
具體來說,我們可以用一段代碼來表示這種“你情我願”的關係:
bool collide = (filterCat.maskBits & filterMouse.categoryBits) != 0 &&
(filterCat.categoryBits & filterMouse.maskBits) != 0;
在默認情況下,類別標誌位值爲0x0001,遮罩標誌位默認值爲0xFFFF,即默認,我們創建的任何對象,都是“I am the god, I can collision with everything!”。如果,所以對象我們都沒改變其類別標誌位和遮罩標誌位,那麼,所有對象默認都是可以互相碰撞的。
如何設置類別標誌位和遮罩標誌位:
每一個標誌位是一個16位的整數,因此我們可以設置16種不同的碰撞類型。具體方法,可以先設置我們要設置物理世界對象類型:
enum CABOX2DMASK
{
BOX2D_MASK_WORD = 0X0001, ///< 世界
BOX2D_MASK_BALL = 0x0002, ///< 球
BOX2D_MASK_BASKET = 0x0004, ///< 球框
… = 0x0008, ///< …
…
…
};
然後,我們在初始化我們世界對象時,可以更根據要求來設置對象的類別標誌位和遮罩標誌位。
例如如下代碼:
// Create ball body and shape 創建球的body
b2Body *body;
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody; ///< 指定body的類型爲dynamic body。默認值是static body,那意味着那個body不能被移動也不會參與仿真。
ballBodyDef.position.Set(BALLPOSITION[m_baBall[i].size].x/PTM_RATIO, BALLPOSITION[m_baBall[i].size].y/PTM_RATIO);
ballBodyDef.userData = m_baBall[i].ball; ///< 設置body的user data屬性爲籃球精靈。可以設置任何東西,但是,你設置成精靈會很方便,特別是當兩個body碰撞的時候,你可以通過這個參數把精靈對象取出來,然後做一些邏輯處理。
body = m_world->CreateBody(&ballBodyDef);
b2CircleShape circle;
circle.m_radius = m_baBall[i].ball->getWidth()/2.0f/PTM_RATIO;
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
<strong>ballShapeDef.filter.categoryBits<span style="white-space:pre"> </span>= BOX2D_MASK_BALL;
ballShapeDef.filter.maskBits<span style="white-space:pre"> </span>= BOX2D_MASK_WORD | BOX2D_MASK_BALL ;</strong>
ballShapeDef.density = 1.0f; ///< 單位體積的質量(密度)。因此,一個對象的密度越大,那麼它就有更多的質量,當然就會越難以移動.
ballShapeDef.friction = 0.2f; ///< 就是摩擦力。它的範圍是0-1.0, 0意味着沒有摩擦,1代表最大摩擦,幾乎移不動的摩擦。
ballShapeDef.restitution = 0.5f; ///< 回覆力。它的範圍也是0到1.0. 0意味着對象碰撞之後不會反彈,1意味着是完全彈性碰撞,會以同樣的速度反彈。
body->CreateFixture(&ballShapeDef);
這裏我們設置我們的對象爲“我是一個BOX2D_MASK_BALL”, 我可以和“BOX2D_MASK_WORD| BOX2D_MASK_BALL ”發生碰撞。
如何動態改變類別標誌位和遮罩標誌位:
在很多情況下,我們的世界對象達到某個條件時,我們要改變其類別標誌位和遮罩標誌位。那麼,我們要首先獲取這個世界對象的內容,然後在該別其值。具體方法,可以參考如下:
for (b2Body *b = m_world->GetBodyList(); b; )
{
///遍歷world對象裏面的所有body,然後看body的user data屬性是否爲空,如果不爲空,就可以強制轉換成精靈對象。接下來,就可以根據body的位置來更新精靈的位置了。
if (b->GetUserData() != NULL)
{
b2Body* b2node = b;
b = b2node->GetNext();
boxSprite* ballData = (boxSprite*)b2node->GetUserData();
if (ballData->getDead()) ///< 改變對象類別、遮罩標誌位
{
<strong> b2Fixture* fixture = b2node->GetFixtureList();
b2Filter filter = fixture->GetFilterData();
//change whatever you need to, eg.
filter.categoryBits = BOX2D_MASK_BASKET;
filter.maskBits = BOX2D_MASK_WORD | BOX2D_MASK_BASKET ;
fixture->SetFilterData(filter);
</strong> ballData->setDead(false);
}
ballData->setPosition(ccp(b2node->GetPosition().x*PTM_RATIO,
b2node->GetPosition().y*PTM_RATIO));
ballData->setRotation(-1*CC_RADIANS_TO_DEGREES(b2node->GetAngle()));
}
else
{
b = b->GetNext();
}
}
具體,類別標誌位和遮罩標誌位的功能,就差不多是這樣了。
分組索引:
分組索引優先於類別和遮罩標誌位,具體作用如下:
-定製器如果有分組索引爲零,那麼使用分類/遮罩規則
-如果兩個分組索引的值不爲零並且不相同,那麼使用分類/遮罩規則
-如果兩個分組索引的值相同,並且爲正數,則產生碰撞
-如果兩個分組索引的值相同,並且爲負數,則不產生碰撞
我們設置對象時,對象的分組索引默認爲0。
好的,Box2D的碰撞過濾,就差不多是這樣的。希望對大家有點用處。下面是我的qq:906391500,歡迎大家一起交流,一起成長。