NEON-29: Ease flat-floor idle latch (HUD XZ drift)
Raise stable-idle floor up-dot entry to 0.998 so Jolt normals on a flat box can latch the idle anchor; 0.999 rarely tripped and corrective move_and_slide + rim/bump nudges kept moving XZ. Add STABLE_IDLE_ENTER_STREAK_FRAMES (2) before first latch; arrival path still latches immediately. Unlatch budget 8→10 ticks.pull/41/head
parent
591db65b4a
commit
865eed2e53
|
|
@ -33,14 +33,18 @@ const FLOOR_SNAP_IDLE: float = 0.11
|
|||
## Below this floor-normal dot up, idle tick uses **moving** `floor_max_angle` (rim).
|
||||
const IDLE_RIM_MIN_FLOOR_UP_DOT: float = 0.968
|
||||
## Stable flat idle support: skip corrective idle motion when support is effectively level.
|
||||
## Below 1.0 so Jolt seam normals do not flicker across the threshold (idle XZ noise).
|
||||
const STABLE_IDLE_FLOOR_MIN_UP_DOT: float = 0.999
|
||||
## 0.998 (~3.6°): 0.999 still saw open-floor HUD drift — Jolt [code]get_floor_normal()[/code] often
|
||||
## reports ~0.9991–0.9992 on a flat box, so the old 0.999 gate rarely latched the idle anchor and
|
||||
## [code]move_and_slide[/code] + rim/bump nudges kept nudging XZ.
|
||||
const STABLE_IDLE_FLOOR_MIN_UP_DOT: float = 0.998
|
||||
## While [member _idle_stable_latched], allow slightly tilted reported normals (tread/riser edge).
|
||||
const STABLE_IDLE_FLOOR_HOLD_MIN_UP_DOT: float = 0.992
|
||||
## Rare edge contacts can keep corrective nudges alive forever; budget them, then hold x/z.
|
||||
const IDLE_MANUAL_CORRECTION_MAX_TICKS: int = 8
|
||||
## Consecutive [code]raw_stable[/code] ticks required before first idle anchor latch (entry debounce).
|
||||
const STABLE_IDLE_ENTER_STREAK_FRAMES: int = 2
|
||||
## Consecutive unstable-idle physics ticks before leaving latched stable idle (thin tread lip).
|
||||
const STABLE_IDLE_UNLATCH_TICKS: int = 8
|
||||
const STABLE_IDLE_UNLATCH_TICKS: int = 10
|
||||
## Horizontal nudge per tick for rim / straddle settle (**`_maybe_idle_rim_settle_nudge`**).
|
||||
const IDLE_RIM_SETTLE_STEP: float = 0.004
|
||||
## Used by tests and vertical routing checks (feet vs goal surface).
|
||||
|
|
@ -90,6 +94,7 @@ var _idle_anchor_y: float = 0.0
|
|||
## Latched stable idle: stay on zero-vel + anchor through brief Jolt false negatives on treads.
|
||||
var _idle_stable_latched: bool = false
|
||||
var _idle_stable_unlatch_streak: int = 0
|
||||
var _idle_stable_enter_streak: int = 0
|
||||
var _idle_manual_correction_ticks: int = 0
|
||||
var _debug_last_idle_xz: Vector2 = Vector2.INF
|
||||
var _debug_idle_heartbeat: int = 0
|
||||
|
|
@ -120,6 +125,7 @@ func set_authoritative_nav_goal(world_pos: Vector3) -> void:
|
|||
_idle_manual_correction_ticks = 0
|
||||
_idle_stable_latched = false
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_enter_streak = 0
|
||||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_nav_agent.set_target_position(world_pos)
|
||||
|
|
@ -134,6 +140,7 @@ func clear_nav_goal() -> void:
|
|||
_idle_manual_correction_ticks = 0
|
||||
_idle_stable_latched = false
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_enter_streak = 0
|
||||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_nav_agent.set_target_position(global_position)
|
||||
|
|
@ -174,6 +181,7 @@ func snap_to_server(world_pos: Vector3) -> void:
|
|||
_idle_manual_correction_ticks = 0
|
||||
_idle_stable_latched = false
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_enter_streak = 0
|
||||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_nav_agent.set_target_position(settled)
|
||||
|
|
@ -678,13 +686,21 @@ func _physics_process(delta: float) -> void:
|
|||
var raw_stable: bool = _stable_idle_support()
|
||||
if raw_stable:
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_latched = true
|
||||
if not _idle_stable_latched:
|
||||
_idle_stable_enter_streak += 1
|
||||
if _idle_stable_enter_streak >= STABLE_IDLE_ENTER_STREAK_FRAMES:
|
||||
_idle_stable_latched = true
|
||||
else:
|
||||
_idle_stable_enter_streak = 0
|
||||
elif _idle_stable_latched:
|
||||
_idle_stable_unlatch_streak += 1
|
||||
if _idle_stable_unlatch_streak >= STABLE_IDLE_UNLATCH_TICKS:
|
||||
_idle_stable_latched = false
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_enter_streak = 0
|
||||
_idle_anchor_active = false
|
||||
else:
|
||||
_idle_stable_enter_streak = 0
|
||||
if _idle_stable_latched:
|
||||
_idle_manual_correction_ticks = 0
|
||||
velocity = Vector3.ZERO
|
||||
|
|
@ -748,6 +764,7 @@ func _physics_process(delta: float) -> void:
|
|||
_idle_manual_correction_ticks = 0
|
||||
_idle_stable_latched = false
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_enter_streak = 0
|
||||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_nav_agent.set_target_position(global_position)
|
||||
|
|
@ -755,6 +772,7 @@ func _physics_process(delta: float) -> void:
|
|||
if _stable_idle_support():
|
||||
_idle_stable_latched = true
|
||||
_idle_stable_unlatch_streak = 0
|
||||
_idle_stable_enter_streak = 0
|
||||
_idle_manual_correction_ticks = 0
|
||||
velocity = Vector3.ZERO
|
||||
floor_snap_length = FLOOR_SNAP_IDLE
|
||||
|
|
|
|||
|
|
@ -70,9 +70,9 @@ func test_idle_support_is_stable_on_flat_floor_without_wall_contacts() -> void:
|
|||
|
||||
func test_idle_support_is_stable_false_when_floor_is_not_flat_enough() -> void:
|
||||
var slide_normals: Array[Vector3] = [Vector3.UP]
|
||||
# Must have floor_normal.dot(UP) < STABLE_IDLE_FLOOR_MIN_UP_DOT (0.999).
|
||||
# (0.02, 0.995).normalized() dots ~0.9998 and incorrectly passes as "flat" at 0.999.
|
||||
var tilted_floor: Vector3 = Vector3(0.05, 0.99875, 0.0).normalized()
|
||||
# Must have floor_normal.dot(UP) < STABLE_IDLE_FLOOR_MIN_UP_DOT (0.998).
|
||||
# (0.05, 0.99875).normalized() dots ~0.999 and passes 0.998; use a slightly steeper tilt.
|
||||
var tilted_floor: Vector3 = Vector3(0.07, 0.9975, 0.0).normalized()
|
||||
var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(true, tilted_floor, slide_normals, 0)
|
||||
assert_that(stable).is_false()
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ No new automated GDScript tests are added for this story — there is no new GDS
|
|||
## Approach treads (district convention)
|
||||
|
||||
- **Shipped in scene:** **`TerracePlatformC_Approach`** (`PCS_*`, teal-style **3×4** cardinals, **7.2 m** NS / **WE** span), **`TerraceStepB_Approach`** (`TSB_*`, **S/E/W** only — north meets violet), **`TerracePlatformB_Approach`** (`TPB_*`, **N/E/W**, **six** ~**0.104 m** risers for **0.6 m** deck height; south remains **gold step**). **Smooth ramps** for other props can follow the same rise/run idea without new `player.gd` logic.
|
||||
- **Idle jitter on thin treads / flat stops:** (1) **`idle_support_is_stable`**: post-stop **`loose_ticks` + ridged** blocks stable idle only when **`floor_normal.dot(UP) < IDLE_RIM_MIN_FLOOR_UP_DOT`** — flat tread + vertical riser (e.g. **TerraceStepB_Approach** corner) no longer spends ~0.8 s in corrective idle. (2) **`use_loose_floor_angle`** not tied to **`loose_ticks` alone**. (3) **`STABLE_IDLE_FLOOR_MIN_UP_DOT` 0.999**; ridged **wallish ny < 0.35**; **stable latch + Y anchor**; **`STABLE_IDLE_UNLATCH_TICKS` 5**. (4) **Arrival** stable shortcut; **nav column steer** hysteresis. (5) **`needs_vertical_routing` Schmitt latch** (`WALK_VERT_ROUTE_LATCH_ON_SEP` / `WALK_VERT_ROUTE_LATCH_OFF_SEP`): **`feet_y`** wobble on treads was toggling path vs direct steer every tick (velocity flip while **`has_goal`**); **`debug_idle_trace`** appends **`feet_y`**, **`vert_sep`**, **`vlat`**, **`ncol`** when walking.
|
||||
- **Idle jitter on thin treads / flat stops:** (1) **`idle_support_is_stable`**: post-stop **`loose_ticks` + ridged** blocks stable idle only when **`floor_normal.dot(UP) < IDLE_RIM_MIN_FLOOR_UP_DOT`** — flat tread + vertical riser (e.g. **TerraceStepB_Approach** corner) no longer spends ~0.8 s in corrective idle. (2) **`use_loose_floor_angle`** not tied to **`loose_ticks` alone**. (3) **`STABLE_IDLE_FLOOR_MIN_UP_DOT` 0.998** (was 0.999 — Jolt flat-floor normals often ~0.9991+ so the anchor rarely latched and **`move_and_slide`** + nudges drifted HUD XZ); **`STABLE_IDLE_ENTER_STREAK_FRAMES` 2**; **`STABLE_IDLE_UNLATCH_TICKS` 10**; ridged **wallish ny < 0.35**; **stable latch + Y anchor**. (4) **Arrival** stable shortcut (immediate latch, no enter streak). (5) **Nav column steer** hysteresis. (6) **`needs_vertical_routing` Schmitt latch**; **`debug_idle_trace`** walking extras.
|
||||
- **Not in this pass:** **`PrototypeTerminal`**, **MoveReject** props, **runtime random bumps** — add geometry when those need climbable access.
|
||||
|
||||
## Open questions / risks
|
||||
|
|
|
|||
Loading…
Reference in New Issue