M2.3 — Camera Follow¶
What you'll learn
- Follow the player with a
Camera2Dand tune position smoothing. - Apply camera limits and zoom as deliberate design choices.
- Keep the HUD still by placing it under
UIwhile the camera movesWorld(the M1.4 split).
How it applies
- The camera is the player's window into danger. In an ARPG the player must see incoming enemies and loot before they reach them. Zoom and follow behavior decide how much warning the player gets; too tight a zoom and off-screen attacks feel unfair, too wide and the character is a speck.
- Smoothing is feel, not decoration. A camera rigidly glued to the player jitters with every sub-pixel movement and amplifies motion sickness; light smoothing lags the camera a hair behind, which reads as weight and polish. It is one boolean with an outsized effect on how the game feels.
- Limits prevent showing the void. Clamping the camera to the arena bounds stops the player from scrolling past the level edge into empty space — a cheap setting that separates a finished-feeling game from a prototype.
- The world/UI split pays off here. Because the camera affects only
World(M1.4), the HUD underUIstays pinned automatically. If the HUD had been parented underWorld, it would now scroll off-screen with the camera — the bug this split was designed to prevent.
Concepts¶
Camera2D follows by being a child¶
A Camera2D defines the rectangle of the 2D world shown on screen, centered on the camera's global
position. The simplest, most robust follow is to make the camera a child of the player: as the player
moves, the camera moves with it, keeping the player centered. No follow code required — the scene tree
does the work.
The camera must sit under World (directly or via the player, who is under World), not under UI.
Anything under the UI CanvasLayer ignores the camera entirely; anything under World is framed by
it. This is the concrete payoff of M1.4's split: world content scrolls, HUD content does not.
Smoothing¶
A camera rigidly pinned to the player snaps exactly to every position, including the tiny per-frame jitters of physics movement, which can look harsh. Position smoothing makes the camera chase the target with a slight lag instead of teleporting to it:
position_smoothing_enabled = trueposition_smoothing_speedcontrols how quickly it catches up (higher = tighter, lower = floatier).
The effect: the camera trails the player by a fraction of a second, which reads as weight. Tune the speed to taste; very high values approach no smoothing, very low values feel like the camera is on a rubber band.
Example
Two cameras, same player. No smoothing: the view locks to the player's exact pixel each frame; on a pixel-art game with pixel snap (M1.1) it can look stuttery as the snapped position jumps a whole pixel at a time. Smoothing at a moderate speed: the camera interpolates toward the player, hiding the per-pixel jumps and adding a faint sense of momentum when the player changes direction. Same movement code; the boolean changes the feel.
Limits¶
Camera2D exposes limit_left/top/right/bottom in pixels. The camera will not scroll past these world
coordinates, so the player can move to the arena edge without the view revealing the empty space beyond
it. Set the limits to the bounds of the playable area (M8 builds a real arena; for now you can leave them
at the defaults, which are effectively unbounded, or set generous values to experiment).
Limits also interact with stretch: with Aspect = keep (M1.1), the visible region is the base
resolution; limits clamp where that region's edges can land in the world.
Zoom¶
Camera2D.zoom scales the view. Counterintuitively, a zoom greater than 1 zooms in (the world
appears larger / you see less of it), and less than 1 zooms out. A top-down ARPG typically zooms in
somewhat so the character and nearby enemies are readable, trading away peripheral awareness. Like
speed in M2.1, zoom is a tuning knob worth exposing or at least centralizing, because it changes the
game's readability and difficulty (more zoom = less warning of off-screen threats).
Walkthrough¶
- Open
res://scenes/actor/player.tscn. - Add a child of
Player:Camera2D, renamedCamera. - In the Inspector for
Camera: - Enable Position Smoothing → Enabled.
- Set Position Smoothing → Speed to a moderate value (try
5–8) and adjust after testing. - Optionally set a Zoom of
(1.5, 1.5)to zoom in; adjust to taste. - Leave Limit at defaults for now (the arena arrives in M8); note where these are for later.
- Confirm
CameraisEnabled(aCamera2Dbecomes the active camera when it enters the tree if it is the only/first one; with several, theenabled/make_currentrules apply). - Save the player scene. Open
main.tscn— the player (with its camera) is already underWorldfrom M2.1. - Press
F5. Walk around: the player stays centered and the (currently empty) world scrolls beneath. There is nothing else to see yet, but you can confirm smoothing by changing direction sharply and watching the camera ease rather than snap.
Optional sanity check
Temporarily add a Sprite2D with a large texture somewhere under World (not under the player), at a
fixed position. Walk away from it and confirm it scrolls off the edge of the screen as the camera
follows you — proof the camera frames World. Then (mentally or with a temporary Label under the
UI CanvasLayer) note that a HUD element would stay fixed instead. Remove the temporary sprite.
Self-check quiz¶
Q1 — What is the simplest robust way to make the camera follow the player?
A. Write follow code in _process that lerps the camera toward the player each frame.
B. Make Camera2D a child of the player so it inherits the player's movement via the scene tree.
C. Parent the camera under the UI CanvasLayer.
D. Set the camera's position equal to the mouse each frame.
Reveal answer
B. Parenting the camera to the player makes the scene tree do the following for free — no code,
no drift. A works but is unnecessary code for the common case (and is what smoothing already does
internally). C is the bug: under CanvasLayer the camera and anything there ignore world
framing, and the camera wouldn't track world content. D is a different mechanic (mouse-look),
not player-follow.
Q2 — A Camera2D has zoom = (2, 2). What does the player see compared to zoom = (1, 1)?
A. The world appears smaller; more of it is visible (zoomed out). B. The world appears larger; less of it is visible (zoomed in). C. No change; zoom only affects 3D cameras. D. The HUD scales but the world does not.
Reveal answer
B. For Camera2D, zoom greater than 1 magnifies the world (zoom in), showing less area. This
trips people up because it is the opposite of some engines' conventions. C is false. D is false —
zoom affects the world view; the HUD under CanvasLayer is unaffected by the camera entirely.
Q3 — Why does enabling position smoothing improve feel on a pixel-snapped game specifically?
A. Smoothing increases the frame rate.
B. With pixel snap, the player's snapped position jumps a whole pixel at a time; smoothing
interpolates the camera between those jumps so the view doesn't stutter.
C. Smoothing disables pixel snap.
D. Smoothing is required for Camera2D to function.
Reveal answer
B. Pixel snap (M1.1) quantizes positions to whole pixels, which can make a hard-locked camera visibly step; smoothing eases the camera between those discrete positions, hiding the steps. A is false. C is false (they coexist; the snap applies to nodes, the smoothing to the camera's chase). D is false — a camera works without smoothing; smoothing is a feel improvement.
Integration question¶
Q4 — open
The camera you just added is a child of the player, which is a child of World; the HUD you build in
later chapters is a child of UI (a CanvasLayer). Using the chain from keypress to screen, explain
why the player's health bar will stay fixed on screen while the dungeon scrolls, and what single
change to the scene tree would break that — turning the health bar into something that slides away.
Reveal expected answer
The camera frames everything under World: when the player moves, the camera (its child) moves,
so all World content — tilemap, enemies, loot — shifts on screen to keep the player centered.
The UI node is a CanvasLayer (M1.4), which renders in its own screen space and is not framed
by any Camera2D; a health bar parented under UI therefore draws at fixed screen coordinates
regardless of camera movement. The single change that breaks it is re-parenting the health bar
(or the whole HUD) from UI to World — once under the camera-framed branch, it would scroll
with the dungeon and slide off the edge as the player walks. The world/UI split exists precisely
to make that mistake structural and obvious rather than a subtle per-element bug.