NS-23: terminal collision, nav around obstacle, agent under region

- Add BoxShape3D to PrototypeTerminal (layer 1, walkable) so the player
  cannot pass through; matches terminal mesh size.
- Bake navigation from collision layer 1 only so the player (layer 2) is
  not merged into the mesh; reparent Player under NavigationRegion3D so
  NavigationAgent3D resolves a valid map.
- Restore nav waypoint following for long horizontal legs; keep horizontal
  steering toward next/goal so bump departure behavior stays sane.
pull/23/head
VinPropane 2026-04-05 01:10:16 -04:00
parent 05d7916cc7
commit a5ec8df4a7
3 changed files with 36 additions and 11 deletions

View File

@ -33,6 +33,9 @@ size = Vector3(0.9, 1, 0.4)
[sub_resource type="StandardMaterial3D" id="Mat_terminal"] [sub_resource type="StandardMaterial3D" id="Mat_terminal"]
albedo_color = Color(0.28, 0.38, 0.45, 1) albedo_color = Color(0.28, 0.38, 0.45, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_terminal"]
size = Vector3(0.9, 1, 0.4)
[sub_resource type="BoxMesh" id="BoxMesh_marker"] [sub_resource type="BoxMesh" id="BoxMesh_marker"]
size = Vector3(0.22, 0.35, 0.22) size = Vector3(0.22, 0.35, 0.22)
@ -74,7 +77,7 @@ albedo_color = Color(0.32, 0.38, 0.55, 1)
[sub_resource type="NavigationMesh" id="NavigationMesh_district"] [sub_resource type="NavigationMesh" id="NavigationMesh_district"]
geometry_parsed_geometry_type = 2 geometry_parsed_geometry_type = 2
geometry_collision_mask = 4294967295 geometry_collision_mask = 1
geometry_source_geometry_mode = 0 geometry_source_geometry_mode = 0
cell_size = 0.15 cell_size = 0.15
cell_height = 0.15 cell_height = 0.15
@ -111,13 +114,17 @@ mesh = SubResource("BoxMesh_floor")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0)
shape = SubResource("BoxShape3D_floor") shape = SubResource("BoxShape3D_floor")
[node name="PrototypeTerminal" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1700001] [node name="PrototypeTerminal" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1700001 groups=["walkable"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
collision_layer = 1
[node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/PrototypeTerminal" unique_id=1700002] [node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/PrototypeTerminal" unique_id=1700002]
mesh = SubResource("BoxMesh_terminal") mesh = SubResource("BoxMesh_terminal")
surface_material_override/0 = SubResource("Mat_terminal") surface_material_override/0 = SubResource("Mat_terminal")
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/PrototypeTerminal" unique_id=1700006]
shape = SubResource("BoxShape3D_terminal")
[node name="InteractionMarkers" type="Node3D" parent="World" unique_id=1700003] [node name="InteractionMarkers" type="Node3D" parent="World" unique_id=1700003]
script = ExtResource("6_rad") script = ExtResource("6_rad")
@ -189,7 +196,7 @@ mesh = SubResource("BoxMesh_obstacle")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("BoxShape3D_obstacle") shape = SubResource("BoxShape3D_obstacle")
[node name="Player" type="CharacterBody3D" parent="." unique_id=352931696] [node name="Player" type="CharacterBody3D" parent="World/NavigationRegion3D" unique_id=352931696]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0.9, -5) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0.9, -5)
collision_layer = 2 collision_layer = 2
collision_mask = 1 collision_mask = 1
@ -198,10 +205,10 @@ floor_snap_length = 0.28
safe_margin = 0.045 safe_margin = 0.045
script = ExtResource("2_player") script = ExtResource("2_player")
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player" unique_id=1695755590] [node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/Player" unique_id=1695755590]
shape = SubResource("CapsuleShape3D_player") shape = SubResource("CapsuleShape3D_player")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="Player" unique_id=8880002] [node name="NavigationAgent3D" type="NavigationAgent3D" parent="World/NavigationRegion3D/Player" unique_id=8880002]
avoidance_enabled = false avoidance_enabled = false
path_desired_distance = 0.35 path_desired_distance = 0.35
target_desired_distance = 0.35 target_desired_distance = 0.35
@ -209,7 +216,7 @@ path_height_offset = 0.0
radius = 0.4 radius = 0.4
height = 1.0 height = 1.0
[node name="MeshInstance3D" type="MeshInstance3D" parent="Player" unique_id=2027034386] [node name="MeshInstance3D" type="MeshInstance3D" parent="World/NavigationRegion3D/Player" unique_id=2027034386]
mesh = SubResource("CapsuleMesh_player") mesh = SubResource("CapsuleMesh_player")
[node name="GroundPick" type="Node3D" parent="." unique_id=2500001] [node name="GroundPick" type="Node3D" parent="." unique_id=2500001]

View File

@ -11,7 +11,7 @@ var _move_reject_msg_token: int = 0
@onready var _camera: Camera3D = $World/Camera3D @onready var _camera: Camera3D = $World/Camera3D
@onready var _nav_region: NavigationRegion3D = $World/NavigationRegion3D @onready var _nav_region: NavigationRegion3D = $World/NavigationRegion3D
@onready var _player: CharacterBody3D = $Player @onready var _player: CharacterBody3D = $World/NavigationRegion3D/Player
@onready var _ground_pick: Node3D = $GroundPick @onready var _ground_pick: Node3D = $GroundPick
@onready var _authority: Node = $PositionAuthorityClient @onready var _authority: Node = $PositionAuthorityClient
@onready var _radius_preview: Node3D = $World/InteractionMarkers @onready var _radius_preview: Node3D = $World/InteractionMarkers

View File

@ -10,6 +10,7 @@ const ARRIVE_EPS: float = 0.35
const VERT_ARRIVE_EPS: float = 0.055 const VERT_ARRIVE_EPS: float = 0.055
const MAX_CLIMB_SPEED: float = 2.6 const MAX_CLIMB_SPEED: float = 2.6
const MAX_DESCENT_SPEED: float = 2.2 const MAX_DESCENT_SPEED: float = 2.2
const DIRECT_APPROACH_RADIUS: float = 0.85
var _has_walk_goal: bool = false var _has_walk_goal: bool = false
var _auth_walk_goal: Vector3 = Vector3.ZERO var _auth_walk_goal: Vector3 = Vector3.ZERO
@ -84,8 +85,25 @@ func _physics_process(_delta: float) -> void:
move_and_slide() move_and_slide()
return return
# Do not follow NavigationAgent3D waypoints: from the bump plateau the first segment often # Waypoints route around obstacles; steering stays horizontal-only (see _steer_toward_world_point)
# drops straight down the mesh, and the agent map can disagree with presentation. Steer only # so bump plateaus do not get a mostly-vertical first segment. If next waypoint aligns under the
# toward the server goal in xz; move_and_slide + floor handles slopes and obstacles. # player (small xz delta), fall back to steering toward the goal xz.
_steer_toward_world_point(_auth_walk_goal) var nav_map: RID = _nav_agent.get_navigation_map()
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
_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:
var next_path_position: Vector3 = _nav_agent.get_next_path_position()
var to_next_h: Vector3 = Vector3(
next_path_position.x - global_position.x, 0.0, next_path_position.z - global_position.z
)
if to_next_h.length_squared() > 0.0025:
_steer_toward_world_point(next_path_position)
else:
_steer_toward_world_point(_auth_walk_goal)
move_and_slide() move_and_slide()