Использование NavigationPaths

Получение NavigationPath

Навигационные пути можно напрямую запрашивать из NavigationServer, для этого не требуются никакие дополнительные узлы или объекты, если на навигационной карте имеется навигационная сетка для работы.

Чтобы получить 2D-путь, используйте NavigationServer2D.map_get_path(map, from, to, optimize, navigation_layers).

Чтобы получить 3D-путь, используйте NavigationServer3D.map_get_path(map, from, to, optimize, navigation_layers).

Для получения дополнительных настраиваемых запросов пути навигации, требующих дополнительной настройки, см. Использовать NavigationPathQueryObjects.

Одним из обязательных параметров запроса является RID навигационной карты. Для каждого игрового мира автоматически создается навигационная карта по умолчанию. Навигационные карты по умолчанию можно получить с помощью метода get_world_2d().get_navigation_map() из любого наследуемого узла Node2D или get_world_3d().get_navigation_map() из любого наследуемого узла Node3D. Второй и третий параметры — начальная и конечная позиции, например Vector2 для 2D или Vector3 для 3D.

Если параметр 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 будет PackedVector2Array для 2D или PackedVector3Array для 3D. Это всего лишь оптимизированный для памяти Array векторных позиций. Все векторы позиций внутри массива гарантированно находятся внутри 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)