NS-23: Arrival footing uses footprint max_y (avoid false stop on bump lip)

Premature arrival used _floor_y_under_body() vs dest footing; at the rim that
can read floor while xz is already within ARRIVE_EPS, clearing the walk goal.
Match step-down logic: compare destination footing to footprint max_y and
cache footprint scan once per physics frame.
pull/23/head
VinPropane 2026-04-05 01:54:42 -04:00
parent 003750c7ae
commit 8053f08804
1 changed files with 17 additions and 4 deletions

View File

@ -31,6 +31,8 @@ const NEXT_WAYPOINT_MIN_HORIZ_SQ: float = 0.01
var _has_walk_goal: bool = false
var _auth_walk_goal: Vector3 = Vector3.ZERO
var _footprint_cache_frame: int = -1
var _footprint_cache: Dictionary = {}
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
@ -94,7 +96,7 @@ func _floor_y_under_body() -> float:
## One ray pass: max floor Y under footprint + whether any hit is an NS-19 bump (`ns19_bump` group).
func _footprint_step_down_state() -> Dictionary:
func _compute_footprint_step_down_state() -> Dictionary:
var p: Vector3 = global_position
var h: float = STEP_FOOTPRINT_HALF
var oxz: Array[Vector3] = [
@ -140,6 +142,14 @@ func _footprint_step_down_state() -> Dictionary:
return {"max_y": best_y, "touches_ns19_bump": on_bump}
func _footprint_step_down_state() -> Dictionary:
var f: int = Engine.get_physics_frames()
if f != _footprint_cache_frame:
_footprint_cache = _compute_footprint_step_down_state()
_footprint_cache_frame = f
return _footprint_cache
## Footing under goal xz (surface Y). Server goal.y may be capsule center (e.g. 0.9) or pick surface
## (e.g. 0) — do not compare that directly to `_floor_y_under_body()` (always surface).
func _destination_footing_y() -> float:
@ -190,9 +200,12 @@ func _physics_process(_delta: float) -> void:
var full_to_goal: Vector3 = _auth_walk_goal - global_position
var horiz_dist: float = Vector2(full_to_goal.x, full_to_goal.z).length()
var vert_err_capsule: float = absf(full_to_goal.y)
var footing_match: bool = (
absf(_floor_y_under_body() - _destination_footing_y()) <= VERT_ARRIVE_EPS
)
var dest_foot_y: float = _destination_footing_y()
var fp: Dictionary = _footprint_step_down_state()
var max_foot_y: float = float(fp["max_y"])
# Same lip issue as step-off: `_floor_y_under_body()` can read floor early and match `dest_foot_y`
# while xz is within ARRIVE_EPS — false “arrived”, goal clears. Use footprint max_y.
var footing_match: bool = absf(max_foot_y - dest_foot_y) <= VERT_ARRIVE_EPS
if horiz_dist <= ARRIVE_EPS and (vert_err_capsule <= VERT_ARRIVE_EPS or footing_match):
velocity = Vector3.ZERO
_has_walk_goal = false