NS-23: skip nav waypoints on NS-19 bump plateaus only

Restore obstacle routing via waypoints elsewhere; on bump tops nav often
prefers a downward segment and breaks departure. Detect plateau with
is_on_floor + flat normal + ray down into group ns19_bump; then steer
only toward authoritative goal xz. Slopes keep waypoint pathing.
pull/23/head
VinPropane 2026-04-05 01:12:11 -04:00
parent a5ec8df4a7
commit db1fe0f46b
2 changed files with 40 additions and 3 deletions

View File

@ -138,7 +138,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.95, 0.22, -1.05)
mesh = SubResource("BoxMesh_marker")
surface_material_override/0 = SubResource("Mat_marker_base")
[node name="NS19BumpA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900001 groups=["walkable"]]
[node name="NS19BumpA" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900001 groups=["walkable", "ns19_bump"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.4, 0.075, -2.6)
collision_layer = 1
@ -149,7 +149,7 @@ surface_material_override/0 = SubResource("Mat_ns19_bump")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900003]
shape = SubResource("Convex_ns19a_frustum")
[node name="NS19BumpB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900011 groups=["walkable"]]
[node name="NS19BumpB" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900011 groups=["walkable", "ns19_bump"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.85, 0.06, -4.1)
collision_layer = 1

View File

@ -1,7 +1,7 @@
extends CharacterBody3D
## NS-23: Nav waypoints + stored server goal for arrival.
## NS-19: bump collision is one convex frustum (plateau + slopes); no autostep.
## NS-19: convex frustum bumps; `ns19_bump` group — on flat plateau skip nav waypoints (departure).
const MOVE_SPEED: float = 5.0
const ARRIVE_EPS: float = 0.35
@ -11,6 +11,8 @@ 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
## Floor normal must be this “flat” to count as bump plateau (slopes use nav waypoints).
const NS19_BUMP_TOP_NORMAL_DOT: float = 0.992
var _has_walk_goal: bool = false
var _auth_walk_goal: Vector3 = Vector3.ZERO
@ -41,6 +43,33 @@ func snap_to_server(world_pos: Vector3) -> void:
_nav_agent.set_target_position(world_pos)
func _on_ns19_bump_plateau() -> bool:
if not is_on_floor():
return false
if get_floor_normal().dot(Vector3.UP) < NS19_BUMP_TOP_NORMAL_DOT:
return false
return _raycast_down_hits_group("ns19_bump", 2.5)
func _raycast_down_hits_group(group_name: String, cast_len: float) -> bool:
var space: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
var from: Vector3 = global_position
var to: Vector3 = from + Vector3(0.0, -cast_len, 0.0)
var q: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(from, to)
q.collision_mask = 1
q.exclude = [get_rid()]
var hit: Dictionary = space.intersect_ray(q)
if hit.is_empty():
return false
var col: Variant = hit.get("collider")
var n: Node = col as Node
while n:
if n.is_in_group(group_name):
return true
n = n.get_parent()
return false
func _steer_toward_world_point(point: Vector3) -> void:
var pos := global_position
var delta_h: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z)
@ -94,6 +123,14 @@ func _physics_process(_delta: float) -> void:
move_and_slide()
return
# Nav waypoints on the plateau often point down the slope first; goal-only + horizontal steer
# matches the NS-19 departure fix. Slopes stay on waypoints so obstacle routing still applies
# when crossing the district.
if _on_ns19_bump_plateau():
_steer_toward_world_point(_auth_walk_goal)
move_and_slide()
return
if horiz_dist <= DIRECT_APPROACH_RADIUS:
_steer_toward_world_point(_auth_walk_goal)
else: