Great platformers feel good because of small, invisible tricks. Let's build a controller that includes them.
Acceleration, not instant speed
Instant movement feels stiff. Ease velocity toward the target speed instead:
const target = input.x * MAX_SPEED;
const rate = onGround ? GROUND_ACCEL : AIR_ACCEL;
velocity.x = moveTowards(velocity.x, target, rate * dt);
Variable jump height
Let players control jump height by cutting upward velocity when they release the button early:
if (jumpReleased && velocity.y > 0) {
velocity.y *= 0.5;
}
Coyote time
Allow a jump for a few frames after walking off a ledge. Players believe they pressed jump in time, so let them.
coyoteTimer = onGround ? COYOTE_TIME : coyoteTimer - dt;
Jump buffering
If the player presses jump slightly before landing, remember it and jump as soon as they touch the ground.
if (jumpPressed) bufferTimer = BUFFER_TIME;
else bufferTimer -= dt;
if (bufferTimer > 0 && coyoteTimer > 0) {
velocity.y = JUMP_SPEED;
bufferTimer = 0;
coyoteTimer = 0;
}
Heavier falling
Increase gravity when falling for a snappy arc:
const g = velocity.y < 0 ? GRAVITY * 1.8 : GRAVITY;
velocity.y -= g * dt;
Start with around 0.1 seconds for both coyote time and jump buffer, then tune by feel.
