NEON-29: Debounce floor probe for ledge peel and zero snap.
Continuation rays reject stair treads (~0.104 m) under WALK_CONTINUATION_MAX_BELOW_FEET (0.078 m), so is_on_floor + probe flickered every tick and toggled peel vs full snap (Y jitter ~1.0 m). Arm peel/snap aggression only after WALK_PEEL_PROBE_FALSE_FRAMES consecutive probe failures; deep descent still arms immediately. Widen horiz peel suspend (0.34→0.46 m vert, 2→5 m XZ) for goal/tread separation gaps.pull/41/head
parent
4e47e9a037
commit
73d26a90d7
|
|
@ -34,10 +34,15 @@ const NAV_PATH_STEER_MIN_GOAL_DOT: float = -0.25
|
|||
const WALK_PEEL_SUSPEND_VERT_SEP: float = 0.48
|
||||
## Fallback when [code]vlat[/code] has unlatched (feet and goal both near the same slab Y) but
|
||||
## [code]ncol[/code] is false because [code]horiz_dist[/code] is past column hysteresis — peel + zero
|
||||
## snap still oscillates on violet treads (NEON-16 ~y=0.92). Tighter than [member WALK_PEEL_SUSPEND_VERT_SEP]
|
||||
## so terrace lips with ~0.45–0.55 m drop keep peel until XZ is tighter.
|
||||
const WALK_PEEL_SUSPEND_HORIZ_VERT_SEP: float = 0.34
|
||||
const WALK_PEEL_SUSPEND_HORIZ_DIST: float = 2.0
|
||||
## snap still oscillates on violet treads (NEON-16 ~y=0.92). Slightly below [member WALK_PEEL_SUSPEND_VERT_SEP]
|
||||
## so shallow lips (~0.48 m) still peel; widened from 0.34 to cover ~0.35–0.47 m stair/goal gaps.
|
||||
const WALK_PEEL_SUSPEND_HORIZ_VERT_SEP: float = 0.46
|
||||
const WALK_PEEL_SUSPEND_HORIZ_DIST: float = 5.0
|
||||
## [method _walk_has_close_floor_probe_below] often flips on stairs: next tread is ~0.104 m down but
|
||||
## [member WALK_CONTINUATION_MAX_BELOW_FEET] is 0.078 m, so rays reject the hit → peel + zero snap
|
||||
## alternate with full snap each tick (~y≈1.0 jitter). Require this many consecutive probe-fail frames
|
||||
## before arming peel/snap aggression; [member WALK_DEEP_DESCENT_FEET_ABOVE_GOAL] still arms immediately.
|
||||
const WALK_PEEL_PROBE_FALSE_FRAMES: int = 2
|
||||
## Hysteresis for [code]needs_vertical_routing[/code]: [code]feet_y[/code] wobbles with
|
||||
## [code]move_and_slide[/code] on treads (~7 cm), so a single margin (see [member DESCEND_GOAL_Y_MARGIN])
|
||||
## can flip path vs direct steering every tick → velocity sign ping-pong while [code]has_goal[/code].
|
||||
|
|
@ -146,6 +151,10 @@ var _walk_vert_route_latched: bool = false
|
|||
## GROUNDED [method move_and_slide] clears downward [code]velocity.y[/code] on floor; accumulate peel
|
||||
## speed here and apply [code]global_position.y[/code] so terrace lips can actually drop.
|
||||
var _walk_ledge_peel_vy: float = 0.0
|
||||
## Pre-[method move_and_slide] debounce for probe flicker (see [member WALK_PEEL_PROBE_FALSE_FRAMES]).
|
||||
var _walk_peel_no_probe_streak: int = 0
|
||||
## True after streak / deep-descent check; drives zero snap and [method _apply_walk_post_slide_ledge_peel].
|
||||
var _walk_debounced_wants_ledge_peel: bool = false
|
||||
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
|
||||
|
||||
|
||||
|
|
@ -171,6 +180,8 @@ func set_authoritative_nav_goal(world_pos: Vector3) -> void:
|
|||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
_walk_peel_no_probe_streak = 0
|
||||
_walk_debounced_wants_ledge_peel = false
|
||||
_nav_agent.set_target_position(world_pos)
|
||||
|
||||
|
||||
|
|
@ -187,6 +198,8 @@ func clear_nav_goal() -> void:
|
|||
_idle_stable_enter_streak = 0
|
||||
_walk_nav_column_steering = false
|
||||
_walk_vert_route_latched = false
|
||||
_walk_peel_no_probe_streak = 0
|
||||
_walk_debounced_wants_ledge_peel = false
|
||||
_nav_agent.set_target_position(global_position)
|
||||
|
||||
|
||||
|
|
@ -218,6 +231,8 @@ func snap_to_server(world_pos: Vector3) -> void:
|
|||
velocity = Vector3.ZERO
|
||||
_has_walk_goal = false
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
_walk_peel_no_probe_streak = 0
|
||||
_walk_debounced_wants_ledge_peel = false
|
||||
# Force corrective idle ticks so Jolt resolves any residual physics state before walking.
|
||||
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
||||
_step_assist_cooldown = 0
|
||||
|
|
@ -481,11 +496,25 @@ func _walk_peel_suspend_near_goal(feet_y: float, horiz_dist: float) -> bool:
|
|||
return vsep <= WALK_PEEL_SUSPEND_HORIZ_VERT_SEP and horiz_dist <= WALK_PEEL_SUSPEND_HORIZ_DIST
|
||||
|
||||
|
||||
func _walk_refresh_ledge_peel_debounce(feet_y: float, move_dir_xz: Vector2) -> void:
|
||||
var probe_ok: bool = _walk_has_close_floor_probe_below(move_dir_xz)
|
||||
var deep: bool = feet_y > _auth_walk_goal.y + WALK_DEEP_DESCENT_FEET_ABOVE_GOAL
|
||||
if deep:
|
||||
_walk_peel_no_probe_streak = WALK_PEEL_PROBE_FALSE_FRAMES
|
||||
elif not probe_ok:
|
||||
_walk_peel_no_probe_streak += 1
|
||||
else:
|
||||
_walk_peel_no_probe_streak = 0
|
||||
_walk_debounced_wants_ledge_peel = (
|
||||
deep or _walk_peel_no_probe_streak >= WALK_PEEL_PROBE_FALSE_FRAMES
|
||||
)
|
||||
|
||||
|
||||
## After [method move_and_slide], peel the capsule down when the authoritative goal is below the feet
|
||||
## but the engine still reports floor contact (GROUNDED mode ate negative [code]velocity.y[/code]).
|
||||
## Uses a small accumulated peel speed so displacement matches [code]∫ g dt[/code], not [code]g Δt[/code] (wrong units).
|
||||
func _apply_walk_post_slide_ledge_peel(
|
||||
delta: float, feet_y: float, move_dir_xz: Vector2, horiz_dist: float
|
||||
delta: float, feet_y: float, _move_dir_xz: Vector2, horiz_dist: float
|
||||
) -> void:
|
||||
if not _has_walk_goal:
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
|
|
@ -505,13 +534,10 @@ func _apply_walk_post_slide_ledge_peel(
|
|||
if _walk_peel_suspend_near_goal(feet_y, horiz_dist):
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
return
|
||||
var peel: bool = (
|
||||
feet_y > _auth_walk_goal.y + WALK_DEEP_DESCENT_FEET_ABOVE_GOAL
|
||||
or not _walk_has_close_floor_probe_below(move_dir_xz)
|
||||
)
|
||||
if not peel:
|
||||
if not _walk_debounced_wants_ledge_peel:
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
return
|
||||
# Debounce was computed pre-slide; do not re-probe here (post-slide rays re-flicker on treads).
|
||||
_walk_ledge_peel_vy += _player_vertical_accel(delta).y * delta
|
||||
var dy: float = _walk_ledge_peel_vy * delta
|
||||
if absf(dy) > 1e-9:
|
||||
|
|
@ -522,7 +548,7 @@ func _apply_walk_post_slide_ledge_peel(
|
|||
func _walk_floor_snap_length(
|
||||
feet_y: float,
|
||||
want_goal_h: Vector3,
|
||||
move_dir_xz: Vector2 = Vector2.ZERO,
|
||||
_move_dir_xz: Vector2 = Vector2.ZERO,
|
||||
horiz_dist: float = INF,
|
||||
) -> float:
|
||||
if _step_assist_active:
|
||||
|
|
@ -532,10 +558,7 @@ func _walk_floor_snap_length(
|
|||
if (
|
||||
is_on_floor()
|
||||
and not _walk_peel_suspend_near_goal(feet_y, horiz_dist)
|
||||
and (
|
||||
not _walk_has_close_floor_probe_below(move_dir_xz)
|
||||
or feet_y > _auth_walk_goal.y + WALK_DEEP_DESCENT_FEET_ABOVE_GOAL
|
||||
)
|
||||
and _walk_debounced_wants_ledge_peel
|
||||
):
|
||||
return 0.0
|
||||
if want_goal_h.length_squared() > 1e-10:
|
||||
|
|
@ -1006,6 +1029,8 @@ func _physics_process(delta: float) -> void:
|
|||
velocity = Vector3.ZERO
|
||||
_has_walk_goal = false
|
||||
_walk_ledge_peel_vy = 0.0
|
||||
_walk_peel_no_probe_streak = 0
|
||||
_walk_debounced_wants_ledge_peel = false
|
||||
floor_block_on_wall = true
|
||||
_floor_angle_loose_ticks = FLOOR_ANGLE_LOOSE_TICKS_AFTER_STOP
|
||||
_step_assist_active = false
|
||||
|
|
@ -1067,6 +1092,7 @@ func _physics_process(delta: float) -> void:
|
|||
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||
|
||||
var walk_move_dir_xz: Vector2 = _resolve_walk_probe_dir_xz(feet_y, want_goal_h)
|
||||
_walk_refresh_ledge_peel_debounce(feet_y, walk_move_dir_xz)
|
||||
|
||||
_apply_walk_air_gravity(delta, feet_y, walk_move_dir_xz)
|
||||
floor_snap_length = _walk_floor_snap_length(
|
||||
|
|
|
|||
Loading…
Reference in New Issue