velocity = velocity + gravity*dt position = position + velocity*dtThese equations work pretty good if you're in a vacuum. In a vacuum, gravity can accelerate a falling object forever and we definitely don't want mushrooms falling at the speed of light! One solution is to clamp the velocity of moving objects to a reasonable threshold.
local v = math.sqrt(velocity.x^2 + velocity.y^2) if v > maxVelocity then local vs = maxVelocity/v velocity.x = velocity.x*vs velocity.y = velocity.y*vs end
velocity = velocity / (1 + damping*dt)Where "damping" coefficient is a value between 0 to infinity. The greater this coefficient the faster the objects will slow down. To be more specific, a value between 0 to 1 is considered "under-damping", 1 is considered "critical damping" and any value greater than 1 is "over-damping".
We have to start with some assumptions.
First, the player will be controlling his jumps using a single button.
The longer he holds the button, the higher we want him to jump.
This creates the illusion that pressing the button harder makes your character jump higher.
A naive approach might be to continually apply an upward force while the player is holding the jump button.
Unfortunately, this produces "floaty" jumps and won't feel right unless the character is sporting a jetpack.
Left: jetpack with thrust applied for different periods of time
Right: jumps with variable initial velocity
if pressedJumpKey then player.yv = initJumpVelocity endThe physics of jumping is similar to that of launching a projectile. We set the initial velocity of the character and let gravity do its thing (ignoring the effects of air resistance). This brings us to the second assumption: regardless of how long the player holds the jump button he always takes off with the same vertical velocity. Knowing the character's maximum jump height we can deduce a number of other constants:
-- what is the gravity that would allow jumping to a given height? g = (2*jumpHeight)/(timeToApex^2) -- what is the initial jump velocity? initJumpVelocity = math.sqrt(2*g*jumpHeight) -- how long does it take to reach the maximum height of a jump? -- note: if "initJumpVelocity" is not a multiple of "g" the maximum height is reached between frames timeToApex = initJumpVelocity/gThanks to the three equations above, you no longer have to set the gravity in your game to some magic value. Nor do you have to tweak and test the character's jump velocity by hand!
In the event that the player releases the jump button quickly, we have to fudge the laws of physics a little bit. The idea is to adjust the player's velocity mid-air so that he doesn't get up to his maximum jump height. Let's call this "jump termination".
First technique: velocity reset
The simplest solution is to set the player's vertical velocity to zero as soon as he releases the jump button:
if releasedJumpKey then -- is the player ascending? if player.yv > 0 then player.yv = 0 end endThis approach is fine and produces sharp jumps like in Super Meat Boy. Unfortunately, it looks a little awkward for small jumps as the player takes off at a high velocity and immediately drops.
Second technique: velocity clamp
An alternative approach is to introduce another constant: "minimum jump height". Every time the character jumps, he is guaranteed to reach at least this minimum allowed height. As a consequence, the player can terminate his jumps only for a given amount of time ("termTime") before reaching the apex of his jump. When the player releases the jump button, instead of resetting the vertical velocity to zero, we clamp it to a predefined termination velocity ("termVelocity").
if releasedJumpKey then -- is the player ascending fast enough to allow jump termination? if player.yv > termVelocity then player.yv = termVelocity end endThis approach make jumps feel like they have momentum. When the player releases the button, he continues to ascend for a little bit before gravity kicks in. The result is pretty good as long as the minimum allowed jump height is not too low. This is how jump termination would look when the minimum jump height is 1/5:
-- what is the velocity required to terminate a jump? -- note: only works when "g" is negative termVelocity = math.sqrt(initJumpVelocity^2 + 2*g*(jumpHeight - minJumpHeight)) -- how much time is available until a jump can no longer be terminated? -- note: "minJumpHeight" must be greater than 0 termTime = timeToApex - (2*(jumpHeight - minJumpHeight)/(initJumpVelocity + termVelocity))
Third technique: gravity scale
The third technique I would like to discuss comes from the Box2D physics library by Erin Catto. Basically, we introduce another variable called "gravity scale". Each object in the game could have a "gravity scale" property (it's default value being 1). Setting the "gravity scale" of an object to -1 would invert its gravity.
obj.velocity = obj.velocity + obj.gravityScale*gravity*dt obj.position = obj.position + obj.velocity*dtSo "gravity scale" is pretty useful if you want to have your character walk on the ceiling. In addition, when we set the "gravity scale" to 2, our character becomes twice as heavier! Let's see how this could be used to affect jumping.
if pressedJumpKey then jumpTime = 0 player.yv = initJumpVelocity endWe modify the "gravity scale" once, when the player releases the jump button while in the air.
if releasedJumpKey then if player.yv > 0 then player.gravityScale = timeToApex/jumpTime end endAlso, we need to track the elapsed jump time during each frame. The "gravity scale" is reset back to 1 after reaching the jump apex.
if jumpTime then jumpTime = jumpTime + dt -- reached jump apex? if jumpTime >= timeToApex then jumpTime = nil player.gravityScale = 1 end endThe results don't look too bad for medium and high jumps. Note that smaller jump are steep and short in duration.
Platformer physics 101 and the 3 fundamental equations of platformers by Zachary Burke
Controlling jump height by Micha
Box2D by Erin Catto