使用 NavigationPaths
取得導覽路徑
只要導覽地圖已有可用的導覽網格,導覽路徑就能直接從 NavigationServer 查詢,不需額外新增任何節點或物件。
若要取得 2D 路徑,請使用 NavigationServer2D.map_get_path(map, from, to, optimize, navigation_layers)。
若要取得 3D 路徑,請使用 NavigationServer3D.map_get_path(map, from, to, optimize, navigation_layers)。
如需進行更多自訂化,並需額外設定的導覽路徑查詢,請參考 使用 NavigationPathQueryObject。
查詢時需提供導覽地圖的 RID。每個遊戲世界都會自動建立一個預設導覽地圖。在任何繼承自 Node2D 的節點上,可使用 get_world_2d().get_navigation_map() 取得 2D 導覽地圖;在任何繼承自 Node3D 的節點上,則可用 get_world_3d().get_navigation_map() 取得 3D 導覽地圖。第二與第三個參數分別為起點與目標位置,2D 使用 Vector2,3D 則為 Vector3。
若 optimized 參數為 true,則會額外經過漏斗演算法處理,使路徑點順著多邊形角落縮短。這對於多邊形大小不一的導覽網格,且需自由移動時效果良好,因為路徑會沿著 A* 演算法找到的多邊形通道貼近轉角。但若格網很小時,A* 可能產生非常狹窄的漏斗型通道,會導致在格狀網格上出現不自然的轉角路徑。
若 optimized 參數為 false,路徑點則會落在每個多邊形邊的中心。這適合用於多邊形大小一致、以格子移動為主的導覽網格,因為路徑會穿越格子中心。若在非格子狀網格上,因多邊形常以單一長邊覆蓋大片開放區域,路徑可能會出現不必要的冗長繞路。
extends Node2D
# Basic query for a navigation path using the default navigation map.
func get_navigation_path(p_start_position: Vector2, p_target_position: Vector2) -> PackedVector2Array:
if not is_inside_tree():
return PackedVector2Array()
var default_map_rid: RID = get_world_2d().get_navigation_map()
var path: PackedVector2Array = NavigationServer2D.map_get_path(
default_map_rid,
p_start_position,
p_target_position,
true
)
return path
using Godot;
using System;
public partial class MyNode2D : Node2D
{
// Basic query for a navigation path using the default navigation map.
private Vector2[] GetNavigationPath(Vector2 startPosition, Vector2 targetPosition)
{
if (!IsInsideTree())
{
return Array.Empty<Vector2>();
}
Rid defaultMapRid = GetWorld2D().NavigationMap;
Vector2[] path = NavigationServer2D.MapGetPath(
defaultMapRid,
startPosition,
targetPosition,
true
);
return path;
}
}
extends Node3D
# Basic query for a navigation path using the default navigation map.
func get_navigation_path(p_start_position: Vector3, p_target_position: Vector3) -> PackedVector3Array:
if not is_inside_tree():
return PackedVector3Array()
var default_map_rid: RID = get_world_3d().get_navigation_map()
var path: PackedVector3Array = NavigationServer3D.map_get_path(
default_map_rid,
p_start_position,
p_target_position,
true
)
return path
using Godot;
using System;
public partial class MyNode3D : Node3D
{
// Basic query for a navigation path using the default navigation map.
private Vector3[] GetNavigationPath(Vector3 startPosition, Vector3 targetPosition)
{
if (!IsInsideTree())
{
return Array.Empty<Vector3>();
}
Rid defaultMapRid = GetWorld3D().NavigationMap;
Vector3[] path = NavigationServer3D.MapGetPath(
defaultMapRid,
startPosition,
targetPosition,
true
);
return path;
}
}
NavigationServer 回傳的 path 會是 2D 的 PackedVector2Array 或 3D 的 PackedVector3Array,皆為記憶體優化的向量陣列。陣列中所有位置向量都保證位於 NavigationPolygon 或 NavigationMesh 內。若路徑陣列非空,第一個索引 path[0] 會是離起點最近的網格位置,最後一個索引 path[path.size()-1] 則為距目標最近的可用網格位置。中間的索引則是角色在不離開導覽網格情況下,應依序通過的路徑點。
備註
若目標位置位於未合併或未連接的不同導覽網格,則導覽路徑會引導到起點所屬網格中,距離目標位置最近的可達點。
以下程式碼範例說明如何在 Node3D 繼承節點中,透過 set_movement_target() 設定目標位置,並利用預設導覽地圖讓節點沿著導覽路徑移動。
@onready var default_3d_map_rid: RID = get_world_3d().get_navigation_map()
var movement_speed: float = 4.0
var movement_delta: float
var path_point_margin: float = 0.5
var current_path_index: int = 0
var current_path_point: Vector3
var current_path: PackedVector3Array
func set_movement_target(target_position: Vector3):
var start_position: Vector3 = global_transform.origin
current_path = NavigationServer3D.map_get_path(
default_3d_map_rid,
start_position,
target_position,
true
)
if not current_path.is_empty():
current_path_index = 0
current_path_point = current_path[0]
func _physics_process(delta):
if current_path.is_empty():
return
movement_delta = movement_speed * delta
if global_transform.origin.distance_to(current_path_point) <= path_point_margin:
current_path_index += 1
if current_path_index >= current_path.size():
current_path = []
current_path_index = 0
current_path_point = global_transform.origin
return
current_path_point = current_path[current_path_index]
var new_velocity: Vector3 = global_transform.origin.direction_to(current_path_point) * movement_delta
global_transform.origin = global_transform.origin.move_toward(global_transform.origin + new_velocity, movement_delta)
using Godot;
public partial class MyNode3D : Node3D
{
private Rid _default3DMapRid;
private float _movementSpeed = 4.0f;
private float _movementDelta;
private float _pathPointMargin = 0.5f;
private int _currentPathIndex = 0;
private Vector3 _currentPathPoint;
private Vector3[] _currentPath;
public override void _Ready()
{
_default3DMapRid = GetWorld3D().NavigationMap;
}
private void SetMovementTarget(Vector3 targetPosition)
{
Vector3 startPosition = GlobalTransform.Origin;
_currentPath = NavigationServer3D.MapGetPath(_default3DMapRid, startPosition, targetPosition, true);
if (!_currentPath.IsEmpty())
{
_currentPathIndex = 0;
_currentPathPoint = _currentPath[0];
}
}
public override void _PhysicsProcess(double delta)
{
if (_currentPath.IsEmpty())
{
return;
}
_movementDelta = _movementSpeed * (float)delta;
if (GlobalTransform.Origin.DistanceTo(_currentPathPoint) <= _pathPointMargin)
{
_currentPathIndex += 1;
if (_currentPathIndex >= _currentPath.Length)
{
_currentPath = Array.Empty<Vector3>();
_currentPathIndex = 0;
_currentPathPoint = GlobalTransform.Origin;
return;
}
}
_currentPathPoint = _currentPath[_currentPathIndex];
Vector3 newVelocity = GlobalTransform.Origin.DirectionTo(_currentPathPoint) * _movementDelta;
var globalTransform = GlobalTransform;
globalTransform.Origin = globalTransform.Origin.MoveToward(globalTransform.Origin + newVelocity, _movementDelta);
GlobalTransform = globalTransform;
}
}