In a 2D game the camera decides what the player sees and, therefore, how well they can anticipate what is about to happen. A camera rigidly locked to the character works, but it transmits every small movement to the screen and tires the eyes. With a few tricks you get a far more pleasant and readable result.
What the camera is in 2D
Technically the camera is just an offset applied to everything that is drawn. If the camera is at (cx, cy), an object at world position (x, y) is drawn on screen at:
screen_x = (x - cx) * zoom + screen_width / 2
screen_y = (y - cy) * zoom + screen_height / 2
The point (cx, cy) corresponds to the centre of the screen. Zoom lets you move the view closer or further away. Many engines and graphics libraries provide a camera object that applies this transformation automatically, but the principle is always the same.
Following with damping
Instead of copying the player's position, the camera approaches gradually:
cx += (player.x - cx) * factor
With a factor between 0 and 1, the camera covers a fraction of the remaining distance each time: it moves quickly when far away and slows down as it gets close. The effect is soft and natural.
There is one catch, though: this formula depends on the frame rate. To make it independent, you use an exponential form:
cx += (player.x - cx) * (1 - exp(-speed * dt))
That way the camera behaves the same at 30, 60 or 144 frames per second.
The dead zone
The dead zone is a rectangle in the centre of the screen within which the player can move without the camera shifting. Only when the character leaves it does the camera follow, just enough to bring them back to the edge of the zone.
It is especially useful in platformers: small steps back and forth, or hopping on the spot, do not make the screen sway. Vertically, many games adopt an even stricter rule: the camera updates its height only when the character lands on a higher or lower platform, ignoring the jump motion.
Looking ahead
A player running to the right needs to see what is coming from the right, not what they are leaving behind. Look-ahead places the camera's target in front of the character, in the direction of movement:
target_x = player.x + direction * lookahead
Look-ahead can depend on speed: the faster the character runs, the further ahead the camera looks. When the direction changes, damping makes the transition gradual instead of a sudden jump.
Respecting level bounds
When the character approaches the edge of the map, the camera must not show the void beyond the level. Its coordinates are clamped:
cx = clamp(cx, half_view_width, level_width - half_view_width)
cy = clamp(cy, half_view_height, level_height - half_view_height)
If the level is smaller than the screen on one axis, it is simplest to centre it on that axis. Many games also define rooms or zones with their own bounds: on entering a new area the bounds change and the camera scrolls to the new framing.
Pixel art and integer coordinates
With pixel-art graphics, a camera at fractional coordinates can produce an annoying shimmer: sprite pixels fall across screen pixels and are rounded differently from one frame to the next. The most common solutions:
- round the camera position to the nearest whole pixel before drawing;
- render the game at low resolution onto a texture and then scale it up by an integer factor;
- keep the fractional position for calculations, rounding only at render time.
Screen shake
Screen shake communicates impact: an explosion, a powerful hit, a boss landing. The most controllable approach uses a «trauma» value between 0 and 1:
trauma = min(1, trauma + event_intensity)
shake = trauma * trauma
offset_x = max_shake * shake * noise(t)
offset_y = max_shake * shake * noise(t + 100)
trauma = max(0, trauma - decay * dt)
Squaring the trauma makes light shakes almost imperceptible and strong ones very noticeable. Using a continuous noise function instead of pure random numbers produces a more organic movement. The offset is added only at render time, without altering the camera's real position.
It is easy to overdo screen shake. Good practice is to offer an option to reduce or disable it, for the benefit of people who suffer from motion sickness or find abrupt movement unpleasant.
Parallax
Finally, to add depth, background layers can move more slowly than the camera:
| Layer | Parallax factor |
|---|---|
| Sky | 0.0 – 0.1 |
| Distant mountains | 0.2 – 0.4 |
| Nearby hills | 0.5 – 0.7 |
| Gameplay level | 1.0 |
| Foreground | 1.1 – 1.3 |
Each layer is drawn with an offset equal to the camera position multiplied by its factor.
In short
- The 2D camera is an offset applied to everything that is drawn.
- Exponential damping gives smooth, frame-rate-independent following.
- A dead zone and look-ahead keep the framing stable and useful to the player.
- Level bounds and integer coordinates prevent empty areas and shimmering.
- Screen shake and parallax add impact and depth, in moderation.