3
\$\begingroup\$

This is the first time I'm seriously developing a game so apologies if I'm missing the obvious.

In my 2D platformer I adopted Box2D since it seemed like a natural solution to model gravitational pull (player dropping off platforms) and jump arcs and in the process get collision detection for free. It was also a breeze to integrate with libgdx which I'm using as the main game engine.

One thing that I'm struggling with though is, ironically, the most simple thing: expressing player movement along the x axis using Box2D. Where previously it was as simple as translating position as a function of time, now I can only indirectly affect player position by applying a horizontal force to the rigid body representing the player, thus increasing its velocity.

I have multiple problems with this approach:

  1. If I add friction to the platform fixtures, which seems natural, the user has to keep applying a force to the player for it to keep moving. However, my input system is based on the user pointing to a target platform or block upon which the character moves there, then stops.

  2. If I remove the friction from any static objects the player steps on, then it's sufficient to apply force force once for the player to keep moving, but how do I tell it to stop at point x? I couldn't come up with a solution that does not feel convoluted or unnatural. I was thinking for instance to store the target position and then apply a balancing force in the opposite direction once the player reached it. Sounds complicated and prone to failure in corner cases though. I also thought about placing invisible geometry one unit ahead of the target position so that the player would collide with it and stop once reaching the position.

But seriously, both ideas sound complicated and kinda dumb. Am I missing the obvious?

I'm thinking already that maybe I should pursue a mixed approach of physics and direct controls, but I already tried that and Box2D does not like it if I set object positions manually, since this short circuits the computations behind the scenes?

\$\endgroup\$
  • \$\begingroup\$ How do you walk down the hall? Think on that deeply for a while, especially in regards to the physics involved, and then design your UI to be consistent with real world physics. \$\endgroup\$ – Pieter Geerkens Dec 1 '14 at 22:57
  • 1
    \$\begingroup\$ How does your pathfinding work? Does the player automatically jump? Or are you only concerned with the X axis? \$\endgroup\$ – Entity Dec 1 '14 at 23:30
  • \$\begingroup\$ @pietergeerkens Hah, yes I actually thought about that. The way I understand it works is that placing one foot ahead of your body and applying a downward backward force propels your body forward (due to Newton's 3rd law I guess? I'm a bit fuzzy on the physics). That doesn't answer my question though as it means it's a step wise motion (duh). What I need, I guess, is a continuous motion / force up until a certain position is reached. Does that make sense? \$\endgroup\$ – Matthias Dec 2 '14 at 8:38
  • \$\begingroup\$ @TheAdamGaskins I haven't implemented jumping yet, but I imagine it working by reacting to an upward fling gesture. Right now I am only concerned with horizontal movement, although I might look into ladder movement on the y axis at some point in the future \$\endgroup\$ – Matthias Dec 2 '14 at 8:41
5
\$\begingroup\$

I was going to type up an answer, but then I found this article, which seems to be exactly what you need: http://www.iforce2d.net/b2dtut/constant-speed

I'll copy a few of the relevant bits. This assumes C++, but it's certainly applicable to any language.

[...] 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.

[...]

Using SetLinearVelocity

For many applications this may be good enough. Inside the Step() function, we will take whatever action is required each time step:

    //inside Step()
    b2Vec2 vel = body->GetLinearVelocity();
    switch ( moveState )
    {
       case MOVE_LEFT:  vel.x = -5; break;
       case MOVE_STOP:  vel.x =  0; break;
       case MOVE_RIGHT: vel.x =  5; break;
    }
    body->SetLinearVelocity( vel );

[...]

Using forces

Forces are more suited to the gradual acceleration, [...] but 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:

    b2Vec2 vel = body->GetLinearVelocity();
    float desiredVel = 0;
    switch ( moveState )
    {
      case MOVE_LEFT:  desiredVel = -5; break;
      case MOVE_STOP:  desiredVel =  0; break;
      case MOVE_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:

    b2Vec2 vel = body->GetLinearVelocity();
    float desiredVel = 0;
    switch ( moveState )
    {
      case MOVE_LEFT:  desiredVel = -5; break;
      case MOVE_STOP:  desiredVel =  0; break;
      case MOVE_RIGHT: desiredVel =  5; break;
    }
    float velChange = desiredVel - vel.x;
    float impulse = body->GetMass() * velChange; // disregard time factor
    body->ApplyLinearImpulse( b2Vec2(impulse,0), body->GetWorldCenter() ); // because ApplyLinearImpulse knows the exact time factor
\$\endgroup\$
  • \$\begingroup\$ Thanks, this looks really helpful. I haven't had time to implement it yet, but I'll work through it. \$\endgroup\$ – Matthias Dec 15 '14 at 16:06
  • 1
    \$\begingroup\$ This approach has the problem that the user controls might control external forces. Example: A cannon throw the character at a high speed. If the user press any button (including the same direction the cannon threw him), then the line velChange = desiredVel - vel is going to make the character lose all the speed the cannon transferred to him, which is usually undesirable. \$\endgroup\$ – Gustavo Maciel Mar 2 '17 at 2:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.