🎮FreeGameSprites
📖 Tutorials·7 min de lecture

Why Your 2D Platformer Feels Floaty (And the 6 Numbers That Fix It)

Floaty jumps kill platformer feel. Six concrete fixes — variable jump height, coyote time, jump buffering, asymmetric gravity, fast-fall, and one tuning trick — with exact frame counts and code-ready values used by Celeste, Hollow Knight, and Super Meat Boy.

Aventurière chatte (16-Bit) — sprite pixel art 256×256

You finished the prototype. The character moves, the jump button works, the level loads. You hand it to a friend, watch them play for thirty seconds, and they say the words every platformer dev dreads: "it feels kind of floaty."

It feels floaty because the math is wrong. Specifically, six numbers in your physics step are at default values that make Unity's, Godot's, and GameMaker's built-in physics produce a moon-jump arc instead of a Mario one. Fix those six numbers and the same character with the same tiles will feel three times more responsive.

This is what every great-feeling platformer — Celeste, Hollow Knight, Super Meat Boy, Hollow Knight, Animal Well — actually does. None of them use realistic physics. All of them use specific tweaks that lie to the player in ways the player's hands love and brain doesn't notice.

Adventurer sprite mid-action — tuning her jump physics is the difference between "floaty" and "tight"
Adventurer sprite mid-action — tuning her jump physics is the difference between "floaty" and "tight"

Number 1: Gravity Scale (the one that matters most)

Default gravity in most engines is calibrated for falling crates, not jumping characters. Unity's default Rigidbody2D gravity is 9.81 m/s², the actual gravity of Earth. This is wrong for a platformer. Real humans, on real Earth, with real gravity, take 1.4 seconds to fall 10 meters. That's an eternity in a platformer where the player makes 10 jumping decisions per second.

The fix: multiply your gravity scale by 2× to 4× for the falling phase. A common starting point that feels right:

gravity_scale = 3.0
jump_velocity = 12.0

These two numbers together give you a roughly 0.4 second total jump arc at maximum height — the sweet spot where the player can plan a jump but doesn't feel they're floating to the apex.

Reference points: Hollow Knight's jump apex sits around 0.32s. Super Meat Boy is at 0.28s. Celeste is at 0.45s but compensates with extreme air control. If your apex is over 0.6s, your jump is floaty by definition.

Number 2: Asymmetric Gravity (the secret sauce)

Real gravity is the same going up as coming down. In a great platformer it isn't. The player ascends slowly, hangs at the apex for a few frames so they can see and aim, then falls noticeably faster than they rose.

Implementation:

if velocity.y < 0:           # falling
    gravity_scale = 4.5      # heavy
elif velocity.y > 0 and not jump_held:   # rising, button released
    gravity_scale = 4.5      # cut the jump
else:                        # rising, button held
    gravity_scale = 2.5      # light

The fall multiplier of roughly 1.5× to 2× the rise multiplier is what every modern platformer is doing under the hood. Mario 3 was doing it in 1988. The player will not consciously notice. They will say your game "feels good."

Number 3: Variable Jump Height

Tap the button: small hop. Hold the button: full jump. Without this, every jump goes the maximum height and the player can't tune their arc to short obstacles.

The clean implementation, regardless of engine:

on_jump_pressed():
    velocity.y = -jump_velocity     # full impulse

on_jump_released():
    if velocity.y < 0:              # still rising
        velocity.y *= 0.4           # cut the rise short

The 0.4 multiplier is the magic number. 0.5 feels too generous (small jumps still go too high), 0.2 feels too punishing (taps barely lift the character). 0.4 is what feels like control.

Pair this with asymmetric gravity above — when the player releases the button, you get both a velocity cut AND a heavier fall. The combination is why Celeste jumps feel exactly as tall as the player wanted them to.

Number 4: Coyote Time

The player runs off a ledge. They press jump 3 frames after leaving the ground. They expected to jump. Your code says they're already falling so the input is ignored. They die. They blame your game.

The fix is one of the most beloved mechanics in platformers, named after the Coyote-Roadrunner gag: give the player a small grace window after leaving a platform during which a jump still works.

const COYOTE_FRAMES = 6   # 0.1 seconds at 60fps

if on_ground:
    coyote_timer = COYOTE_FRAMES
else:
    coyote_timer -= 1

if jump_pressed and coyote_timer > 0:
    do_jump()
    coyote_timer = 0

6 frames (0.1s) at 60fps is the industry-standard window. Celeste uses 6. Hollow Knight uses 5. Super Meat Boy uses 7. Below 4 frames, players still feel the unfairness. Above 10 frames, the character starts looking like they're hovering.

Stone tileset edge — coyote time decides whether the player jumps cleanly or dies on this ledge
Stone tileset edge — coyote time decides whether the player jumps cleanly or dies on this ledge

Number 5: Jump Buffering

The mirror image of coyote time. The player presses jump while still 2 frames in the air, expecting to land and bounce. Without buffering, the input gets eaten because they weren't on the ground when they pressed.

const BUFFER_FRAMES = 6

on_jump_pressed():
    buffer_timer = BUFFER_FRAMES

on_landed():
    if buffer_timer > 0:
        do_jump()

Same magic number as coyote time: 6 frames. Together, coyote time and jump buffering create a ~0.2 second window of forgiveness around every landing/leaving event. Players cannot consciously detect this window. They just say your controls feel "tight."

Number 6: Air Control + Fast Fall

The last touches.

Air control: the player should be able to nudge their horizontal trajectory mid-jump. But less than on the ground — somewhere around 70% to 85% of ground acceleration is the sweet spot. Full air control feels like you're flying. Zero air control feels like every jump is a commitment letter.

Fast fall: hold the down button mid-air to drop faster. Multiply gravity by an extra 1.5× to 2× while the down button is held. This is optional — many platformers skip it — but it's what lets Celeste players string together precision jumps so quickly.

if input.down_held and not on_ground:
    gravity_scale *= 1.7

Putting It All Together: The Tuning Order

Don't tune all six at once. The order matters.

  1. Gravity + jump velocity first. Get the apex around 0.35-0.45s. Everything else depends on this.
  2. Asymmetric gravity next. Add the rise/fall split. Notice how much "weight" appears.
  3. Variable jump height. Test taps vs holds — you should feel real control.
  4. Coyote time + jump buffering. Now you'll never blame yourself for an unfair death.
  5. Air control. Tune until you can adjust mid-jump but can't fly.
  6. Fast fall (optional). Add only if your level design calls for vertical precision.

After step 4, hand the prototype to the same friend. They will not say "floaty." They might not even know why it suddenly feels good. The math made the difference.

Different tile palettes, same physics — the floor doesn't matter, the gravity does
Different tile palettes, same physics — the floor doesn't matter, the gravity does

What Each Famous Platformer Tweaks

If you want to verify these numbers feel real, the modder community has datamined most of them:

  • Celeste: 6-frame coyote, 6-frame buffer, 1.4× rise/fall ratio, ~0.45s apex, fast-fall enabled. Signature: extreme air control (95%) + dash that resets all timers.
  • Hollow Knight: 5-frame coyote, no buffer, 1.6× rise/fall, ~0.32s apex. Signature: tight, deliberate, almost no forgiveness because the level design assumes you're good.
  • Super Meat Boy: 7-frame coyote, 8-frame buffer, 2× rise/fall, ~0.28s apex, very high horizontal momentum. Signature: you slide.
  • Hollow Knight: Silksong (early access): 6-frame coyote, 5-frame buffer, similar to original Knight but with a slightly heavier fall.
  • Animal Well: 8-frame coyote, 8-frame buffer, 1.3× rise/fall, ~0.5s apex. Signature: deliberately floatier than peers because the puzzles need hang time.

Notice how every game lands within the same narrow band: 5-8 frame coyote, 1.3×-2× rise/fall, 0.28-0.5s apex. There's no innovation here, just careful calibration of well-known knobs.

Common Anti-Patterns to Avoid

Bigger jump velocity instead of higher gravity. This makes the character launch fast but still hang at the apex. The arc is wrong. Tune gravity first, jump force second.

Adding "extra falling speed" with constant downward force. This works but produces weird interactions with one-way platforms and trampolines. Use gravity scale instead — it composes cleanly with everything else physics-related.

Cap the falling speed too low. Many tutorials suggest a maximum fall speed (terminal velocity) of around 20 units. This is too low for any drop over 4 tiles. Set it to 40-50 units so long falls actually feel like falls, not parachuting.

Unity's Rigidbody2D mass affecting jump force. If your jump uses AddForce, mass matters. If you set velocity directly, it doesn't. Most platformers should set velocity directly to keep tuning predictable.

The Test That Catches It All

Drop your character onto a small ledge from 3 tiles up. They should:

  1. Land within ~0.18 seconds (snappy fall)
  2. Bounce immediately if jump was buffered (no delay frame)
  3. Be able to step forward and jump off with no input drop

If any of those three things fail, you have one of the six numbers wrong. Tune that number. Test again.

That's the whole technique. Six numbers. Most beginner platformers ship with three of them at default values. Fixing the other three is what separates "almost feels right" from "feels great."

Now go open your engine and tune.

Free Assets You Can Use Right Now

Every sprite below is CC0 — free for any project, no attribution required. Drop them into your engine to test the ideas in this article.