diff --git a/client/scripts/main.gd b/client/scripts/main.gd index c1faebf..d63b9e7 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -7,7 +7,8 @@ extends Node3D ## (see `isometric_follow_camera.gd`). ## Prototype: two random short bumps (sibling StaticBody3D under NavigationRegion3D; see ## `random_floor_bumps.gd`) before nav bake. -## Prototype HUD: world `CharacterBody3D` position in `UICanvas/PlayerPositionLabel`. +## Prototype HUD: world `CharacterBody3D` position in `UICanvas/PlayerPositionLabel` +## (updated in `_physics_process` so it matches physics ticks). const MOVE_REJECT_MSG_SECONDS: float = 4.0 @@ -35,7 +36,7 @@ var _dev_obstacle_smoke: Node3D func _ready() -> void: - set_process(true) + set_physics_process(true) set_process_unhandled_key_input(true) await get_tree().process_frame _dev_obstacle_smoke = get_node_or_null("World/NavigationRegion3D/Obstacle") as Node3D @@ -57,7 +58,7 @@ func _ready() -> void: _radius_preview.call("setup_player", _player) -func _process(_delta: float) -> void: +func _physics_process(_delta: float) -> void: if not is_instance_valid(_player) or not is_instance_valid(_player_pos_label): return var p: Vector3 = _player.global_position diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 0fcb9a9..af99442 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -30,10 +30,12 @@ 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 +## 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 unstable-idle physics ticks before leaving latched stable idle (thin tread lip). -const STABLE_IDLE_UNLATCH_TICKS: int = 5 +const STABLE_IDLE_UNLATCH_TICKS: int = 8 ## 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). @@ -397,11 +399,15 @@ static func idle_slide_contacts_are_ridged(slide_normals: Array[Vector3]) -> boo static func idle_support_is_stable( - on_floor: bool, floor_normal: Vector3, slide_normals: Array[Vector3], loose_ticks: int + on_floor: bool, + floor_normal: Vector3, + slide_normals: Array[Vector3], + loose_ticks: int, + min_flat_up_dot: float = STABLE_IDLE_FLOOR_MIN_UP_DOT, ) -> bool: if not on_floor: return false - if floor_normal.dot(Vector3.UP) < STABLE_IDLE_FLOOR_MIN_UP_DOT: + if floor_normal.dot(Vector3.UP) < min_flat_up_dot: return false # Post-stop **ridged** slides (tread + riser) used to force unstable idle for the entire # `loose_ticks` window even when the **floor** is level — e.g. idle on a flat approach tread @@ -425,8 +431,13 @@ func _current_slide_normals() -> Array[Vector3]: func _stable_idle_support() -> bool: + var min_dot: float = ( + STABLE_IDLE_FLOOR_HOLD_MIN_UP_DOT + if _idle_stable_latched + else STABLE_IDLE_FLOOR_MIN_UP_DOT + ) return idle_support_is_stable( - is_on_floor(), get_floor_normal(), _current_slide_normals(), _floor_angle_loose_ticks + is_on_floor(), get_floor_normal(), _current_slide_normals(), _floor_angle_loose_ticks, min_dot ) diff --git a/client/test/player_test.gd b/client/test/player_test.gd index 4220396..e24ced2 100644 --- a/client/test/player_test.gd +++ b/client/test/player_test.gd @@ -62,6 +62,15 @@ func test_idle_support_is_stable_false_when_floor_is_not_flat_enough() -> void: assert_that(stable).is_false() +func test_idle_support_is_stable_min_flat_up_dot_parameter() -> void: + var slide_normals: Array[Vector3] = [Vector3.UP] + var floor_n: Vector3 = Vector3(0.05, 0.99875, 0.0).normalized() + var strict: bool = PLAYER_SCRIPT.idle_support_is_stable(true, floor_n, slide_normals, 0, 0.999) + var holdish: bool = PLAYER_SCRIPT.idle_support_is_stable(true, floor_n, slide_normals, 0, 0.992) + assert_that(strict).is_false() + assert_that(holdish).is_true() + + func test_idle_support_is_stable_true_when_flat_floor_has_extra_contacts() -> void: var slide_normals: Array[Vector3] = [Vector3.UP, Vector3(0.0, 0.6, 0.8).normalized()] var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(true, Vector3.UP, slide_normals, 0)