NS-23: harden bump departure (always skip nav on bump, steer dy<0)

- If any of three down-rays (body + two lower origins) hits ns19_bump,
  skip NavigationAgent waypoints unconditionally on that footprint.
- Steering: any goal lower than origin with same xz (dy < 0) uses rim
  escape; dy in (-0.04, 0) previously zeroed velocity and could hang.
pull/23/head
VinPropane 2026-04-05 01:16:07 -04:00
parent 66d7438a59
commit 35d16d15db
1 changed files with 9 additions and 18 deletions

View File

@ -1,7 +1,7 @@
extends CharacterBody3D
## NS-23: Nav waypoints + stored server goal for arrival.
## NS-19: `ns19_bump` — skip nav waypoints when leaving (goal lower) or on flatish bump top.
## NS-19: any underfoot ray into `ns19_bump` → skip nav (waypoints); bumps are small vs obstacles.
const MOVE_SPEED: float = 5.0
const ARRIVE_EPS: float = 0.35
@ -11,11 +11,9 @@ const VERT_ARRIVE_EPS: float = 0.055
const MAX_CLIMB_SPEED: float = 2.6
const MAX_DESCENT_SPEED: float = 2.2
const DIRECT_APPROACH_RADIUS: float = 0.85
## Plateau / near-rim: floor normal within ~15° of up still counts (0.992 missed convex edges).
const NS19_BUMP_FLAT_NORMAL_DOT: float = 0.964
const NS19_RAY_DOWN: float = 3.0
## Second ray origin offset toward feet so a tall capsule still hits bump underfoot.
const NS19_FEET_RAY_Y_OFFSET: float = -0.45
## Downward ray origins (Y offset from body origin) so we still detect bump under a tall capsule.
const NS19_UNDERFOOT_RAY_Y: Array[float] = [0.0, -0.45, -0.82]
var _has_walk_goal: bool = false
var _auth_walk_goal: Vector3 = Vector3.ZERO
@ -47,16 +45,9 @@ func snap_to_server(world_pos: Vector3) -> void:
func _should_skip_nav_for_ns19_bump() -> bool:
var under_bump: bool = _ns19_bump_under_ray(global_position)
if not under_bump:
under_bump = _ns19_bump_under_ray(global_position + Vector3(0.0, NS19_FEET_RAY_Y_OFFSET, 0.0))
if not under_bump:
return false
# Waypoints pick “down the mesh” first; horizontal steer to goal fixes departure.
if _auth_walk_goal.y < global_position.y - 0.02:
return true
if is_on_floor() and get_floor_normal().dot(Vector3.UP) >= NS19_BUMP_FLAT_NORMAL_DOT:
return true
for y_off: float in NS19_UNDERFOOT_RAY_Y:
if _ns19_bump_under_ray(global_position + Vector3(0.0, y_off, 0.0)):
return true
return false
@ -96,8 +87,8 @@ func _steer_toward_world_point(point: Vector3) -> void:
var desired: Vector3 = delta.normalized() * MOVE_SPEED
desired.y = clampf(desired.y, -MAX_DESCENT_SPEED, MAX_CLIMB_SPEED)
velocity = desired
elif dy < -0.04:
# Goal is lower and almost same xz (e.g. floor under the plateau): still need rim escape.
elif dy < 0.0:
# Same xz, goal slightly lower (e.g. -0.03): old threshold left velocity at zero (“hang”).
var escape_h := Vector3(-global_transform.basis.z.x, 0.0, -global_transform.basis.z.z)
if escape_h.length_squared() < 1e-8:
escape_h = Vector3(1.0, 0.0, 0.0)
@ -131,7 +122,7 @@ func _physics_process(_delta: float) -> void:
move_and_slide()
return
# On bump + (goal lower or flat top): skip waypoints so we do not follow vertical “drop” legs.
# On bump: skip nav waypoints (drop-first paths); bump area is small vs the obstacle.
if _should_skip_nav_for_ns19_bump():
_steer_toward_world_point(_auth_walk_goal)
move_and_slide()