NS-23: Autostep down+forward to walk off bump edges
parent
6fd49a2e1b
commit
b007c7fe58
|
|
@ -1,8 +1,8 @@
|
||||||
extends CharacterBody3D
|
extends CharacterBody3D
|
||||||
|
|
||||||
## NS-23: Nav agent suggests waypoints; arrival uses stored server goal only (not agent “finished”).
|
## NS-23: Nav agent suggests waypoints; arrival uses stored server goal only (not agent “finished”).
|
||||||
## NS-19 bumps: thin box colliders need **auto-step** (`test_move` up+forward); Y steering alone
|
## NS-19 bumps: **autostep** via `test_move` — up+forward to mount, down+forward to leave (pure down
|
||||||
## is not enough for vertical faces.
|
## 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,10 +10,11 @@ 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
|
||||||
## NS-19 bumps ≤ ~0.15 m; probe slightly above for capsule + nav tolerance.
|
|
||||||
const STEP_MAX_HEIGHT: float = 0.32
|
const STEP_MAX_HEIGHT: float = 0.32
|
||||||
const STEP_PROBE_FWD: float = 0.38
|
const STEP_PROBE_FWD: float = 0.38
|
||||||
const STUCK_HORIZ_SPEED: float = 0.28
|
const STUCK_HORIZ_SPEED: float = 0.28
|
||||||
|
## Drop tries when leaving a bump (combined with forward over the lip).
|
||||||
|
const LEDGE_DROP_STEPS: PackedFloat32Array = PackedFloat32Array([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,19 +56,27 @@ func _steer_toward_world_point(point: Vector3) -> void:
|
||||||
|
|
||||||
|
|
||||||
## Godot `test_move`: returns **true** if the motion would hit something (blocked).
|
## Godot `test_move`: returns **true** if the motion would hit something (blocked).
|
||||||
func _try_autostep(delta: float) -> bool:
|
func _try_autostep(delta: float, full_to_goal: Vector3) -> bool:
|
||||||
var to_goal_h: Vector3 = Vector3(
|
var to_goal_h: Vector3 = Vector3(
|
||||||
_auth_walk_goal.x - global_position.x, 0.0, _auth_walk_goal.z - global_position.z
|
full_to_goal.x, 0.0, full_to_goal.z
|
||||||
)
|
)
|
||||||
if to_goal_h.length_squared() < 0.0225:
|
if to_goal_h.length_squared() < 0.0225:
|
||||||
return false
|
return false
|
||||||
var dir_h: Vector3 = to_goal_h.normalized()
|
var dir_h: Vector3 = to_goal_h.normalized()
|
||||||
var fwd_len: float = maxf(MOVE_SPEED * delta, 0.1)
|
var fwd_len: float = maxf(MOVE_SPEED * delta, 0.1)
|
||||||
var motion_fwd: Vector3 = dir_h * minf(fwd_len, STEP_PROBE_FWD)
|
var motion_fwd: Vector3 = dir_h * minf(fwd_len, STEP_PROBE_FWD)
|
||||||
var motion_up: Vector3 = Vector3(0.0, STEP_MAX_HEIGHT, 0.0)
|
|
||||||
var xf: Transform3D = global_transform
|
var xf: Transform3D = global_transform
|
||||||
if not test_move(xf, motion_fwd):
|
if not test_move(xf, motion_fwd):
|
||||||
return false
|
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):
|
if test_move(xf, motion_up):
|
||||||
return false
|
return false
|
||||||
var xf_up := Transform3D(xf.basis, xf.origin + motion_up)
|
var xf_up := Transform3D(xf.basis, xf.origin + motion_up)
|
||||||
|
|
@ -80,6 +89,20 @@ func _try_autostep(delta: float) -> bool:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _autostep_down(xf: Transform3D, motion_fwd: Vector3, dir_h: Vector3) -> bool:
|
||||||
|
for i: int in range(LEDGE_DROP_STEPS.size()):
|
||||||
|
var drop: float = LEDGE_DROP_STEPS[i]
|
||||||
|
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:
|
func _physics_process(delta: float) -> void:
|
||||||
if not _has_walk_goal:
|
if not _has_walk_goal:
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
|
|
@ -129,6 +152,8 @@ func _maybe_autostep_after_slide(
|
||||||
var hspd: float = Vector2(velocity.x, velocity.z).length()
|
var hspd: float = Vector2(velocity.x, velocity.z).length()
|
||||||
var want_step: bool = hspd < STUCK_HORIZ_SPEED
|
var want_step: bool = hspd < STUCK_HORIZ_SPEED
|
||||||
if not want_step and is_on_wall():
|
if not want_step and is_on_wall():
|
||||||
want_step = full_to_goal.y > 0.03 and horiz_dist > 0.12
|
want_step = horiz_dist > 0.12 and (
|
||||||
if want_step and _try_autostep(delta):
|
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()
|
move_and_slide()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue