Coding top-down movement with Box2D (and a new project)

Coding top-down movement with Box2D (and a new project)

Sometimes you decide you want to code a specific game genre once in your life, and until you develop something like what your mind pictured, you’ll be a slave of your mind. Nothing more will be done until you fulfill your inner gamedev wishes!

That’s the story of me. I always loved Top-down action games. Be it an ARPG like Legend of Zelda series, a brawler like Super Bomberman or a car stealing nirvana like PlayStation 1’s GTA 2, I love’em all. Instigating, I never tried to code one… Until now.

Last 2 months were spent researching and coding a deferred renderer in C++ and OpenGL, were  I decided I would be implementing a still not fully decided top-down action game. Here is how it looks:

Very neat, so now I started coding the mechanics, since I already have some basis of the look of the game.

I’ve been considering three options to rule my game:

  1. Code a lot of SAT, shape collision, collision response, raycast…,
  2. Use Box2D for just collision detection, handle all other stuff by myself
  3. Use Box2D and try to accomodate it to a top-down world.
After thinking through the problem, obviously I wouldn’t go with 1, because It’s just too much code that already exists, and code that is certainly much better than what I could write. The upside of this is that I can fully model the movement as I like, but I’m not sure that this would pay for all the work necessary for it.
2 was my certain option by then, but I found out that when you stop using the solver with forces, velocity, torque, and start using setTransform, then weird things start happening. The solver is very tied with the simulation, it’s like the simulation data was used to predict the result of collisions, And when you stop feeding these, you know your game is going to start to look wrong. The 3rd option started smiling at me.
So let’s use it.
Since my game is 3D with 2D gameplay, I’m gonna interpret things as such:
  • The X axis is the left-right keyboard arrows direction
  • The Y axis is the up-down keyboard arrows direction
  • The Z axis is the height of the objects in the world. Imagine that if a entity jumped, it would move in this axis.

Thinking of it, gravity acts as bringing things down to earth, Box2D gravity is usually expressed as something like b2Vec(0, -10) This is not what we want. If we use this value as our gravity, our character would go down to the bottom of the screen. The right value would be b3Vec(0, 0, -10), acting on the Z axis, but since Box2D is a 2D physics engine, we simply discard the Z axis where it exists:

<code style="color: black; word-wrap: normal;"> b2World world(b2Vec2(0, 0));

Try creating some static and dynamic bodies, they don’t get pushed instantly, and this is already nice. However if you move something, you’ll notice that they simply doesn’t stop. This happens because the gravity force was supposed to do this. In the real world, things stop, because of a force called friction the ground, which is resultant of the gravity. So as we solved one problem, we stumbled into another.

How to solve the friction problem for characters:

Analyze how you walk: You’re stopped at point A and want to get at point B. You start walking, thus, increasing your velocity. When you reach a good walking velocity, you keep it steady until you reach your target. When you reach the point B, you reduce your velocity, just enough to make you stop. (if you reduce more, you’re going to start walking backwards, and you’re no Michael Jackson!).
We can reproduce this as following when the player has some key pressed, assume he wants to walk, give him enough force to accelerate and then keep that speed. If no keys are pressed, apply enough force to deaccelerate the player until he stops. Sounds easy, let’s code it:
<code style="color: black; word-wrap: normal;">    b2Vec2 dir;
    dir.Set(0, 0);
        dir.y += 1;
        dir.y -= 1;
        dir.x += 1;
        dir.x -= 1;
    float sqlen = dir.LengthSquared();
    if(sqlen &gt; 0)
        dir /= sqrt(sqlen);

First we get a vector pointing to where does the player wants to go, if nothing is pressed, then this vector has length 0. With this vector, we can multiply it by the desired speed, and get the desired velocity the player wants to walk:

<code style="color: black; word-wrap: normal;">    const float maxSpeed = 30;
    b2Vec2 desiredVelocity = dir * maxSpeed;

You can see that if the direction is 0, the desired velocity is also 0.
Now, we want to calculate how much velocity we need to apply to get to the desired velocity:

<code style="color: black; word-wrap: normal;">    desired -= body-&gt;GetLinearVelocity();

You can check, that if the body current linear velocity is already the same as the desired velocity, this will yield us zero, which means we’re already at the state we wanted to, don’t apply any more forces to the body.

If you try to run this already, you’ll see that it quite works, but the acceleration is too slow to consider a fast paced game, and also, this is not very consistent, depending on the mass of the body (thus the density of its fixtures), it will move slower or faster, which is not always acceptable.

For these two issues, our friend Isaac Newton comes to the rescue. Remember what he has taught all of us in the high school? One of his brilliant stuff was to postulate the F = MA formula, and if you consider it, we’re not considering mass at all yet, nor the rate of the acceleration.
To consider the mass of the object when calculating a force, we just need to multiply it by our acceleration (which is our desired vector). Now to make this acceleration faster, we just multiply it by some constant. Yeah, it’s that easy! look out:

<code style="color: black; word-wrap: normal;">    desired *= acceleration * body-&gt;GetMass();

Pretty easy huh. To finish this up, I like to make the acceleration var be half of it’s normal value when we’re stoping the character. This makes it accelerate faster than deaccelerate, which is nice.

<code style="color: black; word-wrap: normal;">    if(sqlen &gt; 0) {
        acceleration = 30;
    } else {
        acceleration = 15;

How to solve the friction problem for other dynamic assets:

To be honest, this is a problem I haven’t solved yet. I think I’ll revisit it later (if you find a way, tell me!), but for the time being, this is the current ducktape to hold it:
When creating other bodies (non-characters), set the linearDamping to 1.0f. It’s located on the BodyDef.
This still is NOT what I wanted. I’ve heard of the FrictionJoint, but there’s almost no documentation on how to use it, in case I decipher it or found someone in the web that has already done that, I’ll certainly update this.
This is it. News on this game will be posted soon, stay tuned 🙂
Share this: