使用 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

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)