0

I'm writing a game for Mac OS using cocos2D and Box2D. I've added a b2ContactListener subclass to my world as follows:

contactListener = new ContactListener();
world->SetContactListener(contactListener);

This works perfectly, but I am unsure of the best/accepted way to access the contact listener from other classes that don't currently have a direct reference to the contact listener.

I know I can pass a reference to other classes that need it, but what I was wondering is if there is a better way. More specifically, although I can't find a method to do this, is there some equivalent of this:

world->GetContactListener();

in Box2D?

The reason I am trying to do this is simply because I would prefer to move some game logic (i.e. whether a body is able to jump based on information from the contact listener) to the relevant classes themselves, rather than putting everything in the main gameplay class.

Thanks!

jonmorgan
  • 2,570
  • 2
  • 22
  • 27
  • 1
    The point of a contact listener is that you wait for it to tell you when something happens, you don't go and ask it yourself. Perhaps you could have the contact listener call a function in the game objects to let them know what other object they have touched. – iforce2d Jun 03 '12 at 09:15
  • @iforce2d that is not necessarily true. You MUST not change your box2d bodies state only after the simulation has been calculated for the current step. And the contact listener is used to analyze the current simulation step after it has been finished. – giorashc Jun 04 '12 at 10:28
  • What's not true? Where did I say he should change body states during the time step? The contact listener is called during a time step - you can analyze things then, as long as you don't change anything in the Box2D world until after the time step. Getting back to the original question though, a contact listener just serves as an entry point for the four functions BeginContact, EndContact, PreSolve and PostSolve. Typically it has no member variables, so there is no reason to get it, because there is nothing to get *from* it. Perhaps we should ask Jon what he'd do if he could get the listener. – iforce2d Jun 04 '12 at 17:30
  • @iforce2d if you add that as an answer I'll mark it as correct – jonmorgan Jun 05 '12 at 13:21

2 Answers2

3

A contact listener just serves as an entry point for the four functions BeginContact, EndContact, PreSolve and PostSolve. Typically it has no member variables, so there is no reason to get it, because there is nothing to get from it.

When one of these functions is called during a world Step, you can make a note of which two things touched/stopped touching etc, but you should not change anything in the world right away, until the time step is complete.

I think the crux of this question is the method used to 'make a note' of which things touched, but that's really up to you and depends on what kind of information you need. For example if you're only interested in BeginContact, then the absolute simplest way might be to just store which two fixtures touched as a list of pairs:

std::vector< std::pair<b2Fixture*, b2Fixture*> > thingsThatTouched;

//in BeginContact
thingsThatTouched.push_back( make_pair(contact->GetFixtureA(), contact->GetFixtureB()) );

//after the time step
for (int i = 0; i < thingsThatTouched.size(); i++) {
    b2Fixture* fixtureA = thingsThatTouched[i].first;
    b2Fixture* fixtureB = thingsThatTouched[i].second;
    // ... do something clever ...
}
thingsThatTouched.clear(); //important!!

For this to work you'll need to make the thingsThatTouched list visible from the contact listener function, so it could either be a global variable, or you could set a pointer to it in the contact listener class, or maybe have a global function that returns a pointer to the list.

If you need to keep track of more information such as what things stopped touching, or do something after the time step based on how hard things impacted when they touched etc, it will take a bit more work and becomes more specific. You might find these tutorials useful:

This one uses BeginContact/EndContact to update a list of which other things a body is touching, and uses it to decide if a player can jump at any given time: http://www.iforce2d.net/b2dtut/jumpability

This one uses a similar method to look at what type of surfaces are currently under a car tire, to decide how much friction the surface has: http://www.iforce2d.net/b2dtut/top-down-car

This one uses PreSolve to decide whether two bodies (arrow and target) should stick together when they collide, based on the speed of the impact. The actual 'sticking together' processing is done after the time step finishes: http://www.iforce2d.net/b2dtut/sticky-projectiles

iforce2d
  • 8,194
  • 3
  • 29
  • 40
0

I think you simply can call GetContactList and then process all the contacts using iterator if you need to do it in some other place

Morion
  • 10,495
  • 1
  • 24
  • 33
  • I'm specifically trying to access a member variable of the `ContactListener` class, so that unfortunately won't help me. – jonmorgan Jun 03 '12 at 11:46
  • in this case you can provide access to your contact listener, creating it as singleton or make it an instance of some global data class, if you have it, but i don't really think, that you should do this. – Morion Jun 03 '12 at 12:03