有时候需要避免刚体间的碰撞,那么可以使用filter。它是框架的一个属性,提供了碰撞筛选功能。用法比较有意思,经过了多次的试错,才找到其规则,因此有必要进行一些总结。
filter有三个属性,可以从引擎源代码得知:
struct b2Filter { b2Filter() { categoryBits = 0x0001; maskBits = 0xFFFF; groupIndex = 0; } /// The collision category bits. Normally you would just set one bit. uint16 categoryBits; /// The collision mask bits. This states the categories that this /// shape would accept for collision. uint16 maskBits; /// Collision groups allow a certain group of objects to never collide (negative) /// or always collide (positive). Zero means no collision group. Non-zero group /// filtering always wins against the mask bits. int16 groupIndex; };
这三个属性都是uint16类型的,其中categoryBits和maskBits用二进制来表示(用十六进制初始化,但是是二进制用法)。关于每个属性的意义,虽然英文注释已经说明了很多了,但只有经过了实践,方才真正理解。在查阅了BOX2D手册后,得知filter有两种碰撞筛选策略。
1.categoryBits和maskBits组合判断
categoryBits代表该刚体所属的种族,它的值要设置成2的N次方,这是因为其16位二进制数只能有一位是1,其余都是0。maskBits代表该刚体被允许碰撞的刚体种群(即另一个刚体的categoryBits)。根据这两个属性,引擎给出了一个碰撞筛选规则:
uint16 catA = fixtureA.filter.categoryBits; uint16 maskA = fixtureA.filter.maskBits; uint16 catB = fixtureB.filter.categoryBits; uint16 maskB = fixtureB.filter.maskBits; if ((catA & maskB) != 0 && (catB & maskA) != 0) { // fixtures can collide }
可以看出,两个刚体要相撞,必须是“你情我愿”的,一方不愿意,那么就撞不了了。又从maskBits默认值0xFFFF得知,默认情况下,一个刚体是被允许和任意刚体发生碰撞的,当然这个刚体必须是动态刚体。
那么,如何设置刚体的maskBits的值使其允许和一个以上的刚体发生碰撞呢?那就是使用按位或操作:|,这样该刚体的maskBits和要碰撞的刚体的categoryBits做&操作后,就都不会为0了,满足了碰撞筛选规则。如下代码所示,cat_b2代表的刚体,被允许和和cat_ground刚体以及cat_b1刚体都发生碰撞:
fixturedef.filter.categoryBits = cat_b2;
fixturedef.filter.maskBits = cat_ground | cat_b1;
2.group分组判断
这个比较简单,但要使其生效,那么两个刚体的groupIndex属性值必须一致且不为0。接下来再判断其值的正负,正一定相撞,负一定不相撞。且一旦满足了group分组判断策略,categoryBits和maskBits组合判断策略就无效了。也就是说该判断策略的优先级要高。如下代码所示,fixture1和fixture2是一定相撞的(相等、不为0,正),而fixture3和fixture4是一定不相撞的(相等,不为0,负):
fixture1Def.filter.groupIndex = 1; fixture2Def.filter.groupIndex = 1; fixture3Def.filter.groupIndex = -1; fixture4Def.filter.groupIndex = -1;
动态地改变刚体的碰撞规则
除了通过初始化filter的各种属性来指定碰撞规则,还可以通过b2Fixture::SetFilterData函数随时改变刚体框架的filter,从而根据游戏逻辑需求动态地改变刚体的碰撞规则。