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过来,一块儿来丰富这个项目