NEON-29: Stable idle latch + Y anchor for approach treads
Hold latched zero-vel idle through STABLE_IDLE_UNLATCH_TICKS consecutive unstable readings so thin tread lips do not drop into corrective move_and_slide every other frame. Idle anchor now freezes Y as well as XZ. Reset latch on goal/snap/arrival; seed latch on arrival when support is stable after idle tick. Update hold_idle_anchor GdUnit expectation.pull/41/head
parent
ff6c337e1d
commit
8d9ad0ecd3
|
|
@ -32,6 +32,8 @@ const IDLE_RIM_MIN_FLOOR_UP_DOT: float = 0.968
|
||||||
const STABLE_IDLE_FLOOR_MIN_UP_DOT: float = 0.999
|
const STABLE_IDLE_FLOOR_MIN_UP_DOT: float = 0.999
|
||||||
## Rare edge contacts can keep corrective nudges alive forever; budget them, then hold x/z.
|
## Rare edge contacts can keep corrective nudges alive forever; budget them, then hold x/z.
|
||||||
const IDLE_MANUAL_CORRECTION_MAX_TICKS: int = 8
|
const IDLE_MANUAL_CORRECTION_MAX_TICKS: int = 8
|
||||||
|
## Consecutive unstable-idle physics ticks before leaving latched stable idle (thin tread lip).
|
||||||
|
const STABLE_IDLE_UNLATCH_TICKS: int = 2
|
||||||
## Horizontal nudge per tick for rim / straddle settle (**`_maybe_idle_rim_settle_nudge`**).
|
## Horizontal nudge per tick for rim / straddle settle (**`_maybe_idle_rim_settle_nudge`**).
|
||||||
const IDLE_RIM_SETTLE_STEP: float = 0.004
|
const IDLE_RIM_SETTLE_STEP: float = 0.004
|
||||||
## Used by tests and vertical routing checks (feet vs goal surface).
|
## Used by tests and vertical routing checks (feet vs goal surface).
|
||||||
|
|
@ -77,6 +79,10 @@ var _step_assist_cooldown: int = 0
|
||||||
var _step_assist_active: bool = false
|
var _step_assist_active: bool = false
|
||||||
var _idle_anchor_active: bool = false
|
var _idle_anchor_active: bool = false
|
||||||
var _idle_anchor_xz: Vector2 = Vector2.ZERO
|
var _idle_anchor_xz: Vector2 = Vector2.ZERO
|
||||||
|
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_manual_correction_ticks: int = 0
|
var _idle_manual_correction_ticks: int = 0
|
||||||
var _debug_last_idle_xz: Vector2 = Vector2.INF
|
var _debug_last_idle_xz: Vector2 = Vector2.INF
|
||||||
var _debug_idle_heartbeat: int = 0
|
var _debug_idle_heartbeat: int = 0
|
||||||
|
|
@ -103,6 +109,8 @@ func set_authoritative_nav_goal(world_pos: Vector3) -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_idle_stable_latched = false
|
||||||
|
_idle_stable_unlatch_streak = 0
|
||||||
_walk_nav_column_steering = false
|
_walk_nav_column_steering = false
|
||||||
_nav_agent.set_target_position(world_pos)
|
_nav_agent.set_target_position(world_pos)
|
||||||
|
|
||||||
|
|
@ -114,6 +122,8 @@ func clear_nav_goal() -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_idle_stable_latched = false
|
||||||
|
_idle_stable_unlatch_streak = 0
|
||||||
_walk_nav_column_steering = false
|
_walk_nav_column_steering = false
|
||||||
_nav_agent.set_target_position(global_position)
|
_nav_agent.set_target_position(global_position)
|
||||||
|
|
||||||
|
|
@ -151,6 +161,8 @@ func snap_to_server(world_pos: Vector3) -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_idle_stable_latched = false
|
||||||
|
_idle_stable_unlatch_streak = 0
|
||||||
_walk_nav_column_steering = false
|
_walk_nav_column_steering = false
|
||||||
_nav_agent.set_target_position(settled)
|
_nav_agent.set_target_position(settled)
|
||||||
reset_physics_interpolation()
|
reset_physics_interpolation()
|
||||||
|
|
@ -416,9 +428,10 @@ func _hold_idle_anchor() -> void:
|
||||||
var pos := global_position
|
var pos := global_position
|
||||||
if not _idle_anchor_active:
|
if not _idle_anchor_active:
|
||||||
_idle_anchor_xz = Vector2(pos.x, pos.z)
|
_idle_anchor_xz = Vector2(pos.x, pos.z)
|
||||||
|
_idle_anchor_y = pos.y
|
||||||
_idle_anchor_active = true
|
_idle_anchor_active = true
|
||||||
return
|
return
|
||||||
global_position = Vector3(_idle_anchor_xz.x, pos.y, _idle_anchor_xz.y)
|
global_position = Vector3(_idle_anchor_xz.x, _idle_anchor_y, _idle_anchor_xz.y)
|
||||||
|
|
||||||
|
|
||||||
func _debug_trace_idle_state(tag: String) -> void:
|
func _debug_trace_idle_state(tag: String) -> void:
|
||||||
|
|
@ -624,7 +637,17 @@ func _physics_process(delta: float) -> void:
|
||||||
)
|
)
|
||||||
if not _has_walk_goal:
|
if not _has_walk_goal:
|
||||||
floor_block_on_wall = true
|
floor_block_on_wall = true
|
||||||
if _stable_idle_support():
|
var raw_stable: bool = _stable_idle_support()
|
||||||
|
if raw_stable:
|
||||||
|
_idle_stable_unlatch_streak = 0
|
||||||
|
_idle_stable_latched = true
|
||||||
|
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_anchor_active = false
|
||||||
|
if _idle_stable_latched:
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
floor_snap_length = FLOOR_SNAP_IDLE
|
floor_snap_length = FLOOR_SNAP_IDLE
|
||||||
|
|
@ -685,10 +708,14 @@ func _physics_process(delta: float) -> void:
|
||||||
_step_assist_active = false
|
_step_assist_active = false
|
||||||
_idle_anchor_active = false
|
_idle_anchor_active = false
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
|
_idle_stable_latched = false
|
||||||
|
_idle_stable_unlatch_streak = 0
|
||||||
_walk_nav_column_steering = false
|
_walk_nav_column_steering = false
|
||||||
_nav_agent.set_target_position(global_position)
|
_nav_agent.set_target_position(global_position)
|
||||||
_physics_idle_tick(delta)
|
_physics_idle_tick(delta)
|
||||||
if _stable_idle_support():
|
if _stable_idle_support():
|
||||||
|
_idle_stable_latched = true
|
||||||
|
_idle_stable_unlatch_streak = 0
|
||||||
_idle_manual_correction_ticks = 0
|
_idle_manual_correction_ticks = 0
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
floor_snap_length = FLOOR_SNAP_IDLE
|
floor_snap_length = FLOOR_SNAP_IDLE
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ func test_hold_idle_anchor_sets_anchor_first_then_clamps_xz() -> void:
|
||||||
|
|
||||||
p.global_position = Vector3(1.2, 0.7, 2.3)
|
p.global_position = Vector3(1.2, 0.7, 2.3)
|
||||||
p.call("_hold_idle_anchor")
|
p.call("_hold_idle_anchor")
|
||||||
assert_that(p.global_position).is_equal(Vector3(1.0, 0.7, 2.0))
|
assert_that(p.global_position).is_equal(Vector3(1.0, 0.5, 2.0))
|
||||||
|
|
||||||
|
|
||||||
func test_snap_to_server_clears_idle_anchor() -> void:
|
func test_snap_to_server_clears_idle_anchor() -> void:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue