FPS indepedent jump

Hello everyone,Preformatted text

Newbie here. I have a simple 2D sidescroller code and jumping behaves strangely. When I run the game at fixed 30 FPS, the character jumps 2x higher compared to running the game at 60 FPS. I understand that it’s because of the time multiplication, but can you give me a hint to fix this, so the character jumps the same amount regardless of the FPS?

Here is the releavent code snippet:

float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds * Constants.TIME_OFFSET;

if (kstate.IsKeyDown(Keys.Space) && canJump)
{
                canJump = false;
                direction.Y -= Constants.JUMP_FORCE * elapsedTime;
}

Or am I on a very wrong path with this approach in my code?

Thank you

What is Constants.TIME_OFFSET, and why are you multiplying by it? That’s the only thing that looks off to me.

If you’re running at 30 FPS, gameTime.ElapsedGameTime.TotalSeconds should be 1/30. If running at 60 FPS, it should be 1/60. So half the amount, but twice as often, with the net result being the same.

So the only unknown here is TIME_OFFSET. If that’s a fixed amount in both runs then it shouldn’t matter (even though it’s weird to me), but if it changes between runs, then that might be related to the problem.

If things are still weird, set a breakpoint on elapsedTime and make sure it’s the values you expect :slight_smile:

1 Like

Thanks a lot for your answer! The Constants.TIME_OFFSET (currently set to 1) is just a leftover when I was still using otalMilliSeconds instead of seconds, I needed it because I had to tweak my constants like friction = 0.0007, jumpForce = 0.005 and I was losing precision during calculations, so I just wanted bigger number to work with :slight_smile:
Is that a bad practise in general?

‘direction’ is supposed to be Velocity, right?
In that case you have to set
direction.Y -= Constants.JUMP_FORCE;

You have to use the elapsedTime later when you calculate the position,
position += direction * elapsedTime;

2 Likes

Yes, exactly, I just figured it out as well, there is no point in multiplying by the time for an even that is only 1 frame, thank you :slight_smile:

1 Like

No, I don’t think so… my only complaint is that maybe it’s not the best variable name, but its your code :stuck_out_tongue:

It just threw me because it wasn’t what I expected. Everything else, math wise, looked good such that I didn’t even read your variable name, direction. I just assumed it was position lol.

Good spot, nkast!

1 Like

Um, boy. I got bad news for you. Like, really bad news. Your jumps STILL will not have the same height at different fps.
It will look something like this:

There is a tiny difference, because the jump work in the following way:

  • There is vertical speed, which is being modified by the gravity.
  • There is position, which is being modified by the vertical speed.
    When running the game at 30 fps, that change in speed is linear for 1/30th of a second. And when running at 60fps, it’s linear for 1/60th. This results in ever so slightly difference forces being applied.
    The math gets kinda fucked up from this point, and this is where my understanding stops. There ARE ways to compensate for this, and if you want, I could dig out a formula for you. But I’d just recommend making your game fixed to 60 fps.
1 Like

Thank you for your insight. I will just follow your advice and target 60 FPS with my game :slight_smile:

I threw together an example, I assume this is what you’re referring to:

        //start of each frame, save previous velocity
        Vector3 previousVelocity = velocity;

        // by the way, I would recommend using this for more consistent frame pacing
        float deltaTime = (float)gameTime.ElapsedGameTime.Ticks / TimeSpan.TicksPerSecond;

        // unit consistent in my game world for scale
        // I multiply this onto my velocities when I initialize
        float unitOfMeasure = 64.0f;

        float gravityV = 9f * unitOfMeasure; // gravity velocity

        if (IsGrounded) // feet on ground
        {
            if (InputMap.JustPressed(player, Actions.Jump)) // jump input
            {
                velocity.Y = jump.verticalRate; // jump.verticalRate = 11f * unitOfMeasure
            }
        }

        // add force of gravity to current vertical velocity
        velocity.Y += gravityV * deltaTime;
        if (velocity.Y < -MaxFallRate)  // check if we're moving faster than MaxFallRate
        {
            if (previousVelocity.Y >= -MaxFallRate)
            {
                velocity.Y = -MaxFallRate;
            }
            else
            {
                velocity.Y = previousVelocity.Y;
            }
        }

        // This is called Verlet Integration, as opposed to Euler
        // It is mathematically more accurate than adding velocity normally to position
        position += (previousVelocity + velocity) * 0.5f * deltaTime;
1 Like

That’s very useful, thank you very much!

1 Like

Just wanted to chime in with my opinions, for what it’s worth. I ran into a similar issue many years ago, so it hits close to home.

My personal suggestion, since you were so inclined, is to follow your original idea to allow for variable FPS. I think it’s more professional, as it allows for a better experience for both weaker and stronger hardware. (I’ve had great frustration with games that don’t do this, and run literally more slowly if the PC is at all under spec. I’m looking at you, Ori.)

I also wanted to point out @Mateo_Loooong 's very elaborate response, particularly the very last line. This was something I overlooked when I did something similar about fifteen years ago, and I see that it was missing from the other code in this thread as well. In short, adding velocity * time will move the character according to the speed at the beginning of the update, but does not account for the acceleration over the course of that time. The line of code he gave (or some equivalent thereof) is a bit of Calculus that accounts for this changing rate. This will address @Martenfur 's concern, so you can continue to support variable FPS.

1 Like

And now if you want changing gravity, the math gets even more complicated. Fps spikes will also affect how far your character jumps. If ye want to go down that rabbithole, all power to you, but I’ll steer clear from this.

1 Like

Fortunately, most games don’t involve changing gravity, let alone mid-update. However, if that was a thing for whatever reason, Calculus comes to the rescue there as well. But that is well beyond the scope of this thread.

Thank you guys, I’ll just gonna let the increasing gravity go and save myself the trouble :slight_smile:

Was there going to be increasing gravity? I missed that part of the scenario.

Sorry, I phrased wrong. Not increasing gravity but increasing acceleration when the character is falling.