Box2D C++ tutorials - Moving at constant speed

Last edited: July 14 2013

Chinese version -> 中文

Moving a body at a constant speed


A common requirement in games is to make a body move at a constant speed. This could be a player character in a platform game, a spaceship or car, etc. Depending on the game, sometimes a body should gain speed gradually, in other situations you might want it to start and stop instantaneously. It is very tempting to use the SetLinearVelocity function to explicitly set the velocity for a body to accomplish this, and indeed it does get the job done, but this approach has its drawbacks. While it often looks fine on the screen, setting the velocity directly means the body is not correctly participating in the physics simulation. Let's see how we can use the more realistic forces and impulses to move a body at a desired speed.

We'll look at two situations, one where the body should immediately start moving at the desired speed, and one where it should accelerate gradually until it reaches a specified top speed. To start with we'll need a scene with one dynamic body, and we'll make some static body walls to fence it in. This fence will come in handy in some of the upcoming topics too. To keep track of what the user wants to do, we'll have a class variable to store the last received input.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  //enumeration of possible input states
  enum _moveState {
    MS_STOP,
    MS_LEFT,
    MS_RIGHT,
  };
  
  //class member variables
  b2Body* body;
  _moveState moveState;
  
  FooTest() {
    //body definition
    b2BodyDef myBodyDef;
    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;
    
    //create dynamic body
    myBodyDef.position.Set(0, 10);
    body = m_world->CreateBody(&myBodyDef);
    body->CreateFixture(&myFixtureDef);
    
    //a static body
    myBodyDef.type = b2_staticBody;
    myBodyDef.position.Set(0, 0);
    b2Body* staticBody = m_world->CreateBody(&myBodyDef);
    
    //add four walls to the static body
    polygonShape.SetAsBox( 20, 1, b2Vec2(0, 0), 0);//ground
    staticBody->CreateFixture(&myFixtureDef);
    polygonShape.SetAsBox( 20, 1, b2Vec2(0, 40), 0);//ceiling
    staticBody->CreateFixture(&myFixtureDef);
    polygonShape.SetAsBox( 1, 20, b2Vec2(-20, 20), 0);//left wall
    staticBody->CreateFixture(&myFixtureDef);
    polygonShape.SetAsBox( 1, 20, b2Vec2(20, 20), 0);//right wall
    staticBody->CreateFixture(&myFixtureDef);
  
    moveState = MS_STOP;
  }
Constant speed We'll need a Keyboard() function for input:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  void Keyboard(unsigned char key)
  {
    switch (key)
    {
      case 'q': //move left
        moveState = MS_LEFT;
        break;
      case 'w': //stop
        moveState = MS_STOP;
        break;
      case 'e': //move right
        moveState = MS_RIGHT;
        break;
      default:
        //run default behaviour
        Test::Keyboard(key);
    }
  }
From now, all further changes will be made in the Step() function to implement the movement behaviour depending on this input.

Setting velocity directly


Before we get started on the force/impulse methods, let's see how SetLinearVelocity works to directly specifying the velocity of the body. For many applications this may be good enough. Inside the Step() function, we will take whatever action is required each time step:
1
2
3
4
5
6
7
8
9
  //inside Step()
    b2Vec2 vel = body->GetLinearVelocity();
    switch ( moveState )
    {
      case MS_LEFT:  vel.x = -5; break;
      case MS_STOP:  vel.x =  0; break;
      case MS_RIGHT: vel.x =  5; break;
    }
    body->SetLinearVelocity( vel );
Here we are getting the current velocity and leaving the vertical component unchanged, and feeding it back because we only want to affect the horizontal velocity of this body.

Trying this code in the testbed you'll see that this setup gives us the instantaneous speed scenario. To implement a gradual acceleration up to a maximum speed, you could do something like this instead.
1
2
3
4
5
6
    switch ( moveState )
    {
      case MS_LEFT:  vel.x = b2Max( vel.x - 0.1f, -5.0f ); break;
      case MS_STOP:  vel.x *=  0.98; break;
      case MS_RIGHT: vel.x = b2Min( vel.x + 0.1f,  5.0f ); break;
    }
This will increase the velocity linearly by 0.1 per time step to a maximum of 5 in the direction of travel - with the default testbed framerate of 60fps the body will take 50 frames or just under a second to reach top speed. When coming to a stop the speed is reduced to 98% of the previous frame's speed, which comes to about 0.98^60 = a factor of about 0.3 per second. An advantage of this method is that these acceleration characteristics can be easily tuned.

Using forces


Forces are more suited to the gradual acceleration to top speed scenario, so let's try that first:
1
2
3
4
5
6
7
8
9
    b2Vec2 vel = body->GetLinearVelocity();
    float force = 0;
    switch ( moveState )
    {
      case MS_LEFT:  if ( vel.x > -5 ) force = -50;  break;
      case MS_STOP:  force = vel.x * -10; break;
      case MS_RIGHT: if ( vel.x <  5 ) force =  50; break;
    }
    body->ApplyForce( b2Vec2(force,0), body->GetWorldCenter() );
This is similar to the above in that the acceleration is linear and the braking is non-linear. For this example we have a pretty basic logic which simply applies the maximum force in every time step where the body is moving too slow. You will likely want to adjust this for the application you are making eg. a car might accelerate quickly at low speeds, but as it nears the maximum speed it's rate of acceleration decreases. For this you could just look at the difference between the current speed and the maximum speed and scale back the force as appropriate.

Remembering from the previous topic that forces act gradually, it might seem unlikely that we could use them to implement an instantaneous speed change. However, if we make the time span very short and the force very large, we can get the same effect as an impulse. First we need to do a little math...

The relationship between force and acceleration is f = ma where m is the mass of the body we're moving, a is acceleration which is measured in "units per second per second", and f is the force we want to calculate. The acceleration could also be called "velocity per second", since velocity and "units per second" are the same thing. So we could write this as f = mv/t where t is the length of time the force will be applied.

We can get m by using the body's GetMass() function. v will be the change in velocity we desire which is the difference between the maximum speed and the current speed. To get an instantaneous speed change effect, we would be applying the force for one time step or 1/60th of a second if using the default testbed framerate. Now we know everything except f, so we do something like this:
1
2
3
4
5
6
7
8
9
10
11
    b2Vec2 vel = body->GetLinearVelocity();
    float desiredVel = 0;
    switch ( moveState )
    {
      case MS_LEFT:  desiredVel = -5; break;
      case MS_STOP:  desiredVel =  0; break;
      case MS_RIGHT: desiredVel =  5; break;
    }
    float velChange = desiredVel - vel.x;
    float force = body->GetMass() * velChange / (1/60.0); //f = mv/t
    body->ApplyForce( b2Vec2(force,0), body->GetWorldCenter() );
This should give you the same behaviour as the SetLinearVelocity did, while still remaining a realistic physics scenario.

Using impulses


Astute readers will notice that the code immediately above is basically simulating an impulse. However since impulses already take into account the length of the simulation timestep, we can just take the time part out and get the same effect with ApplyLinearImpulse:
1
2
3
4
5
6
7
8
9
10
11
    b2Vec2 vel = body->GetLinearVelocity();
    float desiredVel = 0;
    switch ( moveState )
    {
      case MS_LEFT:  desiredVel = -5; break;
      case MS_STOP:  desiredVel =  0; break;
      case MS_RIGHT: desiredVel =  5; break;
    }
    float velChange = desiredVel - vel.x;
    float impulse = body->GetMass() * velChange; //disregard time factor
    body->ApplyLinearImpulse( b2Vec2(impulse,0), body->GetWorldCenter() );
For a gradual acceleration, just adjust the desired change in velocity as appropriate:
1
2
3
      case MS_LEFT:  desiredVel = b2Max( vel.x - 0.1f, -5.0f ); break;
      case MS_STOP:  desiredVel = vel.x * 0.98f; break;
      case MS_RIGHT: desiredVel = b2Min( vel.x + 0.1f,  5.0f ); break;