December 20, 2009 4

Lets try creating Box2d ContactListeners!

By admin in Uncategorized

A contactlistener (or sensor, used here on out interchangeably) is an object that as it says, listens for contacts. This object however does not react (in the physical sense) once it has collided.

For example picture a scenario where you had a pinball game, how would you know when the ball should be on the ‘ramp layer’ colliding with only the ramps sides, but not any of the bumpers or flippers below? That’s where the contactlistener comes in! You simply place a sensor at the bottom of the ramp in this scenario, and once the contact occurs you can switch the balls collision mask, to that of the ramp.

In this post, I’ll be guiding you in creating such a contact listener, however like my previous post I’m not a fan of the copypasta blog entries so do expect to have to fill in some blanks.
Moreover, i’m not a great teacher so if any part is confusing as with previous post let me know in the comment area. I’ll adjust the entry, and you get more info as i get better at explaining.

Prerequisites:

  • Using Cocos2D 8.2 with Box2D template
  • Firstly, the problem for me came that I’m was not familiar with objective-c, but I have been learning it and felt pretty comfortable, but to create a contact listener I had to write THAT particular class in c++ because that is what box2d needs that class to be.
    That’s all well and good when looking at c++ it’s a very readable language but for me the stumbling point was creating a simple class from scratch as im not familiar with the syntax. Believe it or not googling that proved harder than I would of imagined.

    Create a ContactListener.h such that

    #import "Box2D.h"
    #import "b2Contact.h"
    class ContactListener : public b2ContactListener
    {
    public:
    	ContactListener();
     
    	void* userData; /// Use this to store application specific body data.
    	void BeginContact(b2Contact* contact); // When we first contact
    	void EndContact(b2Contact* contact); // When we end contact
    };

    Pretty simple huh, it actually is and that’s great!

    Now we’ll need to create the meat of this class, the beauty here is that we’ll be mixing c++ with objective-c so we can take advantage of objective-c message dispatching, and foundation framework.

    Create a contactListener.mm such that

    #import "ContactListener.h"
    #import "Box2D.h"
    #import "b2Contact.h"
    #import "GameScreen.h"
     
    // Implement contact listener.
    ContactListener::ContactListener(){};
     
    void ContactListener::BeginContact(b2Contact* contact)
    {
    	// Box2d objects that collided
    	b2Fixture* fixtureA = contact->GetFixtureA();
    	b2Fixture* fixtureB = contact->GetFixtureB();
    	// Sprites that collided
    	CocosNode* actorA = (CocosNode*) fixtureA->GetBody()->GetUserData();
    	CocosNode* actorB = (CocosNode*)  fixtureB->GetBody()->GetUserData();
     
    	// This is only true if for example a sprite touched something in your box2d simulation that was not a sprite such as the ground
    	// You may not want to return here, so keep that in mind
    	if(actorA == nil || actorB == nil) return;
     
    	// Information about the collision, such as where it hit exactly on each body 
    	b2WorldManifold* worldManifold = new b2WorldManifold();
    	contact->GetWorldManifold(worldManifold);
     
    	// Maybe you wanna handle it differently but for this example, we're going to simply use our global object (see previous post)
    	// To store the gamescene where these bodies exist and tell it they collided
    	[[Global instance]._gameScene onActorDidStartContact:actorA against:actorB at:worldManifold];
    }
     
    	// Implement contact listener.
    void ContactListener::EndContact(b2Contact* contact)
    {
    	// Box2d objects that collided
    	b2Fixture* fixtureA = contact->GetFixtureA();
    	b2Fixture* fixtureB = contact->GetFixtureB();
    	// Sprites that collided
    	CocosNode* actorA = (CocosNode*) fixtureA->GetBody()->GetUserData();
    	CocosNode* actorB = (CocosNode*)  fixtureB->GetBody()->GetUserData();
     
    	// This is only true if for example a sprite touched something in your box2d simulation that was not a sprite such as the ground
    	// You may not want to return here, so keep that in mind
    	if(actorA == nil || actorB == nil) return;
     
    	// Information about the collision, such as where it hit exactly on each body 
    	b2WorldManifold* worldManifold = new b2WorldManifold();
    	contact->GetWorldManifold(worldManifold);
     
    	// Maybe you wanna handle it differently but for this example, we're going to simply use our global object (see previous post)
    	// To store the gamescene where these bodies exist and tell it they collided
    	[[Global instance]._gameScene onActorDidEndContact:actorA against:actorB at:worldManifold];
    }

    That’s the meat of it, notice our file is .mm which indicates to the compiler that we will be using objective-c++ which is a mixture of c++ and objective-c. Hopefully this will get your over that initial hump when using Box2d ContactListeners

    Update per fchan:

    [Global instance]._gameScene, points to my currently alive and running game scene.
    I was not sure how to pass this message to the game, so i took the lazy way out (or smarter?, ok lazy) and created a pointer in my global class for it.

    The gamescene knows about it’s objects, in my case I have a bunny and many cars.
    So this is how i implemented it.

    -(void) onBunnyDidLandOnCar:(GameObject*)actor
    {
         // Nothing to see here, game ended already
    	if(self._gameIsOver) return;
    	// Cast the actor as the car
    	Car* car = (Car*) actor;
    	// Check if landing was valid
    	if(car._body->GetPosition().y + 3.0f > _junny._body->GetPosition().y) { // Too far below the roof of the car to count as landing on it
    		//NSLog(@"onJunnyDidLandOnCar: Too far below");
    		return; 
     
    // Do some other stuff
    ...
    }

    Tags:

    4 Responses to “Lets try creating Box2d ContactListeners!”

    1. fchan says:

      is it possible to show how you implemented the contactlistener class within the gamescene? or is that infomation all handled within the global instance?

    2. ob says:

      great tutorial.. but would it be possible to have a project sourcecode or a video demo so we can visualize it as well?

      THanks

    3. gaurav says:

      Hi , dont we have to say
      world ->setContactListener (contactListener)?
      my app crashes when i write this line :(

    4. admin says:

      Do you have any info on the crash? What’s the stack trace or error?

    Leave a Reply

    Powered by WP Hashcash

    Spam protection by WP Captcha-Free