1.Box2D 碰撞過濾實現機制
在Box2D中,經過標誌位和掩碼的設計來實現碰撞過濾。其中有兩個標誌位和一個組別索引,分別是
git
categoryBits 類別標誌位
github
maskBits 掩碼標誌位
api
groupIndex 組別索引
ide
這三個屬性在碰撞過濾機制中扮演着重要的角色。
過濾規則
函數
若是兩個形狀材質的組別索引相同爲0,使用類別和掩碼計算規則來肯定是否碰撞
post
若是兩個形狀材質的組別索引相同爲正數,則直接肯定爲碰撞
性能
若是兩個形狀材質的組別索引相同爲負數,則直接肯定爲不碰撞
學習
若是兩個形狀材質的組別索引不相同,使用類別和掩碼計算規則來肯定是否碰撞
測試
額外的一些規則
spa
靜態剛體的形狀永遠不會與其餘靜態剛體的形狀發生碰撞
同一剛體上的形狀永遠不會發生碰撞
能夠選擇性的啓用或者禁止被關節約束的剛體形狀之間的碰撞
注:組別索引的過濾篩選要比類別和掩碼標誌位過濾篩選具備更高的優先級。
player1ShapeDef.filter.groupIndex = 1
player2ShapeDef.filter.groupIndex = 1
player3ShapeDef.filter.groupIndex = 2
player4ShapeDef.filter.groupIndex = -3
player5ShapeDef.filter.groupIndex = -3
player6ShapeDef.filter.groupIndex = 0
player7ShapeDef.filter.groupIndex = 0
複製代碼
根據上面的規則,咱們知道
player1與player2碰撞
player4與player5不碰撞
player1與player3,player3與player4,player5與player7等等這些組別索引不一樣的形狀材質,則要進一步根據類別和掩碼計算來肯定是否碰撞,後面咱們立刻會看到。
player6與player7組別索引相同爲0,也要進一步根據類別和掩碼計算來肯定是否碰撞
類別標誌位與掩碼標誌位的計算
Box2D支持16個類別,咱們對於任何一種形狀材質均可以設定類別標誌位。一般咱們能夠用一個16進制來表示一個類別標誌位,一共16位。好比 0x0004 ,展開其實就是 0x0000 0000 0000 0100 。
舉個例子:
playerShapeDef.filter.categoryBits = 0x0001
playerShapeDef.filter.maskBits = 0x0002
monsterShapeDef.filter.categoryBits = 0x0002
monsterShapeDef.filter.maskBits = 0x0001
複製代碼
計算規則:
讓 材質形狀A的類別標誌位 與 材質形狀B的掩碼標誌位 進行"按位與"運算獲得結果r1
讓 材質形狀B的類別標誌位 與 材質形狀A的掩碼標誌位 進行"按位與"運算獲得結果r2
r1與r2進行「邏輯與」,若是爲true,則形狀材質A與形狀材質B則碰撞,false則不碰撞
咱們根據上述規則得出結論,player與player之間不會碰撞,monster與monster之間也不會碰撞,但player與monster之間會發生碰撞。
2. Chipmunk2D 碰撞過濾實現
在Chipmunk中,一個shape具備 group 和 layer 的屬性,一塊兒來看下在 cpSpaceStep.c 中的一個檢測函數 queryReject ,即查詢否認拒絕。
static inline cpBool
queryReject(cpShape *a, cpShape *b)
{ return ( // BBoxes must overlap !cpBBIntersects(a->bb, b->bb) // Don't collide shapes attached to the same body. || a->body == b->body // Don't collide objects in the same non-zero group || (a->group && a->group == b->group) // Don't collide objects that don't share at least on layer. || !(a->layers & b->layers) // Don't collide infinite mass objects || (a->body->m == INFINITY && b->body->m == INFINITY) );
}
複製代碼
根據上面的一些否認狀況,咱們總結出過濾規則:
形狀a與形狀b的軸對齊包圍盒若是沒有發生碰撞,則不可能碰撞
若是形狀a和形狀b同屬於同一個剛體,則不會碰撞
若是形狀a和形狀b在相同的非0組,則不會碰撞,同在0組,或者不相等則考慮碰撞
若是形狀a的層和形狀b的層的按位與運算爲0,即意味着不在一個「位面」上,則不會碰撞
若是形狀a和b從屬的剛體的質量無限大,則不可能碰撞
這個是Chipmunk2D裏面的碰撞機制,看起來和Box2D不太同樣,啊哈?Cocos2dX對物理引擎進行了封裝,碰撞過濾的實現和這裏的方式卻有所不一樣。封裝的碰撞過濾接近了Box2D碰撞過濾的思路。讓咱們再來看下。
CCPhysicsShape/CCPhysicsBody類裏有三個重要的屬性,分別是
categoryBitmask
類別掩碼,該掩碼定義了剛體形狀屬於的類別。Chipmunk支持32種類別。經過對剛體或剛體形狀設定categoryBitmask與 contactTestBitmask,將二者按位與運算,咱們即可以指定遊戲中的哪些剛體之間能夠有相互做用,並在相互做用後並進行後續的通知。(該通 知直接影響到preSolve、postSolve、seperate等回調是否被調用)
默認值爲 0xFFFFFFFF 。
注意:相互做用並不等於就會產生碰撞反應,如傳感器(sensor)就是一例。
contactTestBitmask
接觸測試掩碼,該掩碼定義了哪些類別的剛體能夠與本剛體(或剛體形狀)產生相互做用。在物理空間中,每一個剛體的類別掩碼 (categoryBitmask)會和其餘剛體的接觸測試掩碼(contactTestBitmask)進行按位與運算,若是結果爲非0值,便會產生一 個 PhysicsContact 對象,並做爲參數傳入到physics world的代理方法內。爲了性能考慮,咱們只會設定咱們關注的相互做用的的掩碼。
默認值爲 0x00000000 。
collisionBitmask
碰撞掩碼,該掩碼定義了哪些類別的剛體能夠與本剛體(或剛體形狀)發生碰撞。當剛體彼此接觸的時候,可能會發生碰撞反應。此時該剛體的碰撞掩碼 (collisionBitmask)會與另一個剛體的類別(categoryBitmask)進行按位與運算,若是結果爲非0值,該剛體就會受到碰撞 影響。每一個剛體均可以選擇是否要受到碰撞影響。例如,你能夠經過設定碰撞掩碼來避免碰撞計算帶來的剛體速度的改變。
默認值爲 0xFFFFFFFF 。
另外值得一提的是,封裝後的CCPhysicsShape和CCPhysicsBody的group屬性和Chipmunk2D的group對過濾規則的 影響不同!!!這裏要注意下。上面總結的第三條是Chimunk2D的group的過濾規則,但在Cocos2DX封裝之下的group,卻採起了和 Box2D同樣的group過濾規則,即
若是兩個形狀材質的組別索引相同爲正數,則直接肯定爲碰撞
若是兩個形狀材質的組別索引相同爲負數,則直接肯定爲不碰撞
組別索引的過濾篩選要比掩碼過濾篩選具備更高的優先級。 以前我覺得這是官方的一個bug,提過一個Issule給官方團隊,見這裏 https://github.com/cocos2d/cocos2d-x/pull/6148 。官方解釋的緣由是對物理引擎的封裝要隱藏掉具體的使用哪一個引擎的細節,而更關心的是友好的api,性能和功能性,另一方面是對於有SpriteKit 開發經驗的開發者要更友好點。解釋能夠接受,但感受怪怪的,這裏的封裝建構在Chipmunk2D之上,但group的過濾倒是Box2D的規則。換個角 度想,若是不叫group,或許更好接受點。 關於在Cocos2DX v3.x裏面如何理解Chipmunk2D的碰撞過濾,能夠參考這個簡單的 demo 思考:爲何ball1與ball2不碰撞,box1與ball一、ball2不碰撞,box2與ball一、ball2碰撞?改變他們的group會怎麼樣?對他們的一些掩碼從新賦值會怎麼樣?朋友們能夠嘗試着設定不一樣的掩碼來觀察,方便理解其中的規則。 歡迎朋友們關注這個基礎概念demo的項目,在學習過程的測試demo能夠提交個pull request過來,一塊兒來豐富這個項目