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):
- A hidden
ACameraActoris spawned the first time any zone is entered. - The player controller's view target is set to this interpolation camera once and stays there for all zone transitions.
- Each zone change only updates the interpolation target (position, rotation, FOV).
- Every frame, the subsystem's
Tickdrives the interpolation camera toward the target usingVInterpTo/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 withinTrackingClampExtent, preventing the camera from straying too far from its authored position. - Unclamped mode: camera follows freely using the full authored offset vector.
- Tracking uses
VInterpToat the zone'sTrackingInterpSpeedfor 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 viaSetViewTargetWithBlend. 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.