3
\$\begingroup\$

I am trying to implement simple Newtonian physics for my game. However, my character only jumps half of the height I would expect him to.


Physics says that the take-off velocity (impulse) v for an object to reach a height h is

v=sqrt(2gh)

Thus, to achieve a height of 2.0 meters, an impulse of 6.264 (per unit of mass) would be required. I used this as a benchmark to test the correctness of my physics code.


However, aforementioned impulse only raises my object 0.97 meters into the air before it falls down again.

I've tried Euler integration, Euler (midpoint) integration and Runge-Kutta 4 integration, all resulting in a jump height 0.97. My delta time is fixed to 1 / 120.

// Calculate the movement of the game object in this physics frame
Vector3 translation;
{
  Vector3 acceleration = this.queuedForces / this.Mass;

  // Convert impulses 
  Vector3 impulses = this.queuedImpulses / this.Mass;
  acceleration += impulses / delta; // turn impulse into force over 1 delta tick

  // Apply half of the force scaled by time
  this.Velocity += acceleration * delta / 2.0f;

  // Apply impulses directly to velocity
  //this.Velocity += this.queuedImpulses / this.Mass;

  // Integrate into position
  translation = this.Velocity * delta;

  // Apply second of the force scaled by time
  this.Velocity += acceleration * delta / 2.0f;

  // Finally, queued movements (root motion, etc.) go directly into translation
  translation += this.queuedMovement;
}

Gravity is a force, applied each tick as this.forces += Vector3(0, -9.81, 0) * this.Mass). The jump is an impulse added once as this.impulses += Vector3(0, 6.264, 0).

Where does my mistake lie?

Adding the impulse directly to this.Velocity instead of converting it to a force quadruples the jump height, which equally puzzles me.

\$\endgroup\$
  • \$\begingroup\$ Why split the velocity calculation? Even if you have a good reason to do so, why not simplify it while you identify your problem? \$\endgroup\$ – MichaelHouse Sep 12 '17 at 20:57
  • \$\begingroup\$ Because I believe this would be wrong and introduce a new problem to the simulation rather than make it clearer. The integration formula shown is the "Midpoint Method" (see openarena.ws/board/index.php?topic=5100.0 or lolengine.net/blog/2011/12/14/understanding-motion-in-games) and it matches up with Runge Kutta 4 very closely. \$\endgroup\$ – Cygon Sep 12 '17 at 21:12
  • \$\begingroup\$ Seems like a good troubleshooting starting point to me. Half the velocity being applied and half the height being achieved. \$\endgroup\$ – MichaelHouse Sep 12 '17 at 21:15
  • \$\begingroup\$ I am applying the other half of the acceleration a little further down. If I change it to apply the full acceleration before integrating velocity, my character jumps a tiny bit higher, if I do it after integrating velocity, a tiny bit lower. Both introduce an error of + and - (a * dt / 2) into the velocity. \$\endgroup\$ – Cygon Sep 12 '17 at 21:30
1
\$\begingroup\$

F=MA. Or, Force = Mass * Acceleration.

It appears you're dividing out the mass from your queued forces and impulses to deal with just acceleration, but you're not putting the mass back in when you calculate the force to be applied to the object. And I suspect the mass of your character is equal to 2?

this.Velocity += acceleration * delta / 2.0f;

should be

this.Velocity += (acceleration * mass) * delta / 2.0f;

You can also just not divide out the mass and just sum up forces instead of accelerations.


I wrote a quick test method to see this in action:

    static void Test()
    {
        float delta = 1f/120f;
        float height = .001f;
        float impulse = 6.264f;
        float gravity = -9.81f;
        float mass = 2f;
        float velocity = 0;

        float peakHeight = 0;
        while (height > 0)
        {
            float acceleration = gravity/mass;
            float impulses = impulse/mass;
            impulse = 0;
            acceleration += impulses/delta;
            velocity += (acceleration*mass)*delta/2f;

            height += velocity*delta;

            if (height > peakHeight)
                peakHeight = height;

            velocity += (acceleration*mass)*delta/2f;
        }

        Console.WriteLine("PeakHeight: " + peakHeight);
    }

Which outputs:

PeakHeight: 1.974736

\$\endgroup\$
  • \$\begingroup\$ That seems to be it! My test mass was 80.0, but I was using a gravity multiplier, too. In your code, If I use smaller deltas, the height converges on 2.0 (as expected) and if I change it to use 4th order Runge Kutta integration (also multiplying acceleration by mass) it agrees with the result -> ideone.com/xUzOTD \$\endgroup\$ – Cygon Sep 13 '17 at 7:00
  • \$\begingroup\$ Can you elaborate on what you mean by "You can also just not divide out the mass and just sum up forces instead of accelerations."? If I add gravity as a force via force += gravity * mass and not multiply by mass during integration, would that be correct? \$\endgroup\$ – Cygon Sep 13 '17 at 7:07
  • \$\begingroup\$ You'd leave mass out of that code entirely. Since it's been accounted for elsewhere when you calculated the forces. I'd assume you're already adding gravity as a force somewhere else, which is why you are dividing by mass to get back to an acceleration. \$\endgroup\$ – MichaelHouse Sep 13 '17 at 14:14

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.