NS-23: NS-19 bump convex ramps; drop autostep
- Add four invisible ConvexPolygonShape3D ramp wedges per bump (walkable) so move_and_slide can climb/descend without test_move ledge hacks. - Remove _try_autostep / _maybe_autostep_after_slide and LEDGE_DROP_STEPS; they mis-detected geometry and fought CharacterBody3D slide. - Fix duplicate subresource unique_id on BumpA RampWest and BumpB RampSouth.pull/23/head
parent
394e2a71fb
commit
0e79250f1a
|
|
@ -51,6 +51,30 @@ size = Vector3(0.85, 0.12, 0.85)
|
||||||
[sub_resource type="BoxShape3D" id="BoxShape3D_ns19_bump_b"]
|
[sub_resource type="BoxShape3D" id="BoxShape3D_ns19_bump_b"]
|
||||||
size = Vector3(0.85, 0.12, 0.85)
|
size = Vector3(0.85, 0.12, 0.85)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19a_ramp_s"]
|
||||||
|
points = PackedVector3Array(-0.5, -0.075, -0.65, 0.5, -0.075, -0.65, 0.5, -0.075, -0.5, -0.5, -0.075, -0.5, 0.5, 0.075, -0.5, -0.5, 0.075, -0.5)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19a_ramp_n"]
|
||||||
|
points = PackedVector3Array(-0.5, -0.075, 0.5, 0.5, -0.075, 0.5, 0.5, -0.075, 0.65, -0.5, -0.075, 0.65, 0.5, 0.075, 0.5, -0.5, 0.075, 0.5)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19a_ramp_e"]
|
||||||
|
points = PackedVector3Array(0.5, -0.075, -0.5, 0.65, -0.075, -0.5, 0.65, -0.075, 0.5, 0.5, -0.075, 0.5, 0.5, 0.075, 0.5, 0.5, 0.075, -0.5)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19a_ramp_w"]
|
||||||
|
points = PackedVector3Array(-0.65, -0.075, -0.5, -0.5, -0.075, -0.5, -0.5, -0.075, 0.5, -0.65, -0.075, 0.5, -0.5, 0.075, 0.5, -0.5, 0.075, -0.5)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19b_ramp_s"]
|
||||||
|
points = PackedVector3Array(-0.425, -0.06, -0.575, 0.425, -0.06, -0.575, 0.425, -0.06, -0.425, -0.425, -0.06, -0.425, 0.425, 0.06, -0.425, -0.425, 0.06, -0.425)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19b_ramp_n"]
|
||||||
|
points = PackedVector3Array(-0.425, -0.06, 0.425, 0.425, -0.06, 0.425, 0.425, -0.06, 0.575, -0.425, -0.06, 0.575, 0.425, 0.06, 0.425, -0.425, 0.06, 0.425)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19b_ramp_e"]
|
||||||
|
points = PackedVector3Array(0.425, -0.06, -0.425, 0.575, -0.06, -0.425, 0.575, -0.06, 0.425, 0.425, -0.06, 0.425, 0.425, 0.06, 0.425, 0.425, 0.06, -0.425)
|
||||||
|
|
||||||
|
[sub_resource type="ConvexPolygonShape3D" id="Convex_ns19b_ramp_w"]
|
||||||
|
points = PackedVector3Array(-0.575, -0.06, -0.425, -0.425, -0.06, -0.425, -0.425, -0.06, 0.425, -0.575, -0.06, 0.425, -0.425, 0.06, 0.425, -0.425, 0.06, -0.425)
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_ns19_pedestal"]
|
[sub_resource type="BoxMesh" id="BoxMesh_ns19_pedestal"]
|
||||||
size = Vector3(1.5, 2.5, 1.5)
|
size = Vector3(1.5, 2.5, 1.5)
|
||||||
|
|
||||||
|
|
@ -142,6 +166,30 @@ surface_material_override/0 = SubResource("Mat_ns19_bump")
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900003]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900003]
|
||||||
shape = SubResource("BoxShape3D_ns19_bump_a")
|
shape = SubResource("BoxShape3D_ns19_bump_a")
|
||||||
|
|
||||||
|
[node name="RampSouth" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900004 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA/RampSouth" unique_id=1900005]
|
||||||
|
shape = SubResource("Convex_ns19a_ramp_s")
|
||||||
|
|
||||||
|
[node name="RampNorth" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900006 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA/RampNorth" unique_id=1900007]
|
||||||
|
shape = SubResource("Convex_ns19a_ramp_n")
|
||||||
|
|
||||||
|
[node name="RampEast" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900008 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA/RampEast" unique_id=1900009]
|
||||||
|
shape = SubResource("Convex_ns19a_ramp_e")
|
||||||
|
|
||||||
|
[node name="RampWest" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpA" unique_id=1900010 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpA/RampWest" unique_id=1910014]
|
||||||
|
shape = SubResource("Convex_ns19a_ramp_w")
|
||||||
|
|
||||||
[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"]]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.85, 0.06, -4.1)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.85, 0.06, -4.1)
|
||||||
collision_layer = 1
|
collision_layer = 1
|
||||||
|
|
@ -153,6 +201,30 @@ surface_material_override/0 = SubResource("Mat_ns19_bump")
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpB" unique_id=1900013]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpB" unique_id=1900013]
|
||||||
shape = SubResource("BoxShape3D_ns19_bump_b")
|
shape = SubResource("BoxShape3D_ns19_bump_b")
|
||||||
|
|
||||||
|
[node name="RampSouth" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpB" unique_id=1911004 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpB/RampSouth" unique_id=1911005]
|
||||||
|
shape = SubResource("Convex_ns19b_ramp_s")
|
||||||
|
|
||||||
|
[node name="RampNorth" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpB" unique_id=1900016 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpB/RampNorth" unique_id=1900017]
|
||||||
|
shape = SubResource("Convex_ns19b_ramp_n")
|
||||||
|
|
||||||
|
[node name="RampEast" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpB" unique_id=1900018 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpB/RampEast" unique_id=1900019]
|
||||||
|
shape = SubResource("Convex_ns19b_ramp_e")
|
||||||
|
|
||||||
|
[node name="RampWest" type="StaticBody3D" parent="World/NavigationRegion3D/NS19BumpB" unique_id=1900020 groups=["walkable"]]
|
||||||
|
collision_layer = 1
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="World/NavigationRegion3D/NS19BumpB/RampWest" unique_id=1900024]
|
||||||
|
shape = SubResource("Convex_ns19b_ramp_w")
|
||||||
|
|
||||||
[node name="NS19RejectPedestal" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900021 groups=["walkable"]]
|
[node name="NS19RejectPedestal" type="StaticBody3D" parent="World/NavigationRegion3D" unique_id=1900021 groups=["walkable"]]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.5, 0, -6.5)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.5, 0, -6.5)
|
||||||
collision_layer = 1
|
collision_layer = 1
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
extends CharacterBody3D
|
extends CharacterBody3D
|
||||||
|
|
||||||
## NS-23: Nav agent suggests waypoints; arrival uses stored server goal only (not agent “finished”).
|
## NS-23: Nav waypoints + stored server goal for arrival.
|
||||||
## NS-19 bumps: **autostep** via `test_move` — up+forward to mount, down+forward to leave (pure down
|
## NS-19: invisible convex **ramps** on bumps; no `test_move` autostep (bad vs `move_and_slide`).
|
||||||
## hits the bump volume you’re standing on).
|
|
||||||
|
|
||||||
const MOVE_SPEED: float = 5.0
|
const MOVE_SPEED: float = 5.0
|
||||||
const ARRIVE_EPS: float = 0.35
|
const ARRIVE_EPS: float = 0.35
|
||||||
|
|
@ -10,11 +9,6 @@ const VERT_ARRIVE_EPS: float = 0.16
|
||||||
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
|
const DIRECT_APPROACH_RADIUS: float = 0.85
|
||||||
const STEP_MAX_HEIGHT: float = 0.32
|
|
||||||
const STEP_PROBE_FWD: float = 0.38
|
|
||||||
const STUCK_HORIZ_SPEED: float = 0.28
|
|
||||||
## Drop tries when leaving a bump (combined with forward over the lip).
|
|
||||||
const LEDGE_DROP_STEPS = [0.08, 0.12, 0.18, 0.24, 0.32]
|
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -55,54 +49,7 @@ func _steer_toward_world_point(point: Vector3) -> void:
|
||||||
velocity = desired
|
velocity = desired
|
||||||
|
|
||||||
|
|
||||||
## Godot `test_move`: returns **true** if the motion would hit something (blocked).
|
func _physics_process(_delta: float) -> void:
|
||||||
func _try_autostep(delta: float, full_to_goal: Vector3) -> bool:
|
|
||||||
var to_goal_h: Vector3 = Vector3(
|
|
||||||
full_to_goal.x, 0.0, full_to_goal.z
|
|
||||||
)
|
|
||||||
if to_goal_h.length_squared() < 0.0225:
|
|
||||||
return false
|
|
||||||
var dir_h: Vector3 = to_goal_h.normalized()
|
|
||||||
var fwd_len: float = maxf(MOVE_SPEED * delta, 0.1)
|
|
||||||
var motion_fwd: Vector3 = dir_h * minf(fwd_len, STEP_PROBE_FWD)
|
|
||||||
var xf: Transform3D = global_transform
|
|
||||||
if not test_move(xf, motion_fwd):
|
|
||||||
return false
|
|
||||||
if full_to_goal.y > 0.02:
|
|
||||||
return _autostep_up(xf, motion_fwd, dir_h)
|
|
||||||
if full_to_goal.y < -0.02:
|
|
||||||
return _autostep_down(xf, motion_fwd, dir_h)
|
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
func _autostep_up(xf: Transform3D, motion_fwd: Vector3, dir_h: Vector3) -> bool:
|
|
||||||
var motion_up: Vector3 = Vector3(0.0, STEP_MAX_HEIGHT, 0.0)
|
|
||||||
if test_move(xf, motion_up):
|
|
||||||
return false
|
|
||||||
var xf_up := Transform3D(xf.basis, xf.origin + motion_up)
|
|
||||||
if test_move(xf_up, motion_fwd):
|
|
||||||
return false
|
|
||||||
global_position += motion_up + motion_fwd
|
|
||||||
velocity.x = dir_h.x * MOVE_SPEED
|
|
||||||
velocity.z = dir_h.z * MOVE_SPEED
|
|
||||||
velocity.y = 0.0
|
|
||||||
return true
|
|
||||||
|
|
||||||
|
|
||||||
func _autostep_down(xf: Transform3D, motion_fwd: Vector3, dir_h: Vector3) -> bool:
|
|
||||||
for drop: float in LEDGE_DROP_STEPS:
|
|
||||||
var motion_off: Vector3 = motion_fwd + Vector3(0.0, -drop, 0.0)
|
|
||||||
if test_move(xf, motion_off):
|
|
||||||
continue
|
|
||||||
global_position += motion_off
|
|
||||||
velocity.x = dir_h.x * MOVE_SPEED
|
|
||||||
velocity.z = dir_h.z * MOVE_SPEED
|
|
||||||
velocity.y = -minf(MAX_DESCENT_SPEED, 1.2)
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
|
||||||
if not _has_walk_goal:
|
if not _has_walk_goal:
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
|
|
@ -122,7 +69,6 @@ func _physics_process(delta: float) -> void:
|
||||||
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
||||||
_steer_toward_world_point(_auth_walk_goal)
|
_steer_toward_world_point(_auth_walk_goal)
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_maybe_autostep_after_slide(delta, full_to_goal, horiz_dist)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if horiz_dist <= DIRECT_APPROACH_RADIUS:
|
if horiz_dist <= DIRECT_APPROACH_RADIUS:
|
||||||
|
|
@ -138,21 +84,3 @@ func _physics_process(delta: float) -> void:
|
||||||
_steer_toward_world_point(_auth_walk_goal)
|
_steer_toward_world_point(_auth_walk_goal)
|
||||||
|
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_maybe_autostep_after_slide(delta, full_to_goal, horiz_dist)
|
|
||||||
|
|
||||||
|
|
||||||
func _maybe_autostep_after_slide(
|
|
||||||
delta: float, full_to_goal: Vector3, horiz_dist: float
|
|
||||||
) -> void:
|
|
||||||
if not _has_walk_goal:
|
|
||||||
return
|
|
||||||
if not is_on_floor() and not is_on_wall():
|
|
||||||
return
|
|
||||||
var hspd: float = Vector2(velocity.x, velocity.z).length()
|
|
||||||
var want_step: bool = hspd < STUCK_HORIZ_SPEED
|
|
||||||
if not want_step and is_on_wall():
|
|
||||||
want_step = horiz_dist > 0.12 and (
|
|
||||||
full_to_goal.y > 0.03 or full_to_goal.y < -0.03
|
|
||||||
)
|
|
||||||
if want_step and _try_autostep(delta, full_to_goal):
|
|
||||||
move_and_slide()
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue