You do not need a full physics engine to make a character move well. In most 2D action games three quantities are enough, position, velocity and acceleration, updated with a few lines of code. The hard part is not the maths but the tuning: a «right» jump is a design choice before it is a physics one.
Position, velocity, acceleration
The relationship between these quantities is simple:
- velocity says how much the position changes every second;
- acceleration says how much the velocity changes every second.
Gravity is a constant downward acceleration. On each update, with a time step dt, you apply two operations:
velocity.y += gravity * dt
position.y += velocity.y * dt
The order matters. Updating velocity first and then position with the new velocity is called semi-implicit (or symplectic) Euler, and it is more stable than the «naive» version that uses the old velocity. For games it is the standard method: simple, cheap and accurate enough if the step is fixed and small.
In screen coordinates the y axis usually grows downwards, so gravity is positive and a jump has a negative initial velocity. This is a frequent source of confusion when moving from one engine to another.
Designing the jump from the result
Picking random values for gravity and jump strength leads to long trial-and-error sessions. It is more effective to start from two parameters a game designer can understand:
- the maximum height of the jump, in pixels or tiles;
- the time to reach the apex, in seconds.
From the laws of uniformly accelerated motion you derive the physical quantities directly:
gravity = 2 * height / (time_to_apex * time_to_apex)
jump_velocity = -2 * height / time_to_apex
For example, a 96-pixel jump that reaches its apex in 0.4 seconds requires a gravity of 1,200 pixels per second squared and an initial velocity of −480 pixels per second. If you then want a higher jump, you change only the height and recompute the rest, keeping the same sense of duration.
Making the jump feel good
A physically realistic jump, with a perfectly symmetrical parabola, often feels «floaty». Some widely used tricks in platformers:
Stronger gravity on the way down
Applying stronger gravity when the character is falling, for example 1.5 or 2 times the rising gravity, makes the fall more decisive and the control more responsive.
Variable height
Players expect a short press to produce a low jump and a long press a high one. The simplest way: if the button is released while the character is still rising, reduce the vertical velocity, for example by halving it. Alternatively, increase gravity while the button is not held.
Maximum fall speed
Without a cap, a long fall drives the character to enormous speeds, which make precise landings hard and can cause it to pass through thin platforms. A cap on vertical speed, often called terminal velocity, solves both problems.
Horizontal movement: acceleration and friction
Sideways movement also benefits from acceleration rather than an instant snap:
if right pressed:
velocity.x += acceleration * dt
else if left pressed:
velocity.x -= acceleration * dt
else:
velocity.x = move_toward_zero(velocity.x, friction * dt)
velocity.x = clamp(velocity.x, -max_speed, max_speed)
The move_toward_zero function reduces the absolute value without going past zero, preventing the character from jittering back and forth as it stops. Different acceleration and friction values produce very different feels:
| Acceleration | Friction | Feel |
|---|---|---|
| High | High | Precise control, instant response |
| Low | Low | Slippery movement, like on ice |
| High | Low | Quick start, long stop |
| Low | High | Heavy character, slow start |
It also helps to reduce air control compared with ground control: a character that changes direction mid-air as readily as on the floor is not very believable, but removing it entirely makes the game frustrating. An intermediate value is almost always the best compromise.
Other useful forces
The same scheme extends easily:
- wind or currents: a constant horizontal acceleration in one area of the level;
- bounce: after hitting the ground, reverse the vertical velocity and multiply it by a coefficient less than 1;
- enemy knockback: add an instant impulse to the velocity, an immediate change rather than an acceleration over time;
- water: reduced gravity and increased friction in both directions.
Make everything tweakable
The most practical advice is to expose every parameter in a configuration file or a debug panel that can be edited while the game is running. In my work I have seen that the difference between mediocre and enjoyable movement almost always lies in small adjustments made while playing, not in the formulas. Recompiling every time a number changes slows this process down enormously.
In short
- Position, velocity and acceleration are enough for convincing game physics.
- Semi-implicit Euler integration with a fixed step is stable and simple.
- Gravity and jump velocity follow from the desired height and the time to apex.
- Stronger falling gravity, variable jumps and a speed cap improve the feel.
- Horizontal acceleration and friction define the character of the movement.