Jump not framerate independent

Hey,

I’m using a custom 2D controller, it has a Move() method that uses transform.Translate(velocity) to move the character.

In my player controller script I apply gravity by doing this every frame right before I call Move() :

velocity.y += gravity * Time.deltaTime; 

My Jump() method is called before the gravity, and sets the velocity.y to the jumpForce once.

At 60+ fps i’m able to jump over a specific amount of tiles, at 30 fps I miss the last tile by a small amount of height, the weird thing is that if I slow down the game with time scale, I reach the same height.

What is going on?

EDIT: Bunny83’s solution worked perfectly, I’m going to keep Eno-Khaon’s answer because it clearly explains the issue, thanks guys much appreciated

Physics calculated on a per-frame basis will ALWAYS* be framerate-dependent. This is why Unity uses FixedUpdate() to alleviate that somewhat by providing a consistent rate of performance to offer reproducible results.

*Well, okay, not always, but the calculations required to make it framerate-independent are non-trivial to the point where it’s simpler to wind up close enough instead.

If you calculate where something should be in reality (based on predictive physics calculations), you’re assuming that the rate of acceleration will change an infinite number of times per second. In your case (based on iterative physics calculations), however, the acceleration changes at a rate based on the current framerate.

To give an example of this, here’s a breakdown of behavior under the following conditions:

  1. Gravity accelerates objects at a rate of 1 unit per second.

  2. The object “jumps” with a starting upward force of 1 unit per second velocity

    Column A: Current Y-axis speed
    Column B: Pending height change, based on current frame velocity
    Column C: New height after current frame

    Example 1: 5 frames per second
    deltaTime per frame: 0.2

    |A |B |C
    ±------±------±------
    |1.0 |0.2 |0.2
    |0.8 |0.16 |0.36
    |0.6 |0.12 |0.48
    |0.4 |0.08 |0.56
    |0.2 |0.04 |0.6

    Final Height: 0.6 at 1 second, 5 frames


    Example 2: 10 frames per second
    deltaTime per frame: 0.1

    |A |B |C
    ±------±------±------
    |1.0 |0.1 |0.1
    |0.9 |0.09 |0.19
    |0.8 |0.08 |0.27
    |0.7 |0.07 |0.34
    |0.6 |0.06 |0.4
    |0.5 |0.05 |0.45
    |0.4 |0.04 |0.49
    |0.3 |0.03 |0.52
    |0.2 |0.02 |0.54
    |0.1 |0.01 |0.55

    Final Height: 0.55 at 1 second, 10 frames


    A “correct” Final Height would have been 0.5

As you can see, differences in framerate WILL affect the outcome because the framerate directly influences exactly how long the object is moving at any given speed. While the changes in velocity based on acceleration take effect in a true-to-life manner, the changes in position are based on the current velocity for a given frame, scaled by the time that frame took to process.

While Eno-Khaon is basically right, there is an easy fix for constant accelerations. The main problem is that deltaTime can only be used to make linear things framerate independent. Since a constant acceleration applies a linear change to the velocity, the velocity correctly changes over time. However the position change is not linear but quadratic and it can’t be scaled linearly.

The trick is to apply the velocity not once but twice each frame. Once before you do the velocity change and once after the velocity change. You just apply half the velocity each time. This will not make it perfect as we deal with time discrete values while in reality physics happens continuously. Though it will make the errors even out and not drift over time.

pos += 0.5f * velocity * Time.deltaTime;
if (jump)
    velocity.y = jumpForce;
velocity += gravity * Time.deltaTime;
pos += 0.5f * velocity * Time.deltaTime;

Note that this does only work for constant accelerations / linear velocity changes. So if something like drag is used it won’t work since drag is non linear

Try using Time.fixedDeltaTime