Box2D C++ tutorials - The 'can I jump' question

Last edited: July 14 2013

Chinese version -> 中文

Is my character on the ground?


If ever there was a question about Box2D, "how can I tell if my player character is on the ground?" is probably it. I think the reason this comes up so often is firstly because of the callback-style method of getting contact information - the callback method itself can throw off those who are not familiar with it - and also because keeping track of what is touching what is left to the programmer to implement instead of being built-in to the library. In either case, we have covered both of these topics already. The collision callbacks topic dealt with getting the necessary begin and end contact events, and the sensors topic showed an example where we kept track of a 'currently touching' list.

So what's left to talk about? Well, I'm guessing many people will find this page as their first stop when trying to tackle this problem, so I wanted to point out those previous two topics as better places to start for a proper understanding. And I also thought it would be good to make a demonstration of these techniques in the setting that they are most commonly wanted, the platformer game, rather than the battlefield scenario of the previous topics.

We'll look at an example which is quite effective at solving this question, yet not too complex. The player body will have a main fixture to represent the player itself, and another smaller sensor fixture attached underneath it to detect when something is under-foot. This foot sensor will keep track of what other fixtures in the world it is touching, and because we know it is directly beneath the player and not too big, we will just consider any situation where the foot sensor is touching something to be a jumpable situation. There are a couple of small tweaks we can make but that's the general idea.

Because these are concepts we've already covered just put in a different setting, we'll go one step further and instead of simply keeping track of whether the player is standing on something, we'll also keep track of what kind of something(s) are being stood on. The scene will have two types of boxes of different size, and depending on which the player is standing on we can alter his jumpability.


Preventing jumping in the air


Yes you guessed it, we're gonna start with the same scenario as for the Jumping topic. Dig that one up, and in the constructor add a section like this to create some bodies to jump around on in the scene:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  //add some bodies to jump around on
  {
      //body definition
      b2BodyDef myBodyDef;
      myBodyDef.position.Set(-5,5);
      myBodyDef.type = b2_dynamicBody;
  
      //shape definition
      b2PolygonShape polygonShape;
      polygonShape.SetAsBox(1, 1); //a 2x2 rectangle
  
      //fixture definition
      b2FixtureDef myFixtureDef;
      myFixtureDef.shape = &polygonShape;
      myFixtureDef.density = 1;
  
      for (int i = 0; i < 5; i++)
      {
          b2Fixture* fixture = m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
          fixture->SetUserData( (void*)1 );//tag square boxes as 1
      }
  
      //change size
      polygonShape.SetAsBox(0.5, 1); //a 1x2 rectangle
      
      for (int i = 0; i < 5; i++)
      {
          b2Fixture* fixture = m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
          fixture->SetUserData( (void*)2 );//tag smaller rectangular boxes as 2
      }
  }
This is getting pretty routine by now, the important point is that we have two different types of body there, with different shapes and user data tags. The user data does not refer to any other objects in the program, it's just a simple number tag so we can tell in the collision callback what type of things are colliding. Box2D tutorials - Can I jump here?
Now at the beginning of the constructor, we'll need to make a couple of changes to the 'player' box. Firstly we'll make it a bit taller so we can tell which one it is easier, and we will also add the foot-sensor fixture to it, with a user data tag of 3. You can edit the existing section to do this, but here is the full player body creation for clarity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  //player body
  {
      //body definition
      b2BodyDef myBodyDef;
      myBodyDef.type = b2_dynamicBody;
      myBodyDef.fixedRotation = true;
  
      //shape definition for main fixture
      b2PolygonShape polygonShape;
      polygonShape.SetAsBox(1, 2); //a 2x4 rectangle
  
      //fixture definition
      b2FixtureDef myFixtureDef;
      myFixtureDef.shape = &polygonShape;
      myFixtureDef.density = 1;
  
      //create dynamic body
      myBodyDef.position.Set(0, 10);
      m_body = m_world->CreateBody(&myBodyDef);
  
      //add main fixture
      m_body->CreateFixture(&myFixtureDef);
  
      //add foot sensor fixture
      polygonShape.SetAsBox(0.3, 0.3, b2Vec2(0,-2), 0);
      myFixtureDef.isSensor = true;
      b2Fixture* footSensorFixture = m_body->CreateFixture(&myFixtureDef);
      footSensorFixture->SetUserData( (void*)3 );
  }
Box2D tutorials - Can I jump here? NOTE: it's not necessary for this foot sensor to actually be a sensor, and it doesn't need to exist at all in order to get collision events, but it provides some advantages.
  • Firstly, since we know it's under the player, we know a collision with it means the player is standing
    on something. If we just used the main body to get collision events, those events could be from collisions
    with the walls or ceiling too, so we would have to check the direction before we could say it was a
    ground collision.
  • Secondly, as the player moves around, especially on slopes the main body tends to bounce slightly on
    the ground which causes a large number of begin/end events to occur in quick succession. If we were
    using the main body to determine jumpability, it's possible that the user could try to jump just when
    the main body is off the ground for a few milliseconds, even though it appears to be on the ground, and
    that would just be annoying huh? Using a sensor like this will cause a smoother and more reliable contact
    detection because the sensor will stay in contact with the ground during the tiny bounces.
  • Thirdly, having the foot sensor and the main body as separate fixtures means they can be given individual
    shapes as the situation requires. For example, in this case the foot sensor is much narrower than the main
    body meaning you wont be able to jump when you are teetering on the edge of something, but this could
    easily be adjusted by changing the size, shape or location of the sensor.
Now to know when we can jump, we just need to keep track of how many other fixtures are currently touching the foot sensor. Add a global variable to hold this number:
1
2
3
4
5
  //global scope
  int numFootContacts;
  
  //in constructor
  numFootContacts = 0;
Then we can use a collision callback similar to the standard one in the collision callbacks topic, except this time we are checking the fixture's user data instead of the body's user data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  class MyContactListener : public b2ContactListener
  {
      void BeginContact(b2Contact* contact) {
          //check if fixture A was the foot sensor
          void* fixtureUserData = contact->GetFixtureA()->GetUserData();
          if ( (int)fixtureUserData == 3 )
              numFootContacts++;
          //check if fixture B was the foot sensor
          fixtureUserData = contact->GetFixtureB()->GetUserData();
          if ( (int)fixtureUserData == 3 )
              numFootContacts++;
      }
  
      void EndContact(b2Contact* contact) {
          //check if fixture A was the foot sensor
          void* fixtureUserData = contact->GetFixtureA()->GetUserData();
          if ( (int)fixtureUserData == 3 )
              numFootContacts--;
          //check if fixture B was the foot sensor
          fixtureUserData = contact->GetFixtureB()->GetUserData();
          if ( (int)fixtureUserData == 3 )
              numFootContacts--;
      }
  };
Don't forget to make an instance of this class and tell the Box2D world to refer to it for callbacks, with SetContactListener !

Finally, to actually use this information, we need to ignore any attempts to jump when the foot sensor is not touching anything. You could add something like this to the relevant parts of the switch statement in the Keyboard function:
1
  if ( numFootContacts < 1 ) break;
And since we're demonstrating the jumpable state let's make it really clear by showing it on-screen too:
1
2
3
  //in Step function
  m_debugDraw.DrawString(5, m_textLine, "Can I jump here? %s", numFootContacts>0?"yes":"no");
  m_textLine += 15;
Now run the test, and you should see the jumpability state changing correctly as the foot sensor's contact state changes, and jumping will be prevented when the player body is not on the ground. Great! Well, almost. As mentioned earlier a little tweaking is still required.


Preventing jumping again immediately after just jumping


When jumping using ApplyForce or ApplyLinearImpulse, if you hold down the jump key you will find that although it's not as bad as before, the player can still jump really high. This is simply because even though the jump has started and the body is moving upwards, the keypress repeats many times while the body is still close enough to the ground for the sensor to touch, so many forces/impulses get added in that short time span. To counter this, we can just limit the frequency in which jumps are possible, eg. if the player has just jumped, they shouldn't be jumping again for at least another 100ms.

To code this you would typically use a fast timer to compare the times between when you jumped, and the current time. You can find implementations of such timers for various platforms around on the net, and the latest version of Box2D includes one too. Since the version of Box2D we are using, v2.1.2, does not have any timer we'll just make use of the fact that one timestep is 1/60 seconds to suffice as a basic timer. Let's stop the player from making jumps within 1/4 second of each other, which is 15 time steps at 60Hz. This should be enough time for the foot sensor to have moved above the ground.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  //class member variable
  int m_jumpTimeout;
  
  //in class constructor
  m_jumpTimeout = 0;
  
  //in Step()
  m_jumpTimeout--;
  
  //in Keyboard routine (only showing the impulse version here as an example)
  case 'l':
      if ( numFootContacts < 1 ) break;
      if ( m_jumpTimeout > 0 ) break;
      //ok to jump
      m_body->ApplyLinearImpulse( b2Vec2(0, m_body->GetMass() * 10), m_body->GetWorldCenter() );
      m_jumpTimeout = 15;
      break;


Preventing jumping depending on ground type


The above method is a basic way to stop the player from jumping when he's already in the air. How about if we wanted to allow jumping only when standing on certain objects? Or maybe the jump should be higher from certain objects, etc. Let's try making jumps possible only when standing on the larger boxes in the scene. To do this we'd like to have a list of what's currently being stood on, to check in the Keyboard function when the user presses the jump key. A list like this can be kept up to date by adding fixtures to it in BeginContact, and removing them in EndContact, just like the radar example in the sensors topic.

Alternatively, you could split the numFootContacts into two variables to keep track of the number of large boxes and the number of small boxes currently stood on separately - but this is quite limited information, and if you introduce a third type of box you'll then need a third integer variable to keep track of the new type, and so on. For this demonstration we'll go with the more useful method of keeping a list of the boxes currently stood on, because it's a better long-term solution, and also because I want to use the stood on boxes a little later.

Add a class member variable to hold the current set of boxes being stood on. You could make this a list of bodies but I will make it hold the original fixtures because remember we can also stand on the static 'ground' body which in a real game would likely have many fixtures and we would probably like to be able to tell them apart.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  //global variable (include <set>)
  set<b2Fixture*> fixturesUnderfoot;
  
  //revised implementation of contact listener
  class MyContactListener : public b2ContactListener
  {
    void BeginContact(b2Contact* contact) {
        //check if fixture A was the foot sensor
        void* fixtureUserData = contact->GetFixtureA()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.insert(contact->GetFixtureB());//A is foot so B is ground
        //check if fixture B was the foot sensor
        fixtureUserData = contact->GetFixtureB()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.insert(contact->GetFixtureA());//B is foot so A is ground
    }
  
    void EndContact(b2Contact* contact) {
        //check if fixture A was the foot sensor
        void* fixtureUserData = contact->GetFixtureA()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.erase(contact->GetFixtureB());//A is foot so B is ground
        //check if fixture B was the foot sensor
        fixtureUserData = contact->GetFixtureB()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.erase(contact->GetFixtureA());//B is foot so A is ground
    }
  };
Note that the only part of the contact listener we changed is to insert/erase from the set instead of incrementing/decrementing the counter. A set is a useful container that prevents the same object being added twice and is a little more convenient to erase from.

Now in the Keyboard function, instead of merely checking the number of stood on things, we'll check what type they are too. Specifically, we want to prevent jumping when the only thing being stood on is a small box. This means we check the current fixturesUnderfoot list, and if there is a fixture with user data tag of either 1 (a large box) or NULL (a static 'ground' fixture), then the jump should be permitted. Because this jumpability check is getting a bit long now and we want to call it from various places, I'll put this logic in a function of it's own to keep it tidy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  //class function
  bool CanJumpNow()
  {
      if ( m_jumpTimeout > 0 )
          return false;
      set<b2Fixture*>::iterator it = fixturesUnderfoot.begin();
      set<b2Fixture*>::iterator end = fixturesUnderfoot.end();
      while (it != end)
      {
          b2Fixture* fixture = *it;
          int userDataTag = (int)fixture->GetUserData();
          if ( userDataTag == 0 || userDataTag == 1 ) //large box or static ground
              return true;
          ++it;
      }
      return false;
  }
One drawback with the set container is the slightly more inconvenient way you need to iterate over the contents...
Replace the previous checks using numFootContacts with this function, and your ready to go. Try the program and check that you can only jump from the large boxes or the normal static ground.


Adding some reaction to the environment


Well that about covers the basics of preventing jumping in certain situations. As one last little demonstration, let's give the currently stood on boxes a kick downwards when the player jumps. This is one advantage of keeping a list instead of just an integer to keep track of them.

In the Keyboard function when a jump is performed, you could do something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  //in Keyboard
  //kick player body upwards
  b2Vec2 jumpImpulse(0, m_body->GetMass() * 10);
  m_body->ApplyLinearImpulse( jumpImpulse, m_body->GetWorldCenter() );
  m_jumpTimeout = 15;
  
  //kick the underfoot boxes downwards with an equal and opposite impulse
  b2Vec2 locationOfImpulseInPlayerBodyCoords(0, -2);//middle of the foot sensor
  b2Vec2 locationOfImpulseInWorldCoords =
          m_body->GetWorldPoint(locationOfImpulseInPlayerBodyCoords);
  set<b2Fixture*>::iterator it = fixturesUnderfoot.begin();
  set<b2Fixture*>::iterator end = fixturesUnderfoot.end();
  while (it != end)
  {
      b2Fixture* fixture = *it;
      fixture->GetBody()->ApplyLinearImpulse( -jumpImpulse, locationOfImpulseInWorldCoords );
      ++it;
  }
Now when jumping from a large box, you should see the box react to being stomped on during the jump. Admittedly this scene is not a very good one for illustrating this because if the large box is directly on the static ground no reaction will be visible - try jumping from a large box which is sitting on top of other boxes, and then you should see it kicked downwards a little.