From b8176f8fa1ce5ef616a8db2a3e6081dfdb828694 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 5 Apr 2026 01:26:59 -0400 Subject: [PATCH] =?UTF-8?q?NS-23:=20simplify=20client=20movement=20?= =?UTF-8?q?=E2=80=94=20horizontal=20only,=20no=20bump=20nav=20hacks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace stacked NS-19 workarounds (underfoot rays, plateau nav skip, 3D climb/descent steering, rim escape) with one rule: set horizontal velocity toward waypoint/goal, force velocity.y = 0, move_and_slide + floor_* handle height along geometry. Server MoveCommand remains the single place for allow/deny on illegal steps. --- client/scripts/player.gd | 98 +++++++--------------------------------- 1 file changed, 17 insertions(+), 81 deletions(-) diff --git a/client/scripts/player.gd b/client/scripts/player.gd index 900c60f..3912d82 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -1,28 +1,17 @@ extends CharacterBody3D -## NS-23: Nav waypoints + stored server goal for arrival. -## NS-19: any underfoot ray into `ns19_bump` → skip nav (waypoints); bumps are small vs obstacles. +## NS-23: Follow server move target on walkable geometry. +## +## Horizontal velocity toward nav waypoint or goal only; `velocity.y` stays 0. Height along ramps +## and steps comes from `move_and_slide` + floor_* on CharacterBody3D. Move **legality** (step, +## distance, bounds) is server MoveCommand only — not reimplemented here. +## +## NavigationAgent3D detours obstacles; steering uses target **xz** only. const MOVE_SPEED: float = 5.0 const ARRIVE_EPS: float = 0.35 -## Must be **below** NS-19 bump rise (~0.12–0.15) or we "arrive" while still on the plateau when the -## goal is on the floor beside the bump (small horiz offset + ~0.15 m vertical). 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 -const NS19_RAY_DOWN: float = 3.0 -## Origins (world-space offset from body) before casting down. Wider xz helps the 1 m Bump A rim -## where a single vertical line can clear the frustum; Bump B stays covered too. -const NS19_UNDERFOOT_OFFSETS: Array[Vector3] = [ - Vector3(0, 0, 0), - Vector3(0, -0.45, 0), - Vector3(0, -0.82, 0), - Vector3(0.42, -0.28, 0), - Vector3(-0.42, -0.28, 0), - Vector3(0, -0.28, 0.42), - Vector3(0, -0.28, -0.42), -] var _has_walk_goal: bool = false var _auth_walk_goal: Vector3 = Vector3.ZERO @@ -53,57 +42,13 @@ func snap_to_server(world_pos: Vector3) -> void: _nav_agent.set_target_position(world_pos) -func _should_skip_nav_for_ns19_bump() -> bool: - for off: Vector3 in NS19_UNDERFOOT_OFFSETS: - if _ns19_bump_under_ray(global_position + off): - return true - return false - - -func _ns19_bump_under_ray(from: Vector3) -> bool: - var space: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state - var to: Vector3 = from + Vector3(0.0, -NS19_RAY_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 false - var col: Variant = hit.get("collider") - var n: Node = col as Node - while n: - if n.is_in_group("ns19_bump"): - return true - n = n.get_parent() - return false - - -func _steer_toward_world_point(point: Vector3) -> void: +func _set_horizontal_velocity_toward(point: Vector3) -> void: var pos := global_position - var delta_h: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z) - # Full 3D toward a waypoint that is mostly *below* (same xz, on the slope) while standing on - # the plateau yields almost no horizontal speed, so you never reach the rim. Nav in xz + floor - # slide handles ramps and departure from bump tops. - if delta_h.length_squared() > 1e-8: - velocity = delta_h.normalized() * MOVE_SPEED - return - var dy: float = point.y - pos.y - if dy > 0.08: - var delta: Vector3 = point - pos - if delta.length_squared() < 1e-8: - velocity = Vector3.ZERO - return - var desired: Vector3 = delta.normalized() * MOVE_SPEED - desired.y = clampf(desired.y, -MAX_DESCENT_SPEED, MAX_CLIMB_SPEED) - velocity = desired - elif dy < 0.0: - # Same xz, goal slightly lower (e.g. -0.03): old threshold left velocity at zero (“hang”). - var escape_h := Vector3(-global_transform.basis.z.x, 0.0, -global_transform.basis.z.z) - if escape_h.length_squared() < 1e-8: - escape_h = Vector3(1.0, 0.0, 0.0) - velocity = escape_h.normalized() * MOVE_SPEED - else: - velocity = Vector3.ZERO + var dh: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z) + if dh.length_squared() < 1e-10: + dh = Vector3(1.0, 0.0, 0.0) + velocity = dh.normalized() * MOVE_SPEED + velocity.y = 0.0 func _physics_process(_delta: float) -> void: @@ -122,31 +67,22 @@ func _physics_process(_delta: float) -> void: move_and_slide() return - # Waypoints route around obstacles; steering stays horizontal-only (see _steer_toward_world_point) - # so bump plateaus do not get a mostly-vertical first segment. If next waypoint aligns under the - # player (small xz delta), fall back to steering toward the goal xz. 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 - - # On bump: skip nav waypoints (drop-first paths); bump area is small vs the obstacle. - if _should_skip_nav_for_ns19_bump(): - _steer_toward_world_point(_auth_walk_goal) + _set_horizontal_velocity_toward(_auth_walk_goal) move_and_slide() return if horiz_dist <= DIRECT_APPROACH_RADIUS: - _steer_toward_world_point(_auth_walk_goal) + _set_horizontal_velocity_toward(_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) + _set_horizontal_velocity_toward(next_path_position) else: - _steer_toward_world_point(_auth_walk_goal) + _set_horizontal_velocity_toward(_auth_walk_goal) move_and_slide()