Inconsistent jump height

I am doing some physics for a platformer game in MonoGame. The jump height changes based on FPS even though I'm using delta time. When the FPS decreases and gravity is added before movement, the jump height is shorter than usual. How do I keep a consistent jump height at all frame rates?

Here's the pseudo-code executed each frame:

velocity.Y += gravity * dt;
Move(velocity * dt);

• You move after you change the velocity, change the order of them Jul 26 '17 at 19:40
• Then the jump height will be higher than usual when the framerate decreases. Jul 26 '17 at 19:44
• gafferongames.com/post/fix_your_timestep Jul 26 '17 at 19:52
• @Bálint Why does OP have to change the order? Jul 26 '17 at 20:01
• It doesn't matter how small dt is, the jump height will always be variable to some degree when dt varies with this technique and it will. I'm pretty sure I'm doing something fundamentally wrong because I've never seen a professional platformer where the character jumped higher/lower on different frame rates. Jul 26 '17 at 20:09

The most commonly used method in commercial engines is the semi-implicit Euler method that I'm already using as you can see in my pseudo-code (https://gafferongames.com/post/integration_basics/). But this doesn't give 100% accurate jump height. Everyone tells me to implement RK4 and/or make dt as small as possible. Neither of these things is necessary. While it would be great if the game could run at a consistent 60 FPS on all machines it's just not realistic.

I figured out how to achieve a consistent jump height with no downsides. First of all, you add half of the gravity before and after you move with velocity. Secondly, you need to check if adding the gravity will make the object move in the opposite y direction if so you should first move to the position where velocity.Y = 0 and then add the gravity to the velocity, so the object will move that distance in the next frame.

So how do you find the position where velocity.Y = 0? By using physical formulas.

The time from now until the object's velocity = 0

t = velocity.Y / gravity

The y-distance from here until the position where velocity.Y = 0

y = -0.5 * gravity * t^2 + velocity.Y * t

So in pseudo-code:

if (velocity.Y < 0 && velocity.Y + (gravity * dt) >= 0)
{
float t = velocity.Y / gravity;
float y = (-0.5f * gravity * (t * t)) + (velocity.Y * t);
velocity.Y = -y;
Move(new Vector2(velocity.X * dt, velocity.Y));
velocity.Y += gravity * dt;
}
else
{
velocity.Y += gravity * dt * 0.5f;
Move(velocity * dt);
velocity.Y += gravity * dt * 0.5f;
}


This might change depending on which direction is up and down for you, but this is the general gist of it and this gives me the same jump height for all frame rates.

BTW I'm using continuous collision detection, so there is no fear of tunneling or other weird side effects from low frame rates.