Skip to content

Camera authoring

Problem Statement

The game uses a fixed-perspective camera consistent with its art direction (high angle, narrow 30-degree FOV). Different areas of the level require different camera positions, angles, and tracking behaviors. Transitions between these areas must be smooth regardless of player movement speed, and the system must yield control cleanly to scripted cinematic sequences (such as the rune effect showcase).

Architecture

Three classes and one subsystem make up the camera system:

Class Role
URuneCameraSubsystem Central coordinator. Owns the interpolation camera, manages zone priority, and arbitrates between zone and cinematic cameras.
ACameraZoneActor Level-placed actor with a box trigger and a camera component. Designers configure position, angle, blend time, priority, and optional player tracking per zone.
AEffectShowcaseCamera Temporary camera spawned during rune effect showcases. Frames one or more target actors from an oblique angle, then is released when the showcase ends.
ARephraseCharacter::FollowCamera Fallback camera attached to the character. Active when the player is outside all zones and no cinematic is running.

Ownership Flow

Player enters zone -> ACameraZoneActor overlap -> URuneCameraSubsystem::OnZoneEntered
                                                       |
                                              [sort zones by priority]
                                                       |
                                              [highest priority zone wins]
                                                       |
                                              [interp camera blends toward zone camera]

Cinematic requested -> PushCinematicTarget -> direct SetViewTargetWithBlend
                                                       |
                                              [zone tracking paused]

Cinematic ends -> PopCinematicTarget -> RefreshActiveCamera
                                              |
                                    [resume zone interp or fall back to character]

Zone Camera Transition -- Interpolation Approach

The original implementation called SetViewTargetWithBlend on every zone change. When a player oscillated across a zone boundary, each call interrupted the previous blend mid-flight. The remaining blend distance collapsed to near zero, producing a visible snap.

The current system solves this with a persistent interpolation camera (InterpCamera):

  1. A hidden ACameraActor is spawned the first time any zone is entered.
  2. The player controller's view target is set to this interpolation camera once and stays there for all zone transitions.
  3. Each zone change only updates the interpolation target (position, rotation, FOV).
  4. Every frame, the subsystem's Tick drives the interpolation camera toward the target using VInterpTo / RInterpTo / FInterpTo.

Because there is only one view target (the interp camera) and no blend to interrupt, rapid zone switches produce smooth continuous motion instead of snapping.

The interpolation speed is derived from the zone's authored BlendTime: InterpSpeed = 6.0 / max(BlendTime, 0.1)

This keeps the apparent transition duration proportional to what the designer configured.

Zone Priority

When the player stands inside overlapping zones, the zone list is sorted descending by Priority. The highest-priority zone becomes the active camera source. When the player exits the active zone, the next highest-priority zone takes over seamlessly (the interp target simply changes). When no zones remain, the system falls back to the character's follow camera using the exited zone's blend parameters.

Player Tracking

Each zone can optionally track the player within its bounds:

  • Enabled per zone via bTrackPlayer.
  • Camera position follows the player's X/Y offset from the zone origin, applied to the authored camera position.
  • Clamped mode (bClampTracking = true): offset is clamped within TrackingClampExtent, preventing the camera from straying too far from its authored position.
  • Unclamped mode: camera follows freely using the full authored offset vector.
  • Tracking uses VInterpTo at the zone's TrackingInterpSpeed for smooth following.
  • Tick is only enabled while the zone is active (ActivateTracking / DeactivateTracking). Inactive zones have zero tick cost.

When a zone is deactivated (player exits or a cinematic starts), its camera resets to the authored position so re-entry starts from a known state.

Cinematic Override (Stack-Based)

The showcase system and other scripted sequences need temporary camera control. This is handled through a stack:

  • PushCinematicTarget(AActor*, BlendTime) adds a camera to the stack and immediately blends to it via SetViewTargetWithBlend. Zone tracking is paused.
  • PopCinematicTarget(BlendTime) removes the top entry. If the stack is still non-empty, the next entry becomes active. If the stack is empty, the system resumes zone interpolation (or falls back to the character camera).
  • IsCinematicActive() is exposed to Blueprint for systems that need to know whether the camera is currently under scripted control.

Cinematics bypass the interpolation camera entirely and use direct SetViewTargetWithBlend calls because scripted sequences need precise blend timing that the designer controls per-event.

Spawn-In-Zone Handling

If the player spawns already inside a camera zone, the overlap callback will not fire (no transition occurred). The zone handles this by running CheckInitialOverlaps on the next tick after BeginPlay. If the player character is already inside the zone bounds, the subsystem is notified with BlendTimeOverride = 0.0 so the camera snaps to position instantly without a visible blend from nowhere.

Consistent Visual Language

All cameras in the system share the same baseline parameters to maintain a cohesive look:

  • FOV: 30 degrees (narrow, reduces perspective distortion at height)
  • Default pitch: -50 degrees (steep top-down oblique)
  • Default zone camera height: 800 units above zone origin

The character's fallback camera uses similar values (-600, 0, 800 relative, -50 pitch, 30 FOV) so transitions between the zone system and the fallback are visually continuous.

Integration Points

System Integration
Character movement Uses PlayerCameraManager->GetCameraRotation() for input-relative movement direction, so movement always aligns with whichever camera is active.
Rune effect showcase Spawns an AEffectShowcaseCamera, calls PushCinematicTarget to take camera control, then PopCinematicTarget when the showcase sequence finishes.
Level design Designers place ACameraZoneActor instances in the level, configure box extent to define the trigger area, adjust camera transform, set priority for overlapping zones, and enable tracking where needed. All configuration is exposed as EditAnywhere properties.

File Manifest

File Contents
RuneCameraSubsystem.h / .cpp Subsystem: zone list, cinematic stack, interpolation camera, tick-driven blending
CameraZoneActor.h / .cpp Zone actor: trigger box, camera component, tracking logic, priority, overlap detection
EffectShowcaseCamera.h / .cpp Showcase camera: target framing, oblique angle positioning

See also: Camera system reference for the engineering deep dive.

Change history

  • 2026-05-23 — Gabriel Li — Removed project/date header; page starts at Problem Statement.