Skip to content
Development
Skill

/camera-systems

Build game cameras that feel good — 2D follow with a deadzone, look-ahead, smoothing, and level-bounds clamping; 3D third-person orbit with collision and first-person look; plus multi-target framing and a shake hook. Engine-neutral techniques that pair with the engine's camera

From plugin
awesome-gamedev-agent-skills
6.3k77 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill camera-systems --agent claude-code

SKILL.md

camera-systems.SKILL.md
name: camera-systems
description: >
  Build game cameras that feel good — 2D follow with a deadzone, look-ahead, smoothing, and
  level-bounds clamping; 3D third-person orbit with collision and first-person look; plus
  multi-target framing and a shake hook. Engine-neutral techniques that pair with the engine's
  camera node and rigs like Unity Cinemachine or Godot Camera2D/PhantomCamera. Use when the user
  mentions camera follow, follow camera, deadzone, look-ahead, camera smoothing, camera bounds/
  limits, third-person camera, orbit camera, first-person look, Cinemachine, or camera jitter.

Camera systems

The camera is the player's window; bad camera work makes a good game feel awful. This skill covers the engine-neutral camera techniques — smooth follow, deadzones, look-ahead, bounds clamping, third-person orbit with collision, first-person look, and multi-target framing — and maps them onto each engine's camera node or rig.

When to use

  • Use when a 2D camera should follow the player smoothly, stay inside the level, lead the

player's motion, or ignore small movements (deadzone).

  • Use when building a 3D third-person orbit camera (mouse/stick look, collision push-in) or a

first-person look controller, or framing multiple targets at once.

  • Use to fix camera jitter, snapping, motion sickness, or a camera that shows past the level edge.

**When *not* to use:** for the *magnitude and trigger* of screen shake and impact juice, use `game-feel` (this skill exposes the shake offset hook it drives). For the engine's concrete camera node/component setup, use `godot-3d-essentials` (Camera3D, environment) or the engine skill. For player movement itself use the engine movement skill (`godot-2d-movement`). For performance of many cameras/render targets, see `performance-optimization`.

Core workflow

1. **Decide what the camera serves.** Platformer (lead the jump, see hazards), top-down (center with deadzone), third-person (orbit + collision), first-person (look only). The genre sets the rules. 2. **Follow smoothly and frame-rate independently.** Move the camera toward the target with exponential smoothing or a spring (`SmoothDamp`), not a fixed `lerp(a, b, 0.1)` — that 0.1 is per-frame and changes with frame rate. 3. **Add a deadzone** so tiny target movements don't nudge the camera; it only follows once the target leaves a box/zone. Stops nausea in twitchy games. 4. **Lead the action with look-ahead** by offsetting the camera target in the direction of motion or facing, eased in/out so it doesn't whip. 5. **Clamp to level bounds** so the camera never shows outside the playable area; combine with smoothing so it eases to a stop at the edge. 6. **For 3D, separate look from collision.** Orbit via yaw/pitch on a rig; use a spring arm / ray to pull the camera in when geometry blocks it; clamp pitch. 7. **Update the camera after the target moves.** Follow in the late/post step (after movement and physics resolve) to avoid a one-frame lag jitter. 8. **Verify by moving the target at low and high frame rates**, into corners and walls, and at the level edges; confirm no jitter, no peeking past bounds, smooth stops. Report what you saw.

Patterns

1. Godot 2D built-in follow: smoothing + bounds (don't hand-roll first)

# Godot 4.7 Camera2D. Engine-provided smoothing + hard limits + drag margins.
@onready var cam := $Camera2D
func _ready() -> void:
    cam.make_current()
    cam.position_smoothing_enabled = true
    cam.position_smoothing_speed = 6.0           # higher = snappier; lower = floatier
    cam.limit_left = 0; cam.limit_top = 0        # clamp to the level rect (pixels)
    cam.limit_right = level_width; cam.limit_bottom = level_height
    cam.drag_horizontal_enabled = true           # built-in deadzone via drag margins

2. Frame-rate-independent smooth follow (when you hand-roll it)

# RIGHT: exponential smoothing — same feel at any FPS. `rate` ~ 5..12.
func _follow(dt: float) -> void:
    var t := 1.0 - exp(-rate * dt)               # converges correctly regardless of dt
    global_position = global_position.lerp(target.global_position, t)
# WRONG: global_position = global_position.lerp(target.global_position, 0.1)
#        → faster smoothing at higher FPS; different feel on every machine.
# Unity 6.3 LTS: Vector3.SmoothDamp(transform.position, target.position, ref vel, smoothTime) in
# LateUpdate gives the same spring behavior with built-in frame-rate correction.

3. Deadzone + look-ahead (lead the player, ignore jitter)

# Camera only chases once the target leaves the deadzone box, then aims AHEAD of motion.
func _camera_target(dt: float) -> Vector2:
    var to := target.global_position - _focus
    var dz := deadzone_half_extents                  # e.g. Vector2(48, 32)
    # Only move the focus by the overflow beyond the deadzone (per axis).
    _focus.x += clampf(absf(to.x) - dz.x, 0, INF) * signf(to.x)
    _focus.y += clampf(absf(to.y) - dz.y, 0, INF) * signf(to.y)
    var lead := target.velocity.normalized() * look_ahead_dist    # aim ahead of travel
    return _focus + lead

4. 3D third-person orbit with collision push-in

# Godot 4.7. Yaw/pitch a pivot; a SpringArm3D auto-pulls the camera in when blocked.
func _unhandled_input(e):
    if e is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
        # screen_relative is unscaled; `relative` is scaled by the stretch settings, so
        # sensitivity would change with window size. The docs recommend screen_relative
        # for captured-mouse aiming.
        _yaw -= e.screen_relative.x * sensitivity
        _pitch = clampf(_pitch - e.screen_relative.y * sensitivity,
                        deg_to_rad(-80.0), deg_to_rad(45.0))       # clamp pitch!
func _process(_dt):
    pivot.rotation = Vector3(_pitch, _yaw, 0)
    # $SpringArm3D handles wall collision: set spring_length + collision_m
Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
6.3k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about camera-systems.

Which coding agents can use camera-systems?
camera-systems installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.