Files
open-wc/docs/modules/third-person-camera.md
sindoring 8776a6b362 gmp(M02): add 3.3.5 input profile
Work-Package: M02-GMP-INPUT-PROFILE-335-001
Agent: sindo-main-codex
Tests: player input/movement/capability/terrain/camera/presentation, coordinate/streaming, docs/coordination and renderer dry-run passed
Fidelity: build-12340 binding hashes and selected defaults pinned; TrinityCore turn rate and WoWee mouse routing are compatibility references with explicit limits
2026-07-15 00:17:34 +04:00

13 KiB

Third-Person Camera

Metadata

Field Value
Status Implemented
Target/work package M02 / M02-RND-CAMERA-001 and M02-GMP-INPUT-PROFILE-335-001 consumer update
Owners Camera presentation state and CameraPivot/Camera3D lifecycle
Last verified M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15
Profiles/capabilities Current sandbox orbit/zoom; identity collision policy

Purpose

Own third-person mouse capture, orbit yaw/pitch, zoom state and Camera3D local transforms on the existing CameraPivot node. Expose an explicit replaceable camera-distance collision policy while preserving the current no-collision sandbox baseline.

Non-goals

  • Implement an unverified physics raycast or claim build-12340 collision parity.
  • Own player movement, terrain queries, streaming focus or character visuals.
  • Implement first-person, follow lag, shoulder camera, cinematics or view modes.
  • Change current sensitivity, pitch/zoom limits, FOV or far plane.

Context and boundaries

flowchart LR
    InputMap[Remappable camera actions] --> Player[ThirdPersonWowController dispatch]
    Player --> Rig[ThirdPersonCameraRig]
    Mouse[Mouse motion] --> Player
    Rig --> Character[Character yaw transform]
    Rig --> Pivot[CameraPivot local transform]
    Rig --> Policy[CameraCollisionPolicy]
    Policy --> Distance[Resolved distance]
    Distance --> Camera[Camera3D local transform]
    Rig --> FlightBasis[Godot-world pivot basis]
    FlightBasis --> Movement[LocalPlayerMovementController adapter]

Allowed dependencies:

  • Existing PlayerInputActions names and Godot input events.
  • Explicit character, pivot and Camera3D Node references on the main thread.
  • Replaceable CameraCollisionPolicy selected by composition/tests.
  • Movement adapter may read the rig's Godot-world basis during sandbox flight.

Forbidden dependencies:

  • ADT/terrain parsing, movement velocity, gameplay state or animation ownership.
  • Camera rig discovering renderer/network/server services.
  • Default collision policy performing hidden raycasts or changing baseline distance.
  • Collision policy mutating rig/camera/character nodes.

Public API

Symbol Kind Purpose Thread/lifetime Errors
ThirdPersonCameraRig Node3D component Owns orbit/zoom state on CameraPivot Main thread; scene-owned Reports missing Camera3D
initialize_for_character(character_body) Composition method Attaches yaw target and captures initial character yaw Main thread after scene ready Null leaves yaw target absent
synchronize_yaw_from_character() Composition method Refreshes orbit yaw after keyboard movement turns the character Main physics thread Missing character leaves yaw unchanged
handle_camera_input(event) Input adapter Applies capture/release/zoom/orbit actions Main input thread Returns whether camera consumed state
set_collision_policy(policy) Composition method Replaces distance resolution policy Main thread Null rejected; old policy retained
refresh_camera_transform() Presentation update Applies pivot pose and resolved camera distance Main/physics thread Missing camera leaves state but no Camera3D mutation
godot_world_flight_movement_basis() Read model Returns pivot global basis for sandbox flight Main thread None
CameraCollisionPolicy.resolve_camera_distance(pivot, requested) Policy boundary Resolves camera distance in Godot units Main thread in current implementation Rig clamps output to [0, requested]
NoCameraCollisionPolicy Identity policy Preserves requested distance with no physics query Any main-thread call None

Read-only rig state: yaw_radians, pitch_radians, requested_distance_units, resolved_distance_units and is_mouse_captured.

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Camera action InputEvent Godot/player dispatch Camera rig Engine-owned event One dispatch
Input Mouse relative pixels Godot input Camera rig Engine-owned event One dispatch
Input Character Node3D Player composition Camera rig Scene-owned reference Rig scene lifetime
Input Keyboard-turn synchronization Player scene after yaw application Camera rig Synchronous notification Current physics tick
Input Collision policy Composition/test Camera rig Rig-held RefCounted Until replacement
Output Character yaw Camera rig Player transform Scene mutation Orbit event
Output Pivot pitch/height Camera rig CameraPivot Scene mutation Refresh
Output Requested/resolved distance Camera rig/policy Camera3D Rig-owned scalar/read model Until next input/refresh
Output Godot-world Basis Camera rig Flight movement adapter Value copy Physics tick

Side effects:

  • Mutates mouse mode, character yaw, CameraPivot local transform and Camera3D local transform.
  • Marks Camera3D current and sets its far plane once at ready.
  • Releases captured mouse on rig removal.
  • Creates no files, caches, RIDs, workers, network messages or gameplay state.

Data flow

flowchart LR
    Action[Camera action or mouse delta] --> State[Yaw pitch requested distance capture]
    State --> Clamp[Pitch/zoom clamps]
    Clamp --> Pivot[Pivot height and pitch]
    Clamp --> Request[Requested distance]
    Request --> Policy[CameraCollisionPolicy]
    Policy --> Safe[Clamp 0..requested]
    Safe --> Camera[Camera3D Z distance]
    State --> Character[Character yaw]

Lifecycle/state

stateDiagram-v2
    [*] --> MouseVisible
    MouseVisible --> MouseCaptured: camera rotate pressed
    MouseCaptured --> MouseVisible: camera rotate released
    MouseCaptured --> MouseVisible: release cursor
    MouseCaptured --> MouseCaptured: mouse motion / orbit and clamp
    MouseVisible --> MouseVisible: zoom and clamp
    MouseCaptured --> [*]: rig removed / release mouse
    MouseVisible --> [*]: rig removed

Zoom and collision distance are orthogonal to capture state. The requested zoom persists while a policy may temporarily choose a shorter resolved distance.

Main sequence

sequenceDiagram
    participant Engine as Godot Input
    participant Player as ThirdPersonWowController
    participant Rig as ThirdPersonCameraRig
    participant Policy as CameraCollisionPolicy
    participant Camera as Camera3D
    Engine->>Player: InputEvent
    Player->>Rig: handle_camera_input(event)
    Rig->>Rig: update capture/orbit/zoom state
    Rig->>Policy: resolve_camera_distance(pivot, requested)
    Policy-->>Rig: resolved distance
    Rig->>Camera: apply local position/rotation
    opt captured mouse motion
        Rig->>Player: mutate character yaw transform
    end

Ownership, threading and resources

  • Parent player scene owns the CameraPivot rig and Camera3D child.
  • Rig holds non-owning scene references to character and camera.
  • Rig owns scalar camera state and the RefCounted collision policy.
  • All input/state/scene mutations occur on the main thread.
  • Policy output is a scalar; policies must not mutate supplied nodes.
  • No background tasks, resources, RIDs or caches are created.

Errors, cancellation and recovery

Failure Detection Behavior Diagnostic Recovery
Camera3D missing _ready lookup State continues; transform application skipped ThirdPersonCameraRig error with path Restore child/path and recreate scene
Character missing Initialization Orbit state updates without character yaw mutation Composition/test failure Call initializer with player node
Null collision policy Setter guard Current policy retained Rig error Pass valid policy
Policy returns negative/too large Rig clamp Distance clamped to [0, requested] Policy regression tests Fix/replace policy
Rig removed while captured _exit_tree Mouse made visible Lifecycle regression Recreate rig normally

There is no asynchronous cancellation or persisted rollback. Recreating the rig restores configured defaults.

Configuration and capabilities

Setting/capability Default Profile Runtime mutable Effect
Initial pitch -18° Current sandbox State-owned after ready Initial orbit elevation
Pitch limits -65°..35° Current sandbox Exported Mouse clamp
Mouse sensitivity 0.003 rad/pixel Current sandbox Exported Orbit delta
Pivot height 1.7 units Current sandbox Exported Camera eye origin
Initial distance 8 units Current sandbox Exported before ready Requested zoom
Zoom limits/step 2..18, step 1 Current sandbox Exported Requested distance clamp
Far plane 50000 units Renderer sandbox Exported Camera far plane
Collision policy NoCameraCollisionPolicy Current sandbox Replaceable Identity distance/no raycast

Persistence, cache and migration

Camera state and policy are transient. No SavedVariables/settings schema, cache or migration is introduced. Scene paths remain ThirdPersonPlayer/CameraPivot/Camera3D, so renderer diagnostics and capture tools require no path migration.

Diagnostics and observability

  • Logs: missing camera/null policy emit explicit component errors.
  • Read models: yaw, pitch, requested/resolved distance and capture state.
  • Tests: dedicated verifier reports state/policy/scene counts.
  • Metrics/correlation IDs: not applicable to constant-time local presentation.

Verification

  • State regression: initial yaw/pitch/distance/height/far plane, capture/release, mouse orbit, character yaw, pitch clamps and zoom clamps.
  • Input-profile integration verifies keyboard yaw synchronization, preventing a later right-mouse orbit from restoring stale character yaw.
  • Policy regression: fixed-distance test policy changes Camera3D Z; identity policy restores requested distance; baseline policy contains no physics query.
  • Scene integration: Eastern Kingdoms, Kalimdor and asset-free regression scenes attach the rig to the existing CameraPivot.
  • Boundary: player no longer owns capture/yaw/pitch/distance or camera transform helper.
  • Existing input/movement/terrain regressions and renderer dry-run remain required.
  • Fidelity evidence: current numeric defaults and identity collision behavior only; original-client orbit/collision comparison remains open.
  • Performance: constant-time scalar/transform work once per physics tick and input event; no default raycast.

Extension points

  • Add an opt-in raycast collision policy after reference fixtures define mask, margin, recovery smoothing and first-person limit.
  • Add follow/shoulder/cinematic rigs as separate components, not branches inside this rig.
  • Persist user camera settings through a future versioned settings module.
  • Input contexts may suppress camera actions before dispatch without changing rig state.

Capability status

Capability Status Evidence Gap/next step
Independent third-person rig Implemented Three scene wirings and player boundary regression Integrate target checklist after review
Existing orbit/zoom behavior Implemented Numeric state/transform regression Compare against build 12340
Camera-relative sandbox flight basis Implemented Basis and movement regressions Restrict flight to sandbox profile
Replaceable collision policy Implemented Fixed and identity policy tests Implement verified collision policy
Active camera collision Planned Identity policy intentionally preserves baseline Capture original-client occluder fixtures first
First-person/follow policies Planned Outside current sandbox baseline Later completeness work

Known gaps and risks

  • Default collision policy performs no occluder avoidance, matching the previous sandbox.
  • Exact WoW camera orbit direction, pitch limits, zoom curve, collision margin and recovery smoothing are unverified.
  • Exported initial values are captured at ready; runtime mutation may require an explicit reconfiguration API later.
  • Mouse mode remains process-global Godot state and must be coordinated with future UI contexts.

Source map

Path Responsibility
src/scenes/player/third_person_camera_rig.gd CameraPivot state, input and transforms
src/scenes/player/camera_collision_policy.gd Replaceable distance-policy boundary
src/scenes/player/no_camera_collision_policy.gd Identity baseline policy
src/scenes/player/third_person_wow_controller.gd Camera event dispatch and movement-basis consumer
src/scenes/streaming/*_streaming.tscn Runtime rig composition
src/tests/scenes/player_input_regression.tscn Asset-free rig/player composition
src/tools/verify_third_person_camera_rig.gd State, policy, scene and boundary regression
  • ADR: none; current behavior and scene paths are preserved.
  • Upstream/reference: targets/02-player-decomposition.md, targets/roadmap/02-rendering-and-graphics.md, targets/roadmap/04-gameplay-systems.md, docs/ARCHITECTURE.md.