
Camera Systems Skill
- 9.1k installs
- 6.7k repo stars
- Updated October 28, 2026
Implement smooth 2D follow and 3D orbit cameras with deadzones and bounds.
npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill camera-systemsInstall if
Game devs whose follow or orbit camera feels jittery or cramped.
Skip if
Non-interactive cinematics authored only in DCC tools.
What camera-systems covers
- 2D deadzone follow with look-ahead and bounds clamp
- 3D third-person orbit with collision and FP look
- Pairs with Cinemachine, Camera2D, PhantomCamera
camera-systems by the numbers
| All-time installs | 9,068 | skills.sh registry |
|---|---|---|
| Repo stars | ★ 6.7k | GitHub |
| Security audit | 3 / 3 scanners passed | skills.sh audit |
| Repo last updated | October 28, 2026 | GitHub |
| Repository | gamedev-skills/awesome-gamedev-agent-skills ↗ | |
Data as of Nov 4, 2026 (Skillselion catalog sync)
What does the Camera Systems skill produce?
Camera system with smooth follow, bounds, and collision-aware orbit.
What files ship with camera-systems?
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
- 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.
- Follow smoothly and frame-rate independently. Move the camera toward the target with exponential smoothing or a spring (
SmoothDamp), not a fixedlerp(a, b, 0.1)— that 0.1 is per-frame and changes with frame rate. - 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.
- 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.
- 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.
- 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.
- 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.
- 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 margins2. 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 + lead4. 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:
_yaw -= e.relative.x * sensitivity
_pitch = clampf(_pitch - e.relative.y * sensitivity, -1.2, 0.4) # clamp pitch!
func _process(_dt):
pivot.rotation = Vector3(_pitch, _yaw, 0)
# $SpringArm3D handles wall collision: set spring_length + collision_mask; the child
# Camera3D slides in automatically. RIGHT: spring arm. WRONG: camera clips through walls.
# Unity 6.3 LTS: a Cinemachine 3 CinemachineCamera (namespace Unity.Cinemachine) with an Orbital
# Follow + Cinemachine Deoccluder; the CinemachineBrain on the Camera blends automatically.5. Screen shake hook (owned trigger lives in game-feel)
# Expose an additive offset the game-feel trauma model writes to; follow + shake compose.
var shake_offset := Vector2.ZERO # set each frame by game-feel (trauma^2 * noise)
func _apply(final_focus: Vector2) -> void:
global_position = final_focus + shake_offset # shake rides ON TOP of smooth follow
# Unity Cinemachine: add a CinemachineBasicMultiChannelPerlin and set amplitude from trauma.Pitfalls
lerp(pos, target, const)per frame is frame-rate dependent — floatier at 30 FPS, snappier at 144. Use1 - exp(-rate*dt)orSmoothDamp.- Following in the normal update before the target has moved yields a one-frame lag jitter. Follow in
LateUpdate/ after movement/physics resolve. - No bounds clamp lets the camera show black past the level edge. Clamp focus to the level rect (account for the viewport half-size so the view, not the center, stays inside).
- No deadzone in twitchy games makes the camera twitch with every micro-movement → nausea.
- Unclamped pitch in third/first-person flips the camera over the top. Clamp pitch to ~±80°.
- Camera clipping through walls in 3D — use a spring arm / occlusion ray to pull in.
- Snapping on teleport/respawn is jarring; either hard-cut intentionally (and reset smoothing) or fast-ease. Don't let a huge
SmoothDampdistance whip across the level. - Shake driving the follow target instead of an additive offset makes follow fight shake. Compose: smooth follow first, add shake offset last.
- Per-axis vs radial deadzone confusion — a box deadzone feels different from a circular one; pick deliberately.
References
- For the exponential-smoothing/spring derivation, a complete deadzone+look-ahead+bounds 2D rig, 3D spring-arm/orbit details, first-person look, multi-target/group framing and split-screen, cinematic camera blends, and the Cinemachine 3 / Godot Camera2D / PhantomCamera mapping, read
references/follow-and-framing.md.
Related skills
game-feel— owns screen-shake trauma/triggers; this skill exposes the offset it writes.godot-2d-movement,godot-3d-essentials— the player/world the camera frames; Camera3D setup.physics-tuning— interpolate camera follow with the physics step to kill jitter.platformer,fps-shooter— genres whose camera rules this skill implements.performance-optimization— cost of extra cameras, render targets, and split-screen.
Follow & framing — depth for camera-systems
Detail the camera-systems body defers here: the smoothing math, a full 2D rig, 3D orbit/first- person specifics, multi-target framing, cinematic blends, and the per-engine rig mapping. Snippets target Godot 4.7 and Unity 6.3 LTS / Cinemachine 3.
1. Why 1 - exp(-rate*dt) (frame-rate-independent smoothing)
A naive pos = lerp(pos, target, k) applies k per frame, so at 144 FPS it smooths far more per second than at 30 FPS — the feel changes with hardware. Exponential smoothing fixes the rate per second:
t = 1 - exp(-rate * dt) # rate ≈ 5 (floaty) .. 12 (snappy)
pos = lerp(pos, target, t) # identical convergence at any frame rateA critically-damped spring (Unity Vector3.SmoothDamp, or a hand-rolled spring with smoothTime) gives the same frame-rate independence plus velocity continuity (no overshoot). Prefer the engine's built-in spring/smoothing before hand-rolling.
2. Full 2D rig: deadzone + look-ahead + bounds
Order of operations each frame, after the target moves:
- Compute target overflow beyond the deadzone (box or circle) → move the focus by the overflow only.
- Add look-ahead:
focus += dir_of_motion * lookAheadDist, but ease the look-ahead offset itself toward its goal so a direction flip doesn't snap the view. - Smooth the camera toward the focus (section 1).
- Clamp so the visible rectangle stays in bounds: clamp camera center to
[min + halfView, max - halfView]per axis (clamp the view, not the center).
# Godot 4.7: clamp the VIEW, accounting for zoom and viewport size.
func _clamp_to_level(center: Vector2) -> Vector2:
var half := get_viewport_rect().size * 0.5 / zoom
return Vector2(
clampf(center.x, level_rect.position.x + half.x, level_rect.end.x - half.x),
clampf(center.y, level_rect.position.y + half.y, level_rect.end.y - half.y))
# If the level is smaller than the view on an axis, center on that axis instead of clamping.Tunables: deadzone 32–64 px, look-ahead 40–120 px eased over 0.2–0.4 s, smoothing rate 6–10.
3. 3D third-person orbit
- Rig: an invisible pivot at the character's shoulder/head height; yaw on the pivot, pitch on a child; the camera sits at
-springLengthon local Z. - Collision: a spring arm (Godot
SpringArm3D) or occlusion ray casts from pivot to desired camera position and shortens to the first hit so the camera never clips through walls. - Pitch clamp: ~
[-80°, +45°]so the camera can't flip or bury into the floor. - Sensitivity & invert: expose look sensitivity and invert-Y (see
input-systems). - Cinemachine 3 (Unity 6.3 LTS): a
CinemachineCamera(namespaceUnity.Cinemachine) with an Orbital Follow component and a Cinemachine Deoccluder (collision); theCinemachineBrainon theCamerablends between cameras. This replaces the v2CinemachineVirtualCamera/CinemachineCollidernames.
4. First-person look
- Yaw rotates the body (so movement aligns with view); pitch rotates only the camera head.
- Clamp pitch; never let roll accumulate.
- Mouse: use relative motion + capture the cursor (
Input.mouse_mode = MOUSE_MODE_CAPTURED). - Stick: apply a response curve + deadzone (see
input-systems) and frame-rate-scaled turn rate.
5. Multi-target / group framing
- Compute the bounding box of all targets; place the camera at the box center.
- 2D: set zoom so the box (plus padding) fits the viewport; clamp zoom min/max.
- 3D: dolly the camera back / adjust FOV to fit the box; Cinemachine has a Group Framing / Target Group component for this.
- Split-screen: one camera per player rendering to a viewport rectangle; budget the extra render cost (
performance-optimization).
6. Cinematic blends & transitions
- Blend between gameplay and cutscene cameras over a short time (ease-in-out). Cinemachine blends automatically when you change the active camera's priority; in Godot, tween a rig between marker transforms or swap
currentcameras with a fade. - Reset smoothing state on hard cuts/teleports so the camera doesn't slingshot across the map.
7. Per-engine rig summary
| Need | Godot 4.7 | Unity 6.3 LTS |
|---|---|---|
| 2D follow + smoothing + limits | Camera2D (position_smoothing_*, limit_*, drag_*) | Cinemachine CinemachineCamera (2D) + Confiner2D |
| 3D follow/orbit | pivot + SpringArm3D + Camera3D | CinemachineCamera + Orbital Follow + Deoccluder |
| Shake | additive offset from game-feel | CinemachineBasicMultiChannelPerlin |
| Bounds | limit_* / manual clamp | CinemachineConfiner2D/3D |
| Addon option | PhantomCamera (declarative 2D/3D rigs) | built-in Cinemachine |
Which skills are related to camera-systems?
Camera Systems skill FAQ
Who is the Camera Systems skill for?
Game devs whose follow or orbit camera feels jittery or cramped. Skip if: Non-interactive cinematics authored only in DCC tools.
When should I use camera-systems?
User mentions camera follow, deadzone, orbit, or jitter.
Is Camera Systems safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
Add a camera-systems badge
Show developers this skill is listed on Skillselion. Paste this into your README.