NS-23: Fix nav movement (sync bake, map guard, path/fallback steering)
parent
0ac059fa3b
commit
f49c8b88d2
|
|
@ -14,7 +14,7 @@ Do not grow an all-in-one **`main.gd`**. The main scene root should **compose**
|
|||
With the game server running ([`server/README.md`](../server/README.md)), each valid floor click sends a **`MoveCommand`** (**`POST`**) and a follow-up **`GET`** for **`PositionState`**. The server still **snaps** authority to the target (NS-16/19); the client **walks** along a **baked navigation mesh** (`NavigationRegion3D` + **`NavigationAgent3D`** on the player) toward that verified position instead of teleporting on the **`GET`** (NS-23). **Boot** `sync_from_server()` **snaps** once so spawn matches the server.
|
||||
|
||||
- **Scripts:** `scripts/ground_pick.gd` (walkable pick + `target_chosen`), `scripts/position_authority_client.gd` (`PositionAuthorityClient`: POST move, GET verify; second signal arg = boot snap vs nav goal), `scripts/player.gd` (path-follow), thin `scripts/main.gd` (nav bake on first frame, then wiring).
|
||||
- **Scene:** `scenes/main.tscn` — walkable **`StaticBody3D`** geometry lives under **`World/NavigationRegion3D`** so **`bake_navigation_mesh()`** (called from `main.gd` after one **`process_frame`**) picks up floor, NS-19 props, and the **`Obstacle`** cube. After moving floor or obstacles, open the scene in the editor and **Bake NavigationMesh** on the region if you need to refresh cached mesh data.
|
||||
- **Scene:** `scenes/main.tscn` — walkable **`StaticBody3D`** geometry lives under **`World/NavigationRegion3D`**. `main.gd` calls **`bake_navigation_mesh(false)`** (main-thread bake) after one **`process_frame`**, then waits two **`physics_frame`**s so **`NavigationServer3D`** has a map before agents query paths. After moving floor or obstacles, re-bake in the editor if needed.
|
||||
- **Inspector:** select **`PositionAuthorityClient`** on the main scene to set **`base_url`** (default `http://127.0.0.1:5253`) and **`dev_player_id`** (default `dev-local-1`, must match server `Game:DevPlayerId`).
|
||||
|
||||
### Manual check (NS-16 + NS-23)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ var _move_reject_msg_token: int = 0
|
|||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
_nav_region.bake_navigation_mesh()
|
||||
# Default `bake_navigation_mesh()` bakes on a background thread; sync bake ensures the map
|
||||
# exists before any `NavigationAgent3D` queries (see Godot navigation_using_navigationagents).
|
||||
_nav_region.bake_navigation_mesh(false)
|
||||
await get_tree().physics_frame
|
||||
await get_tree().physics_frame
|
||||
_ground_pick.set("fallback_camera", _camera)
|
||||
_ground_pick.connect("target_chosen", Callable(self, "_on_target_chosen"))
|
||||
_authority.connect(
|
||||
|
|
|
|||
|
|
@ -4,43 +4,64 @@ extends CharacterBody3D
|
|||
## `MoveCommand` (NS-16, NS-19); boot snap vs post-verify nav goal from `main.gd`.
|
||||
|
||||
const MOVE_SPEED: float = 5.0
|
||||
## If the nav mesh is missing or the agent gives up, steer on XZ toward `target_position`.
|
||||
const FALLBACK_ARRIVE_EPS: float = 0.4
|
||||
|
||||
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_nav_agent.target_position = global_position
|
||||
_nav_agent.set_target_position(global_position)
|
||||
|
||||
|
||||
## After successful move POST + GET: walk to server target without teleporting.
|
||||
func set_authoritative_nav_goal(world_pos: Vector3) -> void:
|
||||
_nav_agent.target_position = world_pos
|
||||
_nav_agent.set_target_position(world_pos)
|
||||
|
||||
|
||||
## NS-19 rejection or cancel pending path without moving the body.
|
||||
func clear_nav_goal() -> void:
|
||||
velocity = Vector3.ZERO
|
||||
_nav_agent.target_position = global_position
|
||||
_nav_agent.set_target_position(global_position)
|
||||
|
||||
|
||||
## Boot (and any hard reconcile): teleport to server position and reset agent.
|
||||
func snap_to_server(world_pos: Vector3) -> void:
|
||||
global_position = world_pos
|
||||
velocity = Vector3.ZERO
|
||||
_nav_agent.target_position = world_pos
|
||||
_nav_agent.set_target_position(world_pos)
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
if _nav_agent.is_navigation_finished():
|
||||
var nav_map: RID = _nav_agent.get_navigation_map()
|
||||
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
||||
velocity = Vector3.ZERO
|
||||
move_and_slide()
|
||||
return
|
||||
if _nav_agent.is_navigation_finished():
|
||||
var target: Vector3 = _nav_agent.target_position
|
||||
var flat_target := Vector3(target.x, global_position.y, target.z)
|
||||
var to_target: Vector3 = flat_target - global_position
|
||||
to_target.y = 0.0
|
||||
if to_target.length_squared() > FALLBACK_ARRIVE_EPS * FALLBACK_ARRIVE_EPS:
|
||||
velocity = to_target.normalized() * MOVE_SPEED
|
||||
velocity.y = 0.0
|
||||
else:
|
||||
velocity = Vector3.ZERO
|
||||
move_and_slide()
|
||||
return
|
||||
|
||||
var next_path_position: Vector3 = _nav_agent.get_next_path_position()
|
||||
var to_next: Vector3 = next_path_position - global_position
|
||||
to_next.y = 0.0
|
||||
if to_next.length_squared() < 0.0001:
|
||||
velocity = Vector3.ZERO
|
||||
var to_final: Vector3 = _nav_agent.target_position - global_position
|
||||
to_final.y = 0.0
|
||||
if to_final.length_squared() > 0.0001:
|
||||
velocity = to_final.normalized() * MOVE_SPEED
|
||||
velocity.y = 0.0
|
||||
else:
|
||||
velocity = Vector3.ZERO
|
||||
else:
|
||||
velocity = to_next.normalized() * MOVE_SPEED
|
||||
velocity.y = 0.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue