diff --git a/client/scenes/main.tscn b/client/scenes/main.tscn index b62c246..bab6f8d 100644 --- a/client/scenes/main.tscn +++ b/client/scenes/main.tscn @@ -193,6 +193,7 @@ shape = SubResource("BoxShape3D_obstacle") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0.9, -5) collision_layer = 2 collision_mask = 1 +floor_snap_length = 0.22 script = ExtResource("2_player") [node name="CollisionShape3D" type="CollisionShape3D" parent="Player" unique_id=1695755590] diff --git a/client/scripts/player.gd b/client/scripts/player.gd index cb3d47d..8173a4b 100644 --- a/client/scripts/player.gd +++ b/client/scripts/player.gd @@ -2,9 +2,14 @@ extends CharacterBody3D ## NS-23: Nav agent suggests waypoints; arrival uses stored server goal only (not agent “finished”). ## `is_navigation_finished()` / internal target can clear early while we are still far from goal. +## NS-19 bumps: allow limited Y in `velocity` so `move_and_slide` can step up/down shallow hits. const MOVE_SPEED: float = 5.0 const ARRIVE_EPS: float = 0.35 +## Must match NS-19 shallow bumps (~0.15 m rise); don’t clear goal while still below pick Y. +const VERT_ARRIVE_EPS: float = 0.16 +const MAX_CLIMB_SPEED: float = 2.6 +const MAX_DESCENT_SPEED: float = 2.2 ## Outside this XZ distance to server goal, use nav corners; inside, steer straight to goal. const DIRECT_APPROACH_RADIUS: float = 0.85 @@ -40,17 +45,26 @@ func snap_to_server(world_pos: Vector3) -> void: _nav_agent.set_target_position(world_pos) +func _steer_toward_world_point(point: Vector3) -> void: + var delta: Vector3 = point - global_position + 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 + + func _physics_process(_delta: float) -> void: if not _has_walk_goal: velocity = Vector3.ZERO move_and_slide() return - var to_goal: Vector3 = Vector3( - _auth_walk_goal.x - global_position.x, 0.0, _auth_walk_goal.z - global_position.z - ) - var dist_goal: float = to_goal.length() - if dist_goal <= ARRIVE_EPS: + var full_to_goal: Vector3 = _auth_walk_goal - global_position + var horiz_dist: float = Vector2(full_to_goal.x, full_to_goal.z).length() + var vert_err: float = absf(full_to_goal.y) + if horiz_dist <= ARRIVE_EPS and vert_err <= VERT_ARRIVE_EPS: velocity = Vector3.ZERO _has_walk_goal = false _nav_agent.set_target_position(global_position) @@ -59,24 +73,20 @@ func _physics_process(_delta: float) -> void: var nav_map: RID = _nav_agent.get_navigation_map() if NavigationServer3D.map_get_iteration_id(nav_map) == 0: - velocity = to_goal.normalized() * MOVE_SPEED - velocity.y = 0.0 + _steer_toward_world_point(_auth_walk_goal) move_and_slide() return - var dir: Vector3 - if dist_goal <= DIRECT_APPROACH_RADIUS: - dir = to_goal.normalized() + 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: Vector3 = Vector3( + 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.length_squared() > 0.0025: - dir = to_next.normalized() + if to_next_h.length_squared() > 0.0025: + _steer_toward_world_point(next_path_position) else: - dir = to_goal.normalized() + _steer_toward_world_point(_auth_walk_goal) - velocity = dir * MOVE_SPEED - velocity.y = 0.0 move_and_slide()