Cocos2d with Box2d

Registering for collision between sprites of certain types

Consider the following case where you have multiple sprites with different tags, but you only care when sprites of a certain tag collide with sprites of another tag.
For example Mario colliding with the coins, some checkpoint (a bezier line set as sensor) and turtles in the level.
In all 3 cases you want to do a certain action.
Lets define the methods that will be called when collision occurs.
We can define, begin-end collision, pre collision and post collision methods. You can use one callback, all callback or only what you need, depending on your game.
-(void)beginEndCollisionBetweenMarioAndCoin:(LHContactInfo*)contact{
	if([contact contactType])
    	NSLog(@"Mario ... Coin begin contact");
    else
	    NSLog(@"Mario ... Coin end contact");
}
-(void)beginEndCollisionBetweenMarioAndTurtle:(LHContactInfo*)contact{
	if([contact contactType])
    	NSLog(@"Mario ... Turtle begin contact");
    else
	    NSLog(@"Mario ... Turtle end contact");
}
-(void)beginEndCollisionBetweenMarioAndCheckpoint:(LHContactInfo*)contact{
	if([contact contactType])
    	NSLog(@"Mario ... Checkpoint begin contact");
    else
	    NSLog(@"Mario ... Checkpoint end contact");
}
-(void)preCollisionBetweenMarioAndCoin:(LHContactInfo*)contact{
    NSLog(@"Mario ... Coin");
}
-(void)preCollisionBetweenMarioAndTurtle:(LHContactInfo*)contact{
    NSLog(@"Mario ... Turtle");
}
-(void)preCollisionBetweenMarioAndCheckpoint:(LHContactInfo*)contact{
    NSLog(@"Mario ... Checkpoint");
}
-(void)postCollisionBetweenMarioAndCoin:(LHContactInfo*)contact{
    NSLog(@"Mario ... Coin");
}
-(void)postCollisionBetweenMarioAndTurtle:(LHContactInfo*)contact{
    NSLog(@"Mario ... Turtle");
}
-(void)postCollisionBetweenMarioAndCheckpoint:(LHContactInfo*)contact{
    NSLog(@"Mario ... Checkpoint");
}

Now that we have defined our functions that will perform the actions we need, lets instruct LevelHelper to call the apropriate action when a collion occurs.
So after we have loaded our level we do:
//creating the objects
[lh addObjectsToWorld:world cocos2dLayer:self];

...                
        
[lh useLevelHelperCollisionHandling];//necessary or else collision in LevelHelper will not be performed

[lh registerBeginOrEndColisionCallbackBetweenTagA:COIN
               	    	             andTagB:MARIO
	                	           idListener:self
    	        	    	      selListener:@selector(beginEndCollisionBetweenMarioAndCoin:)];

[lh registerBeginOrEndColisionCallbackBetweenTagA:CHECKPOINT
                    	          andTagB:MARIO
                        	  idListener:self
                          	selListener:@selector(beginEndCollisionBetweenMarioAndCheckpoint:)];

[lh registerBeginOrEndColisionCallbackBetweenTagA:TURTLE
                               andTagB:MARIO
                          idListener:self
                         selListener:@selector(beginEndCollisionBetweenMarioAndTurtle:)];


[lh registerPreColisionCallbackBetweenTagA:COIN
               	                 andTagB:MARIO
                	           idListener:self
            	    	      selListener:@selector(preCollisionBetweenMarioAndCoin:)];

[lh registerPostColisionCallbackBetweenTagA:COIN
               	                 andTagB:MARIO
                	           idListener:self
            	    	      selListener:@selector(postCollisionBetweenMarioAndCoin:)];
        
[lh registerPreColisionCallbackBetweenTagA:CHECKPOINT
                              andTagB:MARIO
                          idListener:self
                          selListener:@selector(preCollisionBetweenMarioAndCheckpoint:)];

[lh registerPostColisionCallbackBetweenTagA:CHECKPOINT
                              andTagB:MARIO
                          idListener:self
                          selListener:@selector(postCollisionBetweenMarioAndCheckpoint:)];

        
[lh registerPreColisionCallbackBetweenTagA:TURTLE
                               andTagB:MARIO
                          idListener:self
                         selListener:@selector(preCollisionBetweenMarioAndTurtle:)];

[lh registerPostColisionCallbackBetweenTagA:TURTLE
                               andTagB:MARIO
                          idListener:self
                         selListener:@selector(postCollisionBetweenMarioAndTurtle:)];

In the collision handling functions the argument "contact" will return the info about the collision.

In order to get the bodies from the contact info we do as follows:
-(void)preCollisionBetweenMarioAndCoin:(LHContactInfo*)contact{
    NSLog(@"Mario ... Coin");
    b2Body* coin = [contact bodyA];
    b2Body* mario= [contact bodyB];    
}
More info is available in the LHContactInfo class.
The following table describes the available attributes.
LHSprite* sprA = [contact spriteA]; 
Sprite with Tag A. Use this to get the exact LHSprite* which has the tag A.
LHSprite* sprB = [contact spriteB]; 
Sprite with Tag B. Use this to get the exact LHSprite* which has the tag B.
[contact contactType];
In the case of beginEnd callback this will tell what kind of contact this is.
Returns the following values:
1 if its begin contact
0 if its end contact
-1 if its pre solve contact
-2 if its post solve contact
A better aproach is to use the LH_CONTACT_TYPE enum that contains the following definition
enum LH_CONTACT_TYPE
{
    LH_BEGIN_CONTACT = 1,
    LH_END_CONTACT = 0,
    LH_PRE_SOLVE_CONTACT = -1,
    LH_POST_SOLVE_CONTACT = -2
};
e.g
if(LH_BEGIN_CONTACT == [contact contactType])
{
	NSLog(@"Contact between objects has just begin");
}
b2Body* bodyA = [contact bodyA]; 
Body with Tag A. Use this to get the exact body which has the tag A.
b2Body* bodyB = [contact bodyB]; 
Body with Tag B. Use this to get the exact body which has the tag B.
b2Contact* boxContact = [contact contact];
The Box2d contact info - Look inside this class to find get all the info.
b2Contact* boxContact = [contact contact];
The Box2d contact info - Look inside this class to get all the info.
const b2Manifold* oldManifold = [contact oldManifold];
old manifold info. Available only at pre collision. Can be NULL if one of the body in the current contact is Sensor. Check for NULL before using this. Get the current manifold info from the b2Contact class.
const b2ContactImpulse* impulse = [contact impulse];
the impulse info of the contact. Available only at post collision. Can be NULL if one of the body in the current contact is Sensor. Check for NULL before using this.

Removing a sprite during collision

Box2d has a unique way of handling collision and so it locks bodies when they collide. As such any attempt to remove a body during contact will make Box2d to assert and crash the game (safe mechanism).
LevelHelper code comes to the resque.
In your contact callback method just mark the sprite you want for removal as the example above.
-(void)beginEndCollisionBetweenMarioAndCoin:(LHContactInfo*)contact{
	if(LH_END_CONTACT == [contact contactType])
	{
		[loader markSpriteForRemoval:[info spriteA]];
	}
}
IMPORTANT NOTE:
When using BeginOrEnd callbacks you should only mark sprites in one of the contact type and never in both.
So if you marked the sprite for removal in LH_BEGIN_CONTACT, then DO NOT mark it again in LH_END_CONTACT.
Not respecting this rule will make box2d assert and crash your game.

Do not mark a sprite for removal in multiple collision callback. e.g Don't mark a sprite in beginEndCallback and then mark it again in pre or post solve callbacks.



Marking a sprite does not remove it. In order to actually remove a sprite, call removeMarkedSprites at the end of your tick method.
-(void) tick: (ccTime) dt
{
	...
	
	//Iterate over the bodies in the physics world
	for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
	{
		if (b->GetUserData() != NULL) 
        {
			//Synchronize the AtlasSprites position and rotation with the corresponding body
			CCSprite *myActor = (CCSprite*)b->GetUserData();
            
            if(myActor != 0)
            {
                //THIS IS VERY IMPORTANT - GETTING THE POSITION FROM BOX2D TO COCOS2D
                myActor.position = [LevelHelperLoader metersToPoints:b->GetPosition()];
                myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());		
            }
            
        }	
	}	
    
    .....
    
    
    [lh removeMarkedSprites]; //this will remove the sprites
}

Static vs Static Collision or Manually moved static bodies vs all others

Box2d does not register any collision when a static body touches another static body or when a static body that was moved by you has enter in contact with another body of any type.
This is normal behaviour as you don't want all walls or platforms that are on top of each other to trigger a collision on every frame.

But there are situations when you want to know when such collision has occure, like for example when a platform that is moving up and down has enter in collision with the player.
Because i was asked this many times, here is how to do it.

Imagine you have a platform (static body) that moved up and down on a path defined in LevelHelper.
Imagine you want a collision callback when this platform has touched the player.
You register for the collision but nothing happens.
Sollution:
Duplicate the platform again, set it as invisible by unselecting "Visible" in LevelHelper.
Set the new platform physic property as "Is Sensor", "Dynamic", "Is Bullet", "Fixed Rotation". All other property as you see fit.
Set the tag on this new platform to match the tag of the initial platform.
Attach this new platform to the original platform with a Distance Joint.
Now, the new platform that will be invisible in your game will follow your static platform because its connected with a joint.
This invisible platform will trigger the collision for you and so you know when to perform certain actions.