NEON-29: Hysteresis for nav column steering (walk ping-pong)

Single-radius switch at DIRECT_APPROACH_RADIUS let horiz_dist cross every
frame between direct-to-goal and nav-path steering, flipping velocity ~180°.
Schmitt band NAV_COLUMN_STEER_ENTER/EXIT with latched _walk_nav_column_steering;
reset on new goal, snap, arrival, clear.
pull/41/head
VinPropane 2026-04-12 17:02:17 -04:00
parent 0f4c23b815
commit ff6c337e1d
1 changed files with 19 additions and 1 deletions

View File

@ -16,6 +16,12 @@ const FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP: int = 96
const ARRIVE_EPS: float = 0.35
const VERT_ARRIVE_EPS: float = 0.055
const DIRECT_APPROACH_RADIUS: float = 0.85
## Hysteresis for nav-path column steering (see `_physics_process`). At exactly
## [member DIRECT_APPROACH_RADIUS] the old test flipped every frame: path nudges
## tangentially → [code]horiz_dist[/code] crosses the radius → direct vs path
## alternates → velocity sign flips (Jolt ping-pong while [code]has_goal[/code]).
const NAV_COLUMN_STEER_ENTER_DIST: float = 0.74
const NAV_COLUMN_STEER_EXIT_DIST: float = 0.97
const FLOOR_SNAP_MOVING: float = 0.32
## Idle floor snap length (stronger to pin rim contacts).
const FLOOR_SNAP_IDLE: float = 0.11
@ -76,6 +82,8 @@ var _debug_last_idle_xz: Vector2 = Vector2.INF
var _debug_idle_heartbeat: int = 0
var _debug_last_transform_xz: Vector2 = Vector2.INF
var _debug_trace_frame: int = 0
## True while we use [method _set_horizontal_velocity_from_nav_path_or_goal] for vertical routing.
var _walk_nav_column_steering: bool = false
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
@ -95,6 +103,7 @@ func set_authoritative_nav_goal(world_pos: Vector3) -> void:
_step_assist_active = false
_idle_anchor_active = false
_idle_manual_correction_ticks = 0
_walk_nav_column_steering = false
_nav_agent.set_target_position(world_pos)
@ -105,6 +114,7 @@ func clear_nav_goal() -> void:
_step_assist_active = false
_idle_anchor_active = false
_idle_manual_correction_ticks = 0
_walk_nav_column_steering = false
_nav_agent.set_target_position(global_position)
@ -141,6 +151,7 @@ func snap_to_server(world_pos: Vector3) -> void:
_step_assist_active = false
_idle_anchor_active = false
_idle_manual_correction_ticks = 0
_walk_nav_column_steering = false
_nav_agent.set_target_position(settled)
reset_physics_interpolation()
@ -674,6 +685,7 @@ func _physics_process(delta: float) -> void:
_step_assist_active = false
_idle_anchor_active = false
_idle_manual_correction_ticks = 0
_walk_nav_column_steering = false
_nav_agent.set_target_position(global_position)
_physics_idle_tick(delta)
if _stable_idle_support():
@ -708,7 +720,13 @@ func _physics_process(delta: float) -> void:
_auth_walk_goal.y > feet_y + DESCEND_GOAL_Y_MARGIN
or _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN
)
if needs_vertical_routing and horiz_dist <= DIRECT_APPROACH_RADIUS:
if not needs_vertical_routing:
_walk_nav_column_steering = false
elif horiz_dist <= NAV_COLUMN_STEER_ENTER_DIST:
_walk_nav_column_steering = true
elif horiz_dist >= NAV_COLUMN_STEER_EXIT_DIST:
_walk_nav_column_steering = false
if needs_vertical_routing and _walk_nav_column_steering:
_set_horizontal_velocity_from_nav_path_or_goal(want_goal_h)
else:
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)