Box2D詳解2 碰撞篩選

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,歡迎大家一起交流,一起成長。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章