NEON-29: Fix nav path steer ping-pong (get_next_path_position)
Vertical+column branch scanned path for first point with XZ offset >5 cm; Jolt moved the body across that edge each tick and flipped the chosen waypoint → 180° velocity oscillation (vlat && ncol). Use NavigationAgent3D.get_next_path_position() when navigation not finished; fall back to auth goal when finished or degenerate.pull/41/head
parent
865eed2e53
commit
2821c5973b
|
|
@ -55,7 +55,7 @@ Elevated walkables use **distinct albedo tints** in `scenes/main.tscn` so screen
|
|||
|
||||
With the game server running ([`server/README.md`](../server/README.md)), each valid floor click sends a **`MoveCommand`** (**`POST`**) and a follow-up **`GET`** for **`PositionState`**. The server still **snaps** authority to the target (NEON-4/19); the client **moves** toward that verified position using **`NavigationAgent3D`** + a **baked mesh** instead of teleporting on the **`GET`** (NEON-8). **Boot** `sync_from_server()` **snaps** once so spawn matches the server.
|
||||
|
||||
**Tradeoff (prototype):** Horizontal motion is a **direct XZ bee-line** toward the server’s verified target. The baked **`NavigationMesh`** is still used when the goal surface is **clearly above or below** the feet and the capsule is already **within `DIRECT_APPROACH_RADIUS`** of the goal column — so terraces / steps can follow the mesh toward a ramp or tread instead of hugging a vertical face. **`get_next_path_position()`** is no longer the default steer (it regressed under **Jolt** at **120 Hz**). **No descend bypass** for gravity while on an upper deck (same as before). **Automatic routing around tall obstacles on one click is still not guaranteed** — use **several clicks** to go around e.g. the gray **`Obstacle`** when needed.
|
||||
**Tradeoff (prototype):** Horizontal motion is a **direct XZ bee-line** toward the server’s verified target. The baked **`NavigationMesh`** is still used when **vertical routing** is active (goal surface clearly above/below the feet, with Schmitt latch) **and** the capsule is inside the **nav column** hysteresis band (`NAV_COLUMN_STEER_ENTER_DIST` / `EXIT`) — so terraces / steps steer via **`NavigationAgent3D.get_next_path_position()`** toward the next waypoint. (A prior **manual** path scan with a **5 cm** “skip near waypoints” threshold caused **180° velocity flips** each physics tick; **`get_next_path_position()`** is **not** used as the **default** bee-line steer for all walking — that still regressed under **Jolt** at **120 Hz** when it was global.) **No descend bypass** for gravity while on an upper deck (same as before). **Automatic routing around tall obstacles on one click is still not guaranteed** — use **several clicks** to go around e.g. the gray **`Obstacle`** when needed.
|
||||
|
||||
**NEON-7 / movement QA bumps:** On **run**, **`spawn_short_random_bumps`** adds **two** green cylinders, each on its own **`StaticBody3D`** **sibling** of **`Floor`** under **`NavigationRegion3D`** (**`walkable`** on bump roots — avoids compound **internal-edge** jitter vs floor+cylinder on one body). Bump meshes use Godot group **`random_floor_bump_mesh`**. **Collision radius** = mesh **+ `COLLISION_RADIUS_EXTRA`** (see **`scripts/random_floor_bump_collision_constants.gd`**, capped by **`COLLISION_RADIUS_MAX`**). **`bake_navigation_mesh(false)`** after spawn.
|
||||
|
||||
|
|
|
|||
|
|
@ -368,15 +368,21 @@ func _apply_floor_block_for_descend_at_wall(feet_y: float) -> void:
|
|||
## When the authoritative goal shares XZ with the capsule (e.g. terrace above), horizontal
|
||||
## `full_to_goal` is zero and steering must follow the baked path toward a ramp/step, not +X.
|
||||
func _set_horizontal_velocity_from_nav_path_or_goal(fallback_goal_xz: Vector3) -> void:
|
||||
var pos: Vector3 = global_position
|
||||
var path: PackedVector3Array = _nav_agent.get_current_navigation_path()
|
||||
for i: int in range(path.size()):
|
||||
var p: Vector3 = path[i]
|
||||
var dh: Vector3 = Vector3(p.x - pos.x, 0.0, p.z - pos.z)
|
||||
if dh.length_squared() > 0.0025:
|
||||
_set_horizontal_velocity_toward(p, fallback_goal_xz)
|
||||
return
|
||||
_set_horizontal_velocity_toward(_auth_walk_goal, fallback_goal_xz)
|
||||
# Do **not** scan `get_current_navigation_path()` for the first point with
|
||||
# `dh.length_squared() > 0.0025` (5 cm). Jolt nudges the capsule across that radius every
|
||||
# physics tick → the chosen waypoint alternates → velocity sign flips 180° while
|
||||
# `needs_vertical_routing && _walk_nav_column_steering` (NEON-16 debug trace).
|
||||
if _nav_agent.is_navigation_finished():
|
||||
_set_horizontal_velocity_toward(_auth_walk_goal, fallback_goal_xz)
|
||||
return
|
||||
var next_pos: Vector3 = _nav_agent.get_next_path_position()
|
||||
var dh: Vector3 = Vector3(
|
||||
next_pos.x - global_position.x, 0.0, next_pos.z - global_position.z
|
||||
)
|
||||
if dh.length_squared() < 1e-8:
|
||||
_set_horizontal_velocity_toward(_auth_walk_goal, fallback_goal_xz)
|
||||
else:
|
||||
_set_horizontal_velocity_toward(next_pos, fallback_goal_xz)
|
||||
|
||||
|
||||
static func capsule_feet_y(body_origin_y: float, capsule_half_height: float) -> float:
|
||||
|
|
@ -794,12 +800,11 @@ func _physics_process(delta: float) -> void:
|
|||
)
|
||||
_apply_floor_block_for_descend_at_wall(feet_y)
|
||||
|
||||
# Horizontal motion toward the **authoritative** click target (XZ), not toward
|
||||
# `NavigationAgent3D.get_next_path_position()`. Waypoint steering regressed badly under Jolt /
|
||||
# 120 Hz (next point barely advancing, path finished edge cases, map iteration timing), which
|
||||
# reads as “stuck” or a few centimeters per click. Use the baked path only when we are close in
|
||||
# XZ but the goal surface is clearly above or below the feet — same column as a terrace /
|
||||
# step / drop — so the mesh can steer us toward a ramp or tread instead of hugging a wall.
|
||||
# Horizontal motion toward the **authoritative** click target (XZ) by default. A **global**
|
||||
# `get_next_path_position()` steer regressed under Jolt / 120 Hz (“stuck” / slow advance).
|
||||
# Use the baked path only when vertical routing is latched **and** column hysteresis says so;
|
||||
# then **`_set_horizontal_velocity_from_nav_path_or_goal`** uses **`get_next_path_position()`**
|
||||
# (not a 5 cm path scan — that ping-ponged waypoints every tick).
|
||||
var vert_sep: float = absf(_auth_walk_goal.y - feet_y)
|
||||
if not _walk_vert_route_latched:
|
||||
if vert_sep > WALK_VERT_ROUTE_LATCH_ON_SEP:
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ No new automated GDScript tests are added for this story — there is no new GDS
|
|||
- **Terrace B descend / climb (resolved, iterated):** Scene lifts and **descend-bypass** bee-lines caused regressions (stuck climbs, lip oscillation, wrong-direction slides). **Removed the descend bypass:** vertical moves (e.g. platform ↔ floor) use the **baked nav path + step assist** only. **Walk gravity** is **`!is_on_floor()`** again — never “goal below feet” while still walking on an upper surface (that applied gravity across the whole **TerracePlatformB** deck and destroyed horizontal motion). **`velocity.y`** is cleared by the horizontal steer each tick as before.
|
||||
- **Lip stuck (floor→platform / platform→floor):** QA screenshots for this were on **`TerracePlatformA`** (10 × 10 large SE pad, **0.3 m** single step — not the NW **Terrace B** step/platform pair). Step assist required **`vel_h.dot(want) ≤ 0.52`**, so a **head-on** push into the vertical face (dot ≈ 1) **never** triggered the lift. **Removed** that gate. For **platform→floor**, **`floor_block_on_wall = false`** only when the nav goal is **below** the feet **and** **`is_on_wall()`** or a **wall-ish slide normal** exists, so open deck walking stays stable.
|
||||
- **Teal pad still stuck (follow-up):** Step assist used **slide normals only**; Jolt often exposes the lip via **`is_on_wall()`** / **`get_wall_normal()`** instead — re-added a **directional** wall check (`wnh.dot(want) < -0.02`). Descend relax now also triggers on **shallow floor normal** (`get_floor_normal().dot(UP) < 0.992`) and slightly wider vertical-ish slide band. **`TerracePlatformA`** root **Y = 0.012 m** breaks **coplanarity** with the main floor without the larger gap that hurt **TerraceStepB** approach.
|
||||
- **Nav waypoint steering dropped (movement):** Following **`get_next_path_position()`** by default caused **near-zero** horizontal progress under **Jolt** + **120 Hz** (waypoint advance / finished edge cases), read as universal **stuck** movement even with the server up. **`player.gd`** now **bee-lines in XZ** toward **`_auth_walk_goal`** and uses **`_set_horizontal_velocity_from_nav_path_or_goal`** only when **`needs_vertical_routing`** (latched via **`WALK_VERT_ROUTE_LATCH_*`** on **`abs(goal.y - feet_y)`**, not the raw **`DESCEND_GOAL_Y_MARGIN`** alone — tread bounce was flipping routing every tick) **and** **`horiz_dist <= NAV_COLUMN_STEER_*`** hysteresis.
|
||||
- **Nav waypoint steering dropped (movement):** Following **`get_next_path_position()`** by default caused **near-zero** horizontal progress under **Jolt** + **120 Hz** (waypoint advance / finished edge cases), read as universal **stuck** movement even with the server up. **`player.gd`** now **bee-lines in XZ** toward **`_auth_walk_goal`** and uses path-based steer only when **`needs_vertical_routing`** (Schmitt **`WALK_VERT_ROUTE_LATCH_*`**) **and** **`NAV_COLUMN_STEER_*`** hysteresis. **`_set_horizontal_velocity_from_nav_path_or_goal`** uses **`get_next_path_position()`**; a prior **5 cm** path-point scan caused **180° velocity flips** when Jolt nudged the capsule across the threshold each tick.
|
||||
- **Step assist vs `agent_max_climb`:** Nav **`agent_max_climb = 0.35`** only constrained mesh links; **bee-line + chained step assist** could still climb **TerracePlatformB** (~**0.6 m** from floor) in one click. **`_try_walk_step_assist`** now returns false when **`_auth_walk_goal.y > feet + WALK_STEP_ASSIST_MAX_SURFACE_DELTA`** (**0.35**, same as the mesh).
|
||||
- **Teal cardinal / tread shake (rollback):** Aggressive tweaks (**0.16** lift, **1**-tick cooldown, **`descending_stall`** toggling **`floor_block_on_wall`**, **seam-based** step-assist clear, **walk-stall nav replan**) caused **oscillation** on the small approach treads. **Reverted** to **`WALK_STEP_ASSIST_DELTA = 0.11`**, **`WALK_STEP_ASSIST_COOLDOWN_TICKS = 8`**, **no** stall replan, **no** `descending_stall` branch, **no** seam-based assist clear — only the original clear (clean floor without wall, or airborne with no slides). **Climb** wallish snap cap applies only when **`goal.y > feet_y + 0.12`** so shallow tread-to-tread motion is not capped to assist snap every frame.
|
||||
- **Teal pad — geometry wins:** Script-only mitigations still failed in practice; **`TerracePlatformA_Approach`** adds **twelve** `walkable` **`StaticBody3D`** treads (three per cardinal) with **~0.104 m** rise each, **darker teal** albedo, flush to the platform lip — nav + `CharacterBody3D` use **sloped contact** instead of fighting a **single vertical face**. **Follow-up:** tread **run** was **0.45 m** along the climb axis while the capsule **diameter is 0.8 m** — the body was wider than each tread, causing straddle / stuck motion when **entering** and blocking horizontal progress toward the lip when **exiting**. Treads widened to **1.0 m** run with positions re-centered. **`NavigationAgent3D`** `path_desired_distance` / `target_desired_distance` were briefly lowered to **0.22** but that caused **floor** movement to **stall**; **reverted to 0.35**.
|
||||
|
|
|
|||
Loading…
Reference in New Issue