From 644bfc8c3d5473e919cfea1f794ba0b353b66d41 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Tue, 7 Apr 2026 23:34:20 -0400 Subject: [PATCH] NEON-25: lock low-controversy camera defaults in implementation plan --- docs/plans/NEON-25-implementation-plan.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/plans/NEON-25-implementation-plan.md b/docs/plans/NEON-25-implementation-plan.md index 80a2dad..95f8b6f 100644 --- a/docs/plans/NEON-25-implementation-plan.md +++ b/docs/plans/NEON-25-implementation-plan.md @@ -16,7 +16,7 @@ **In scope** -- Godot: `Camera3D` (on a small rig) for isometric presentation; follow the player / follow target each frame (**smooth follow** via lerp/damped tracking unless profiling suggests snap — document the chosen behavior in code/README). +- Godot: `Camera3D` (on a small rig) for isometric presentation; follow the player / follow target each frame per **locked defaults** below (smooth follow + teleport snap). - **Player-facing controls:** no camera rotate / orbit; **yaw stays at default** (`0` rad) for normal play. - **`CameraState`** (dedicated script type): follow target reference, **yaw** (always `0` in prototype UX), default distance or zoom index (**single band** / scalar distance until NEON-26). - **Seam:** `@export` or config fields (e.g. `allow_yaw`, `max_yaw_deg`) default off; short comment where orbit input would apply. @@ -38,14 +38,24 @@ - [ ] **Seam** for optional yaw (config/flags) documented in code or module implementation snapshot. - [ ] Short note in client or `E1_M2_IsometricCameraController.md` implementation snapshot when merged. +## Locked defaults (agreed before implementation) + +| Topic | Choice | +|--------|--------| +| **Follow motion** | **Damped / lerped** toward the desired rig position; **teleport snap** when error exceeds a threshold (e.g. after `snap_to_server` or huge separation) so the camera never eases across the map. | +| **Framing** | **Match** the current static `World/Camera3D` framing in `main.tscn` as the starting **distance / pitch / FOV**; fine-tune in the inspector after follow is wired. | +| **`CameraState` refresh** | Rig **owns one instance** and **updates it every frame** after computing pose (simplest contract for future readers). | +| **Follow target wiring** | **`@export NodePath`** (resolve to `Node3D` each tick); tests may assign path or set target via code as needed. | +| **Update phase** | **`_process`** (not `_physics_process`) for the rig. | + ## Technical approach -1. **Introduce `CameraState`** as a small **RefCounted** (or equivalent) script under `res://scripts/` with explicit fields: follow target (`Node3D` reference or `NodePath` resolved each frame), **yaw** (`float`, default `0`), **distance** or zoom index placeholder for NEON-26, and room for future flags. Avoid `class_name` so headless CI matches [godot-client-script-organization](../../.cursor/rules/godot-client-script-organization.md) guidance; tests can `preload` the script. +1. **Introduce `CameraState`** as a small **RefCounted** (or equivalent) script under `res://scripts/` with explicit fields: follow target path (or resolved node identity in state for debug), **yaw** (`float`, default `0`), **distance** or zoom index placeholder for NEON-26, and room for future flags. Avoid `class_name` so headless CI matches [godot-client-script-organization](../../.cursor/rules/godot-client-script-organization.md) guidance; tests can `preload` the script. 2. **Isometric rig** — add a **`Node3D`** (e.g. `IsometricFollowCamera` or `CameraRig`) with script that: - Owns or references the active **`Camera3D`** (`current = true`). - - Each `_process` (or `_physics_process` if we want lockstep with player; default `_process` for smooth view): read player **`global_position`**, compute desired eye position from **fixed pitch** + **effective yaw** (forced to `0` when `allow_yaw` is false), optionally **lerp** rig origin toward target+offset for stability. - - Updates a **`CameraState`** instance each frame (or on change) for future consumers. -3. **Scene wiring** — replace the static **`World/Camera3D`** transform in `main.tscn` with a rig under `World` (camera as child), **`@export`** follow target = `Player`, distances matching current prototype readability (~existing diagonal framing). + - Each **`_process`**: resolve **`NodePath`** follow target, read **`global_position`**, compute desired eye position from **fixed pitch** + **effective yaw** (forced to `0` when `allow_yaw` is false), **lerp** rig toward target+offset; **snap** rig/camera when separation exceeds a configured threshold. + - Updates the owned **`CameraState`** instance **every frame** after pose is finalized. +3. **Scene wiring** — replace the static **`World/Camera3D`** transform in `main.tscn` with a rig under `World` (camera as child), **`@export`** follow **`NodePath`** → `Player`, initial exports chosen to **match** the previous static camera readability. 4. **`main.gd`** — keep thin: obtain rig/camera reference for **`ground_pick.fallback_camera`** (unchanged contract); no camera math in `main.gd`. 5. **Yaw seam** — when `allow_yaw` is false: ignore any future input hook and set state yaw to `0`; when true (dev-only): clamp by `max_yaw_deg` and apply to basis (implementation stub can leave input unbound). 6. **Docs** — add a short **Implementation snapshot** subsection to `E1_M2_IsometricCameraController.md` after merge (or README pointer + module snapshot — satisfy AC). @@ -79,6 +89,6 @@ Godot may add companion `.uid` files when scripts are first touched in the edito ## Open questions / risks -- **Smooth vs snap:** Default to **damped follow**; if lag is noticeable at 120 Hz physics, reduce smoothing or align update phase — document final choice. +- **Smoothing tuning:** If follow feels floaty or laggy after the locked defaults, adjust lerp speed or snap threshold; document chosen constants in script or README. - **`player.gd` `_snap_capsule_upright`:** Identity basis today; camera yaw `0` matches world-aligned capsule. Future facing yaw must stay consistent with E1.M2 policy (not this story’s implementation unless trivial). - **gdUnit rig tests:** May require small refactor (static math helpers) for stable headless asserts; acceptable follow-up within the same story if the first PR ships manual AC + `camera_state` tests.