Check the Filter
Box2D allows filtering of collision contacts into groups. In my case I require a boundary at center-screen to limit game player movement, but need a third piece to cross the center barrier without being stopped. Box2D bodies in a group tagged ‘-1′ will not have their vectors changed when a collision is detected.
Without making changes to a playerPiece class, I can add this grouping information to a ‘ball’ class to give it free movement across the board.
This example is gathered from the Box2D documentation and the testbed files in Cocos2D, and assumes you have a b2World and b2ContactListener already initialized.
Firstly, in the main GamePlay.mm file, a b2ContactFilter class needs to be added:
#pragma mark ContactFilter
class myContactFilter : public b2ContactFilter
{
// There are more callbacks than this one, see manual
bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB);
};
// add a method to test the collision data for filter settings
bool myContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
{
b2Filter filter1 = fixtureA->GetFilterData();
b2Filter filter2 = fixtureB->GetFilterData();
// test to let pass or calculate collision results
if(filter1.groupIndex == filter2.groupIndex) {
return false;
} else {
return true;
}
}
..then add the ContactFilter to the GamePlay.mm init function:
-(id) init
{
if( (self=[super init])) {
[...] // add b2World and groundbox
// start b2World
world = new b2World(gravity, doSleep);
// init a ContactFilter class
world->SetContactFilter(new myContactFilter);
}
}
Next in the main GamePlay.mm class, we add the Box2D edge line at screen center:
-(id) init
{
if( (self=[super init])) {
[...] // add b2World and groundbox
// start b2World
world = new b2World(gravity, doSleep);
// init a ContactFilter class
world->SetContactFilter(new myContactFilter);
// center bar
b2PolygonShape polygon;
polygon.SetAsEdge(b2Vec2(0,(screenSize.height / 2) / PTM_RATIO),
b2Vec2(320/PTM_RATIO,(screenSize.height / 2) / PTM_RATIO));
// calculates horizontal and vertical screen centers
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 0.0f;
//zero density makes the object 'static', behaves like an obstacle
// add the contact filter group with an index of -1.
//Category and mask bits are other ways to match collision groups
of colliding and non-colliding Box2D bodies.
boxShapeDef.filter.groupIndex = -1;
//boxShapeDef.filter.categoryBits = 0x0004;
//boxShapeDef.filter.maskBits = 0xFFFF;
b2BodyDef boxBodyDef;
b2Body* body3 = world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
}
return self;
}
Lastly in my external Ball.mm class, I add similar filter information to its init function:
// initialize the Box2D body in Ball.mm external class:
- (id)initWithPos:(CGPoint)points {
if(self = [super init]) {
[self setBodyDef:new b2BodyDef];
[self bodyDef]->position.Set(points.x/PTM_RATIO, points.y/PTM_RATIO);
//PTM_RATIO converts Box2D units(m) into Cocos2D units(px)
[self bodyDef]->linearDamping = 0.2f;
[self setShapeDef:new b2CircleShape];
[self shapeDef]->m_radius = 22.0f/PTM_RATIO;
[self setFixtureDef:new b2FixtureDef];
[self fixtureDef]->shape = shapeDef;
[self fixtureDef]->friction = 0.06f;
[self fixtureDef]->density = 0.8f;
[self fixtureDef]->restitution = 0.4f;//bounciness
// filter for passing middle boundary
self.fixtureDef->filter.groupIndex = -1;
self.fixtureDef->filter.categoryBits = 0x0004;
self.fixtureDef->filter.maskBits = 0xFFFF;
}
return self;
}
Now if the Box2D world is built correctly, bodies without the group index of -1 will be blocked while the Ball body passes through without stopping.