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.

You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

Comments

  1. On January 19, 2010 f says:

    Thanks for this, I am just starting to play with BOX2d.

    I am having some troubles grasping how to integrate box2d on external classes, like you have on Ball.mm

    Can you post a link to your complete class?

  2. On January 28, 2010 XyrisKenn says:

    Hi f, thank you for writing. I don’t have a Ball.mm class, as the tutorial and Cocos forum notes were distilled from my game code which has gotten complex.
    But I can say that the trick is just to make sure your Game class and Ball (or game piece class) can talk to each other.

    A variable like: Game *gamescene, @property (nonatomic, readonly, retain) gamescene gets added to Ball.mm. This lets the Ball external class find out about the Box2D world.

    In Game.mm, add a @class Ball note and of course Ball *myBall, @property (nonatomic, readonly, retain) myBall. This lets the main Game class communicate with the Ball class.

    Then in Ball.mm, refer to the game as [[self gamescene] world]->GetJointList() for example.

    If you have methods in both Ball.mm and Game.mm to call and set each other’s details and variables, it becomes easier.
    For example, I add the Ball’s texture from Game.mm by calling a method in Ball.mm, ‘addSprite’: [myBall addSprite];

    I hope this helps.

    Cheers,
    Kenn.