6.3 KiB
NS-24 — Implementation plan
Story reference
| Field | Value |
|---|---|
| Key | NS-24 |
| Title | Godot client: eliminate CharacterBody3D idle / rest vibration |
| Jira | NS-24 |
| Parent | NS-20 — Tech Debt |
| Related | 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
CharacterBody3Dplayer driven byplayer.gd(move_and_slide, floor interaction,NavigationAgent3Dcoupling as relevant). - Preserve NS-23 behavior: walking, arrival at goal,
snap_to_serveron 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.mdand/or theplayer.gdheader (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
- At rest (no click-to-move goal, feet on floor), no visible vibration in the default prototype scene for several seconds of observation.
- Walking, arrival, and
snap_to_serverstill behave correctly; no regression on NS-19 bumps or the NS-23 obstacle / descend-bypass tradeoff documented inclient/README.md. - 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
- 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.
- CharacterBody3D / physics — iterate on documented levers:
move_and_slideat idle withfloor_snap_length = 0;safe_margin; compoundFloorcollision;physics/3d/physics_engine(Jolt Physics vs Godot Physics — Jolt targets internal-edge / compound-shape jitter);floor_stop_on_slope,motion_mode,up_directionif still needed. - Navigation agent — confirm
NavigationAgent3Dtarget 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. - Camera / parenting — rule out camera or child node noise if transform root is stable.
- Ship the smallest change that clears the AC (code, scene, or project), then update
client/README.mdto 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, docs/plans/NS-23-implementation-plan.md (residual polish pointer), and client/README.md after changes.