Box2D C++ tutorials - Jumping

Last edited: July 14 2013

Chinese version -> 中文

Jumping


A platform game character's gotta jump, right? Let's take a look at some different ways of implementing a jump. We already kind of did this in the forces and impulses topic, but now we'll think about how each method would fit into a game. Start with the same scene as for the moving at constant speed topic, with the fenced in area and the left/right controls to move a dynamic body. Jumping

Setting the velocity directly


When the player character jumps their velocity changes, so let's try doing that to start with. Add a case to the Keyboard() function to get a jump input from the user:
1
2
3
4
5
6
7
  case 'j': //jump
  {
    b2Vec2 vel = body->GetLinearVelocity();
    vel.y = 10;//upwards - don't change x velocity
    body->SetLinearVelocity( vel );
  }
  break;
Now using the j key will directly set the velocity, which as we know by now is not a physically realistic method. There are a few things to notice about this method that might make it unsuitable for a jump in a platform game. The momentum of the body is not taken into account, so the player can jump with equal strength even when they are falling quickly. The player can also jump in mid-air - however this might be what you want, say for a 'double-jump' feature. For now we'll settle for this jumping in mid-air, and in a later topic we'll look at how to determine when the player is standing on the ground or not.

Using a force


When you jump in real life, you are applying a force upwards on your body, so let's see if we can do that by using a strong force for a few timesteps. In the Keyboard() function we will simply make a note that the jump was started, and apply the force in the Step() function. Use a class member variable to keep track of how many more time steps the force should be applied:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  //class member variable
  int remainingJumpSteps;
  
  //in constructor
  remainingJumpSteps = 0;
  
  //keyboard function
  case 'k': //jump
    remainingJumpSteps = 6;// 1/10th of a second at 60Hz
    break;
  
  //in Step() function
  if ( remainingJumpSteps > 0 ) {
    body->ApplyForce( b2Vec2(0,500), body->GetWorldCenter() );
    remainingJumpSteps--;
  }
Now use the k key to try this out. The behaviour is similar to before, but this time when the body is falling, jumping has less effect since the existing downward momentum is taken into account. The magnitude used for the force here is just a number that seemed about right. If you want to specify the take-off velocity though, you could use the same formula as for finding forces to move the body left/right at a desired velocity (f=mv/t), and apply it evenly across all time steps:
1
2
3
4
5
6
7
8
  if ( remainingJumpSteps > 0 ) {
    //to change velocity by 10 in one time step
    float force = body->GetMass() * 10 / (1/60.0); //f = mv/t
    //spread this over 6 time steps
    force /= 6.0;
    body->ApplyForce( b2Vec2(0,force), body->GetWorldCenter() );
    remainingJumpSteps--;
  }

Using an impulse


This is probably what you want in most cases, essentially the same force as above but applied in only one time step to take full effect instantly. As in the previous topics we can just leave out the time component:
1
2
3
4
5
6
7
  case 'l':
  {
    //to change velocity by 10
    float impulse = body->GetMass() * 10;
    body->ApplyLinearImpulse( b2Vec2(0,impulse), body->GetWorldCenter() );
  }
  break;
This gives a behaviour closer to direct setting of the velocity than the force does, because each time step the force is applied, gravity gets a chance to push back. If you look closely you'll see that the forced jump does not quite go as high as the others. However if you have a situation where you want the jump to look a little softer, instead of like the character has been hit from below by a giant hammer, the force method might be useful.

Other methods


In real life, for every action there is an equal and opposite reaction. If you use your legs to push your body up into a jump, whatever you were standing on just got pushed down upon with an equal force. None of the methods above take this into account, and most likely in your game they don't need to. But if you wanted to have this modeled accurately, there are two ways you could do it, both of which we need some more study in order to implement so I'll just mention them in passing for now.

After first making sure that the player is standing on something, you could then apply the same force/impulse to that object if it's a dynamic body. This would look better when you are jumping on a swing bridge, for example, the bridge would get a realistic kick downwards as the player jumps instead of merely having the player's weight removed. For this we'll need to know how to tell what the player is touching.

Using a prismatic (sliding) joint to join two bodies together is probably the most accurate way to model a character jumping. In this method the player would have a main body like we are using here, and another smaller body joined to it which can slide vertically. The joint motor could be used to shove this smaller body downwards, much like you shove your legs downwards to jump. This has the nice advantage that we don't need to bother checking if the player is standing on anything because if it's not, pushing the 'legs' downward will just do nothing. We also get an opposite force applied downwards to whatever was under the player eg. to kick the swing bridge downwards. Furthermore, if the player is standing on something 'soft' like a swing bridge, the jump will be less effective, just like real-life. Of course apart from being more work to implement, this approach has it's own issues to consider. I hope to discuss this method in another topic after we've covered joints and joint motors.

Stopping the body from rotating


You've probably noticed the body is still freely rotating which is starting to feel a bit out of place now that we are starting to look at the body as a player character. The recommended way to stop this is simply to set the body as a fixed rotation body:
1
  body->SetFixedRotation(true);