From 8b2b64a32651c6aaac4cf0f54ae1e00f652562d8 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 5 Apr 2026 01:33:57 -0400 Subject: [PATCH] NS-23: bump step-off bypass using floor-under-feet, not body Y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ray down for floor Y under the capsule; skip nav only when goal is a bit lower than that surface, drop ≤0.55 m, and horiz ≤1.05 m. Matches leaving a bump without comparing pick/body origin (which broke obstacle pathing). Long crosses still use waypoints. --- client/scripts/player.gd | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 2b11d34..bcf4e10 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -6,12 +6,16 @@ extends CharacterBody3D ## and steps comes from `move_and_slide` + floor_* on CharacterBody3D. Move **legality** (step, ## distance, bounds) is server MoveCommand only — not reimplemented here. ## -## Nav for detours; if next waypoint has almost no xz offset, steer xz toward goal. +## Nav for detours; short local step-down (feet vs goal Y, not body origin) may bypass waypoints. const MOVE_SPEED: float = 5.0 const ARRIVE_EPS: float = 0.35 const VERT_ARRIVE_EPS: float = 0.055 const DIRECT_APPROACH_RADIUS: float = 0.85 +const FLOOR_PROBE_DOWN: float = 4.0 +const STEP_OFF_FLOOR_MARGIN: float = 0.04 +const STEP_OFF_MAX_DROP: float = 0.55 +const STEP_OFF_MAX_HORIZ: float = 1.05 var _has_walk_goal: bool = false var _auth_walk_goal: Vector3 = Vector3.ZERO @@ -42,6 +46,37 @@ func snap_to_server(world_pos: Vector3) -> void: _nav_agent.set_target_position(world_pos) +func _floor_y_under_body() -> float: + var space: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state + var from: Vector3 = global_position + var to: Vector3 = from + Vector3(0.0, -FLOOR_PROBE_DOWN, 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 global_position.y + var pv: Variant = hit.get("position") + if pv is Vector3: + return (pv as Vector3).y + return global_position.y + + +func _nav_short_step_down_use_goal_direct() -> bool: + var floor_y: float = _floor_y_under_body() + if _auth_walk_goal.y >= floor_y - STEP_OFF_FLOOR_MARGIN: + return false + var drop: float = floor_y - _auth_walk_goal.y + if drop > STEP_OFF_MAX_DROP: + return false + var hd: float = Vector2( + _auth_walk_goal.x - global_position.x, _auth_walk_goal.z - global_position.z + ).length() + if hd > STEP_OFF_MAX_HORIZ: + return false + return true + + func _set_horizontal_velocity_toward(point: Vector3) -> void: var pos := global_position var dh: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z) @@ -73,6 +108,11 @@ func _physics_process(_delta: float) -> void: move_and_slide() return + if _nav_short_step_down_use_goal_direct(): + _set_horizontal_velocity_toward(_auth_walk_goal) + move_and_slide() + return + if horiz_dist <= DIRECT_APPROACH_RADIUS: _set_horizontal_velocity_toward(_auth_walk_goal) else: