# NS-24 — Implementation plan ## Story reference | Field | Value | |--------|--------| | **Key** | NS-24 | | **Title** | Godot client: eliminate CharacterBody3D idle / rest vibration | | **Jira** | [NS-24](https://neon-sprawl.atlassian.net/browse/NS-24) | | **Parent** | [NS-20 — Tech Debt](https://neon-sprawl.atlassian.net/browse/NS-20) | | **Related** | [NS-23](https://neon-sprawl.atlassian.net/browse/NS-23) (path-follow baseline; idle jitter explicitly deferred here) | ## Goal, scope, and out-of-scope **Goal:** **Stable rest pose** — no perceptible micro-vibration when horizontal velocity is zero, there is no active walk goal, and the avatar stands on walkable geometry, including prototype floors and **NS-19** stepped bumps. **In scope** - Diagnose and fix (or materially reduce) idle jitter on the prototype **`CharacterBody3D`** player driven by **`player.gd`** (`move_and_slide`, floor interaction, **`NavigationAgent3D`** coupling as relevant). - Preserve **NS-23** behavior: walking, arrival at goal, **`snap_to_server`** on boot, descend bypass tradeoff, and **NS-19** validation UX on the server side (client must not regress those flows). - Document non-obvious fixes in **`client/README.md`** and/or the **`player.gd`** header (per Jira). **Out of scope** - Full prediction/reconciliation, server-side nav, or redesign of **MoveCommand** / **PositionAuthorityClient** contracts. - Replacing the capsule with a different locomotion stack unless a minimal inspector tweak is sufficient. ## Acceptance criteria checklist - [x] At rest (no click-to-move goal, feet on floor), **no visible vibration** in the default prototype scene for **several seconds** of observation. - [x] **Walking**, **arrival**, and **`snap_to_server`** still behave correctly; **no regression** on NS-19 bumps or the **NS-23** obstacle / descend-bypass tradeoff documented in **`client/README.md`**. - [x] If a **scene** or **project** setting change is required, it is **checked in** and called out for designers (README or short comment in scene notes). ## Resolution (shipped) **Jolt Physics** + **`physics_interpolation` off**; QA bumps on **separate `StaticBody3D`** bodies (no floor+bump compound internal edges); idle **`floor_max_angle`** loosened on rim/straddle; **rim settle** + **`random_floor_bump_mesh`** lip/wall escape; **`NavigationAgent3D.avoidance_enabled`** false, **`floor_block_on_wall`** true; **no `global_transform` in `_process`**; **visual** capsule **mesh Y lift** vs collision to reduce **z-fight** with bump tops. Shared bump collider fudge: **`client/scripts/random_floor_bump_collision_constants.gd`**. ## Technical approach 1. **Reproduce and isolate** in Godot 4.x editor: Remote **Transform** on the player (and camera parent if suspected), **Visible Collision Shapes**, and frame / physics profiler to see whether jitter is position, floor snap, or render-only. 2. **CharacterBody3D / physics** — iterate on documented levers: **`move_and_slide`** at idle with **`floor_snap_length = 0`**; **`safe_margin`**; compound **`Floor`** collision; **`physics/3d/physics_engine`** (**Jolt Physics** vs Godot Physics — Jolt targets internal-edge / compound-shape jitter); **`floor_stop_on_slope`**, **`motion_mode`**, **`up_direction`** if still needed. 3. **Navigation agent** — confirm **`NavigationAgent3D`** target updates at rest (`set_target_position(global_position)` on clear/arrive) do not trigger path churn each frame; adjust only if measurements show agent-driven motion. 4. **Camera / parenting** — rule out camera or child node noise if transform root is stable. 5. **Ship** the smallest change that clears the AC (code, scene, or project), then update **`client/README.md`** to remove or narrow the “Known issue” idle jitter line and describe the chosen pattern. **Shipped note:** Idle uses **non-zero** **`floor_snap_length`** (see **`player.gd`**) rather than zero snap from the investigation list; straddle/rim logic and bump geometry split were the main levers. Code review: **`docs/reviews/2026-04-05-NS-24.md`**. ## Files to add | Path | Purpose | |------|---------| | `client/scripts/random_floor_bump_collision_constants.gd` | Single source for QA bump **`CylinderShape3D`** radius fudge vs mesh and **`RANDOM_FLOOR_BUMP_MESH_GROUP`** (shared by **`random_floor_bumps.gd`** and **`player.gd`** lip escape). | ## Files to modify | Path | Rationale | |------|-----------| | `client/scripts/player.gd` | Idle **`move_and_slide`** policy, walking snap, arrival path, bump lip escape (imports **`random_floor_bump_collision_constants.gd`**). | | `client/scenes/main.tscn` | Player **`CharacterBody3D`** / **`NavigationAgent3D`** inspector properties, collision layers, or nav region only if diagnosis requires it (AC: designer-visible changes documented). | | `client/project.godot` | Physics or project defaults (e.g. physics ticks, 3D defaults) only if required and safe for the whole project. | | `client/README.md` | Replace **Known issue: idle / rest jitter** with the fix summary and any manual verification deltas. | ## Tests | Action | What to cover | |--------|----------------| | **Manual (required)** | Run main scene with server as in **NS-16/NS-23** README steps: stand idle **10+ s** at spawn and after a move; click-move including **stepped bumps** (NS-19 props); cold boot **`snap_to_server`**; far/vertical rejects still show NS-19 UX; optional multi-click around **`Obstacle`** per NS-23 tradeoff. | | **Automated** | **None** — no Godot/GDScript test harness in repo; server **xUnit** suites unchanged unless an unrelated regression appears (not expected). | ## Open questions / risks **Resolved for prototype QA:** Primary causes were **compound internal edges** (mitigated by per-bump bodies), **physics/render** artifacts (**interpolation**, **`CharacterBody3D`** transform desync, **z-fight**), and **rim/wall** contacts (mitigated by idle policy + small escapes). **Remaining:** **`_snap_capsule_upright()`** uses full **identity basis** — when **yaw** facing is added, preserve **Y** rotation only (see **`TODO`** in **`player.gd`**). Moving platforms / uneven nav mesh not validated. ## PR / review Cross-check [E1.M1 — InputAndMovementRuntime](../decomposition/modules/E1_M1_InputAndMovementRuntime.md), **`docs/plans/NS-23-implementation-plan.md`** (residual polish pointer), and **`client/README.md`** after changes.