NEON-29: Hold-threshold stable idle + physics HUD coords
While _idle_stable_latched, use STABLE_IDLE_FLOOR_HOLD_MIN_UP_DOT (0.992) for idle_support floor flatness so tread-edge normal flicker does not unlatch as often. idle_support_is_stable gains optional min_flat_up_dot; unlatch streak 8. Prototype position label updates in main _physics_process. Add GdUnit for param.pull/41/head
parent
5f6058b0ce
commit
46f8851a1a
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue