Box2D C++ tutorials - Forces and impulses
Last edited: July 14 2013Chinese version -> 中文
Forces and impulses
To move things around, you'll need to apply forces or impulses to a body. Forces act gradually over time to change the velocity of a body while impulses can change a body's velocity immediately. As an example, imagine you have a broken down car and you want to move it. You could use a force by slowly driving another car up next to it until their bumpers touched and then push it, causing it to accelerate over time. Or you could use an impulse by driving the other car into it at full speed. Both methods have their place depending on what you are trying to simulate.
You can also 'warp' a body instantaneously by simply setting its location. This can be handy for games where you need a teleport feature, but bear in mind that this is not realistic physics! The whole point of a physics simulator like Box2D is to make things look real, and to this end I would recommend using forces and impulses to move bodies as much as you can. Sometimes it might seem tricky to think of a way to do this, but in the real world everything happens via forces or impulses, so unless you are creating a feature which is obviously not real-world (teleport etc), keep thinking. It may end up giving you less problems down the line.
Angular movement can also be controlled by forces and impulses, with the same gradual/immediate characteristics as their linear versions. Angular force is called torque. Think of this as a twisting strength, like when you twist the cap off a bottle - the bottle doesn't go anywhere, but you are still applying a force (torque) to it.
In this topic, we'll make three bodies and try using each one of the above-mentioned methods to move and twist them. Let's set up a scene similar to the one in the fixtures topic, but with all the shapes the same.
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 | //class member variable to keep track of three bodies b2Body* bodies[3]; 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 identical bodies in different positions for (int i = 0; i < 3; i++) { myBodyDef.position.Set(-10+i*10, 20); bodies[i] = m_world->CreateBody(&myBodyDef); bodies[i]->CreateFixture(&myFixtureDef); } //a static floor to drop things on myBodyDef.type = b2_staticBody; myBodyDef.position.Set(0, 0); polygonShape.SetAsEdge( b2Vec2(-15,0), b2Vec2(15,0) ); m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef); } |

Linear movement
We need some way of affecting these bodies without using the mouse. Now would be a good time to check out the keyboard input feature of the testbed. Override the Keyboard function of the Test class to use a different method for each body:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void Keyboard(unsigned char key) { switch (key) { case 'q': //apply gradual force upwards bodies[0]->ApplyForce( b2Vec2(0,50), bodies[0]->GetWorldCenter() ); break; case 'w': //apply immediate force upwards bodies[1]->ApplyLinearImpulse( b2Vec2(0,50), bodies[1]->GetWorldCenter() ); break; case 'e': //teleport or 'warp' to new location bodies[2]->SetTransform( b2Vec2(10,20), 0 ); break; default: //run default behaviour Test::Keyboard(key); } } |
Run the test and try pressing the q/w/e keys. The impulsed body should react as if it was suddenly hit by something. The teleported body moves instantly to the new location, but notice that it retains its linear and angular velocity. What's happening with the body we applied the force to? Well, this Keyboard function is only called when we press the key down, not every timestep. In the example of car-pushing analogy, this would be like pushing the car for a fraction of a second, and then stopping. Since a force works over time, what we really need is some way to turn the force on and off instead of just blipping it on for a brief moment. We could use a class member variable to keep track of whether the force is on, and use the q key to toggle it. Make the following changes to the class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //class member variable bool forceOn; //inside constructor forceOn = false; //modified case for q key in Keyboard() function case 'q': forceOn = !forceOn;//toggle bool value break; //inside Step() function if (forceOn) bodies[0]->ApplyForce( b2Vec2(0,50), bodies[0]->GetWorldCenter() ); |
Hmm.... we applied the same magnitude (50) for both impulse and force, so why does the force seem to be weaker than the impulse? Well, remember that gravity is a force too. Try turning gravity off and the answer might become clear. The reason is that the force acts a little bit each timestep to move the body up, and then gravity acts to push it back down again, in a continual up down up down struggle. The impulse on the other hand, does all its work before gravity gets a chance to interfere. With gravity off, try turning the force on for about one second, then off. You will notice that after one second, the forced body has the same velocity as the impulsed body.
Now what about the last parameter of the apply force/impulse functions that we have ignored? So far we set this using the GetWorldCenter() of the body which will apply the force at the center of mass. As we can see, a force applied to the center of mass does not affect the rotation of the body. Imagine a CD on a friction-less flat surface, like an air-hockey table. If you put your finger in the hole in the middle of the CD and flick it across the table, it will not spin around as it moves. But if you do this with your finger anywhere else on the CD, it will spin around as it moves, and the further away from the middle your finger was, the more it will spin.
Let's offset the point at which we apply the force/impulse. Change the ApplyForce and ApplyLinearImpulse calls so that instead of using GetWorldCenter() to apply force at the center of mass, we'll apply it at the top right corner of the box:
1 2 3 4 5 | //before GetWorldCenter() //after GetWorldPoint( b2Vec2(1,1) ) |

Angular movement
Angular movement is controllable by using angular forces (torque) and angular impulses. These behave similar to their linear counterparts in that force is gradual and impulse is immediate. Let's add a couple of cases to the Keyboard() function to try them out. (Although there is a third case where the rotation can be set instanteously, we have already done that with SetTransform() above, so we'll skip that here.)
1 2 3 4 5 6 7 8 | case 'a': //apply gradual torque counter clockwise bodies[0]->ApplyTorque( 20 ); break; case 's': //apply immediate spin counter clockwise bodies[1]->ApplyAngularImpulse( 20 ); break; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //class member variable bool torqueOn; //inside constructor torqueOn = false; //modified case for a key in Keyboard() function case 'a': torqueOn = !torqueOn;//toggle bool value break; //inside Step() function if (torqueOn) bodies[0]->ApplyTorque( 20 ); |
As with the linear forces, given the same magnitude parameter, ApplyTorque will take one second to gain as much rotational velocity as ApplyAngularImpulse does immediately.