Camera and Phantom Camera
Camera3D, first/third-person views, and a plugin equivalent of Cinemachine.
Camera3D — the main node
Camera3D is the node that “looks” into the scene. Only one Camera3D is active at any moment (if
you’re not using separate viewports). A scene can have several cameras; the active one is the one
whose current = true, or the one made active via make_current().
Key properties:
projection—PERSPECTIVE(3D, with perspective) orORTHOGONAL(without, for isometric/2.5D).fov— field of view in degrees, vertical. The FPS standard is 60–90°.near,far— clipping planes. A large spread hurts Z-buffer precision (Z-fighting).cull_mask— a bitmask: which visual layers this camera renders.
@onready var camera: Camera3D = $Head/Camera3D
func _ready() -> void:
camera.fov = 75.0
camera.near = 0.05
camera.far = 500.0
Simple follow
The equivalent of Unity’s SmoothDamp approach. Godot has no built-in SmoothDamp, but it has lerp:
extends Camera3D
@export var target: Node3D
@export var offset: Vector3 = Vector3(0, 2.0, 4.0) # behind, higher up
@export var smoothing: float = 5.0
func _process(delta: float) -> void:
if target == null:
return
var desired = target.global_position + target.global_transform.basis * offset
global_position = global_position.lerp(desired, smoothing * delta)
look_at(target.global_position + Vector3.UP * 1.5, Vector3.UP)
vector.lerp(to, weight) is a Vector3 method for linear interpolation. look_at(target, up_vector)
aims the camera’s “forward” (−Z) at the target.
If you move the camera in _physics_process, it will “stick” on frames between physics ticks and
look jittery. The camera is a render task; its place is in _process. If the player is a
CharacterBody3D with physics, interpolate its position or enable global physics interpolation in
Project Settings (since 4.3).
Phantom Camera — a Cinemachine equivalent
Godot core has nothing on the level of Unity’s Cinemachine. But there’s Phantom Camera —
a popular community plugin (ramokz/phantom-camera in the Asset Store / Godot Asset Library).
It provides virtual cameras with blends, follow / look-at modes, framing, dead zones, and hosts.
Installation: AssetLib (inside the editor) → search for “Phantom Camera” → Install. Enable it in Project → Project Settings → Plugins.
A basic follow-camera in Phantom Camera
Player ← CharacterBody3D
└── PhantomCamera3D_Follow ← virtual camera (follow_mode = ThirdPerson)
PhantomCameraHost ← one per scene, attached to the main Camera3D
└── Camera3D ← the real camera
Virtual camera parameters:
- Follow Mode —
Simple,Group,Path,Framed,ThirdPerson. - Look At Mode —
None,Simple,Group,Mimic. - Tween Resource — the curve and duration of the blend when switching.
- Priority — priority; PhantomCameraHost automatically picks the camera with the highest one.
# Switching between virtual cameras
$PhantomCamera3D_FirstPerson.priority = 20
$PhantomCamera3D_ThirdPerson.priority = 10
# The host smoothly transitions to FirstPerson
If your game needs cutscenes, first/third-person view switching, a drone camera, or fine framing — install the plugin from the very start. Writing all of this by hand is slow and fragile.
Rotating the camera with the mouse (FPS)
We already saw this in the input chapter, but let’s revisit it in the camera context:
extends CharacterBody3D
@onready var head: Node3D = $Head
@onready var camera: Camera3D = $Head/Camera3D
@export var sensitivity: float = 0.003
const MAX_PITCH: float = 1.4
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
# yaw — rotates the player's body (affects movement direction)
rotate_y(-event.relative.x * sensitivity)
# pitch — head only, the body doesn't tilt
head.rotate_x(-event.relative.y * sensitivity)
head.rotation.x = clamp(head.rotation.x, -MAX_PITCH, MAX_PITCH)
Hierarchy:
Player (CharacterBody3D) ← rotate_y (yaw)
├── CollisionShape3D
└── Head (Node3D) ← rotate_x (pitch)
└── Camera3D ← position (0, 0, 0)
Pitch (vertical) is applied to the Head so that the player’s collider doesn’t tilt when you look up at the sky.
Multiple viewports — split screen, mini-maps
For split-screen or picture-in-picture, use SubViewport + SubViewportContainer:
SubViewportContainer (stretches across half the screen)
└── SubViewport
└── Camera3D (current = true)
└── (the same 3D world, but via a shared World3D)
SubViewport.world_3d = main_viewport.world_3d — both cameras render the same scene.
Projection types
Camera3D.projection affects the feel:
- PERSPECTIVE — natural 3D where distant objects shrink.
- ORTHOGONAL — no perspective. The
sizeproperty sets the width of the visible area. Suitable for isometric views, technical visualizations, and 2.5D. - FRUSTUM — an advanced asymmetric projection (off-axis). Rarely needed — VR, special effects.
In the next chapter — rendering.