0
\$\begingroup\$

I want to achieve a platformer jump similar to that of Super Mario Bros. (keep pressed the jump button to jump higher) and I want it to be frame rate independent. At 30 FPS it works as I want. However, I noticed that, if frame rate is lower (e.g. 10 FPS) the jump height is much lower.

The following is the pseudo-code I am using on my character. What am I doing wrong? Note: I'm considering the Y-axis to grow downwards, so a negative vertical speed means the character is moving upwards.

jump_force = 4000;
jump_max_speed = 800;
gravity = 2000;

yspeed = 0; // vertical speed (negative is upwards)
is_jumping = false;

update() { // called every frame
    dy = 0; // delta y-position for this frame

    if (yspeed != 0 && is_grounded()) {
        yspeed = 0;
    }

    if (jump_button_pressed() && is_grounded())
        is_jumping = true;
    else if (is_jumping && !jump_button_pressed())
        is_jumping = false;

    if (is_jumping) {
        yspeed -= jump_force * dt(); // dt() is delta time, i.e. time elapsed since last frame
        // cap vertical speed to limit jump height
        if (yspeed < -jump_max_speed) {
            is_jumping = false;
            yspeed = -jump_max_speed;
        }
    }
    else if (!is_grounded()) {
        // apply gravity only when the character is not jumping
        yspeed += gravity * dt();
    }

    dy = yspeed * dt();

    // if character would fall inside terrain, place it just above it
    if (dy > 0 && check_for_ground(x, y + dy)) {
        y = ground.y;
        yspeed = 0;
    }
    else
        y += dy;
}
\$\endgroup\$
2
  • \$\begingroup\$ Have you considered that your dt() function might not work correctly? \$\endgroup\$
    – Philipp
    Commented Dec 6, 2018 at 17:38
  • \$\begingroup\$ Hi there. As suggested by Philipp, something might be wrong with your dt() functions. You may want to do a quick search for 'frame independent' movement on your favorite search engine. For a good explanation on the use of delta times check: gafferongames.com/post/fix_your_timestep (what Bram is also suggestion below...) \$\endgroup\$
    – Bert
    Commented Dec 6, 2018 at 17:42

2 Answers 2

2
\$\begingroup\$

You need to decouple the render frequency from the simulation frequency: You measure the delayed time, do an appropriate amount of simulation steps, then render the frame.

The easiest way to do this, is to set the simulation frequency really high. For a platformer this should be easily achievable. I suggest 240Hz or 480Hz.

Then count the number of simulation frames that have passed during your game loop, and perform those.

Let's say your sim rate is 240Hz.

If your FPS is 120, then you do two simulation steps. On a 20 FPS machine, you need to do 12 of them.

If you cannot afford a high simulation rate, people often interpolate or extrapolate based on "left-over time."

This is the seminal article on this: Fix your timestep.

But personally, I use the high sim rate approach.

\$\endgroup\$
0
\$\begingroup\$

I noticed something similar with my particle simulation program. I was doing the same as you, only accounting for delta time * speed_per_second and changing the FPS changed the position of the particles. At high FPS they would overshoot. At low FPS they moved short distances. This is because the integration math is incorrect. We need acceleration too.

In order to have a more correct integration step, we must apply acceleration per delta as well as the velocity per delta.

double elapsed = dt();
speed += vector2(acceleration.x * elapsed, acceleration.y * elapsed);
pos   += vector2(speed.x * friction.x * elapsed , speed.y * friction.y * elapsed );

After I made these changes, the particles moved correctly. try that and see if it fixes your jumping problem

\$\endgroup\$
2
  • \$\begingroup\$ Isn’t it what I am already doing in the code snippet I posted in my question? I’m referring to the following rows: yspeed -= jump_force * dt();, yspeed += gravity * dt(); and dy = yspeed * dt();. \$\endgroup\$ Commented Dec 7, 2018 at 13:30
  • \$\begingroup\$ Didn't see the force code before. Yeah but you may be taking the integral of the accel twice. Try total_force = jump_force + gravity; yspeed += total_force * dt(); dy += yspeed * dt(); Taking the total of all forces before multiplying by delta. If that's not working then it could be the dt() function as others have said. \$\endgroup\$
    – Dan
    Commented Dec 7, 2018 at 17:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .