Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
使用 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;
}
}