Использовать NavigationPathQueryObjects
Совет
Параметры запроса пути предоставляют различные возможности для повышения производительности поиска пути или снижения потребления памяти.
Они удовлетворяют более продвинутые потребности в поиске пути, которые не всегда могут удовлетворить узлы высокого уровня.
См. соответствующие разделы опций ниже.
NavigationPathQueryObjects можно использовать вместе с NavigationServer.query_path() для получения в значительной степени настроенного пути навигации, включая необязательные метаданные о пути.
Это требует больше настроек по сравнению с получением обычного NavigationPath, но позволяет адаптировать поиск пути и предоставляемые данные о пути к различным потребностям проекта.
NavigationPathQueryObjects состоит из пары объектов: объекта NavigationPathQueryParameters, содержащего параметры настройки запроса, и NavigationPathQueryResult, который получает (регулярные) обновления с полученным путем и метаданными из запроса.
2D- и 3D-версии NavigationPathQueryParameters доступны как NavigationPathQueryParameters2D и NavigationPathQueryParameters3D соответственно.
2D- и 3D-версии NavigationPathQueryResult доступны как NavigationPathQueryResult2D и NavigationPathQueryResult3D соответственно.
Создание базового запроса пути
Оба параметра и результат используются в паре с функцией NavigationServer.query_path().
Доступные параметры настройки см. ниже. См. также описания каждого параметра в справочнике классов.
Хотя это и не является строгим требованием, оба объекта должны быть созданы один раз заранее, сохранены в постоянной переменной для агента и повторно использованы для каждого последующего запроса пути с обновленными параметрами.
Повторное использование одних и тех же объектов повышает производительность при частом создании объектов или выделении памяти.
Следующий скрипт создаёт объекты и предоставляет функцию query_path() для создания новых навигационных путей. Результирующий путь идентичен пути, полученному при использовании NavigationServer.map_get_path() при повторном использовании объектов.
extends Node2D
# Prepare query objects.
var query_parameters := NavigationPathQueryParameters2D.new()
var query_result := NavigationPathQueryResult2D.new()
func query_path(p_start_position: Vector2, p_target_position: Vector2, p_navigation_layers: int = 1) -> PackedVector2Array:
if not is_inside_tree():
return PackedVector2Array()
var map: RID = get_world_2d().get_navigation_map()
if NavigationServer2D.map_get_iteration_id(map) == 0:
# This map has never synced and is empty, no point in querying it.
return PackedVector2Array()
query_parameters.map = map
query_parameters.start_position = p_start_position
query_parameters.target_position = p_target_position
query_parameters.navigation_layers = p_navigation_layers
NavigationServer2D.query_path(query_parameters, query_result)
var path: PackedVector2Array = query_result.get_path()
return path
extends Node3D
# Prepare query objects.
var query_parameters := NavigationPathQueryParameters3D.new()
var query_result := NavigationPathQueryResult3D.new()
func query_path(p_start_position: Vector3, p_target_position: Vector3, p_navigation_layers: int = 1) -> PackedVector3Array:
if not is_inside_tree():
return PackedVector3Array()
var map: RID = get_world_3d().get_navigation_map()
if NavigationServer3D.map_get_iteration_id(map) == 0:
# This map has never synced and is empty, no point in querying it.
return PackedVector3Array()
query_parameters.map = map
query_parameters.start_position = p_start_position
query_parameters.target_position = p_target_position
query_parameters.navigation_layers = p_navigation_layers
NavigationServer3D.query_path(query_parameters, query_result)
var path: PackedVector3Array = query_result.get_path()
return path
Параметры постобработки пути
Различия в постобработке пути в зависимости от компоновки полигонов навигационной сетки.
Поиск пути осуществляется от ближайшего ребра полигона навигационной сетки к ближайшему ребру вдоль доступных полигонов. При возможности строится полигональный коридор к полигону целевой позиции.
Этот исходный путь коридора полигона "search" (поиска) не очень оптимизирован и обычно плохо подходит для перемещения агентов. Например, ближайшая точка границы полигона навигационной сетки может привести к значительному отклонению агентов на более крупных полигонах. Для повышения качества путей, возвращаемых запросом, существуют различные параметры path_postprocessing.
Постобработка
PATH_POSTPROCESSING_CORRIDORFUNNELсокращает пути, направляя их вокруг углов внутри доступного полигонального коридора.Это постобработка по умолчанию, которая обычно наиболее полезна, поскольку она даёт кратчайший путь внутри доступного полигонального коридора. Если полигональный коридор уже неоптимален, например, из-за неоптимальной компоновки навигационной сетки, воронка может застревать в неожиданных углах полигона, что приведёт к отклонениям.
Постобработка
PATH_POSTPROCESSING_EDGECENTEREDзаставляет все точки пути размещаться посередине пересеченных ребер полигона внутри доступного коридора полигона.Такая постобработка обычно полезна только при использовании со строго плиточными полигонами навигационной сетки, имеющими одинаковый размер и где ожидаемое следование по пути также ограничено центрами ячеек, например, типичная сеточная игра с движением, ограниченным центрами ячеек сетки.
Постобработка
PATH_POSTPROCESSING_NONEвозвращает путь таким, каким он был при поиске пути внутри доступного полигонального коридора.Эта постобработка очень полезна для отладки, поскольку показывает, как поиск пути проходил от ближайшей точки края до ближайшей точки края и какие полигоны были выбраны. Многие неожиданные или неоптимальные результаты поиска пути можно сразу объяснить, взглянув на этот исходный путь и коридор полигонов.
Упрощение пути
Совет
Упрощение пути может помочь в управлении агентами или агентами, которые колеблются на тонких гранях полигонов.
Разница точек пути с упрощением пути или без него.
Если параметр simplify_path включён, к пути применяется вариант алгоритма упрощения пути Рамера-Дугласа-Пейкера (Ramer-Douglas-Peucker). Этот алгоритм выпрямляет пути, удаляя менее важные точки в зависимости от используемого параметра simplify_epsilon.
Упрощение пути помогает решить все проблемы движения агентов в "открытых пространствах", возникающие из-за большого количества ненужных рёбер полигонов. Например, сетка рельефа, запечённая в навигационной сетке, может привести к чрезмерному количеству полигонов из-за небольших (но практически не имеющих значения для поиска пути) перепадов высот рельефа.
Упрощение пути также помогает "управляющим" агентам, поскольку им нужно стремиться только к более критическим угловым точкам пути.
Предупреждение
Упрощение пути — это дополнительная финальная обработка пути. Оно увеличивает производительность запроса, поэтому используйте его только при действительной необходимости.
Примечание
Упрощение пути реализовано в NavigationServer как универсальная функция. Её можно использовать и вне навигационных запросов для всех типов массивов позиций.
Метаданные пути
Совет
Отключение ненужных параметров метаданных пути может повысить производительность и снизить потребление памяти.
Запрос пути может возвращать дополнительные метаданные для каждой точки пути.
Флаг
PATH_METADATA_INCLUDE_TYPESсобирает массив с примитивной информацией о владельцах точек, например, принадлежит ли точка региону или ссылке.Флаг
PATH_METADATA_INCLUDE_RIDSсобирает массив с RIDs владельцев точек. В зависимости от примитива владельца точки, эти RID могут использоваться с различными функциями NavigationServer, связанными с регионами или ссылками.Флаг
PATH_METADATA_INCLUDE_OWNERSсобирает массив сObjectID-амивладельцев точек. Эти идентификаторы объектов можно использовать с @GlobalScope.instance_from_id() для получения узла, лежащего в основе этого экземпляра объекта, например, узла NavigationRegion или NavigationLink.
По умолчанию собираются все метаданные пути, так как эти метаданные могут быть необходимы для более продвинутого навигационного игрового процесса.
Например, чтобы узнать, какая точка пути соответствует какому объекту или владельцу узла внутри SceneTree.
Например, чтобы узнать, является ли точка пути началом или концом навигационной ссылки, требующей скриптового захвата.
Для большинства базовых задач метаданные пути не всегда необходимы. Сбор метаданных пути можно выборочно отключить для повышения производительности и снижения потребления памяти.
Исключая или включая регионы
Совет
Фильтры регионов могут существенно повысить производительность на больших навигационных картах, разделенных на регионы.
Параметры запроса позволяют ограничить поиск пути определенными навигационными сетками региона.
Если большая навигационная карта хорошо разделена на более мелкие регионы, это может существенно повысить производительность, поскольку запрос может пропустить большое количество полигонов на одной из самых ранних проверок при поиске пути.
По умолчанию и если оставить поле пустым, включаются все регионы запрашиваемой навигационной карты.
Если регион RID добавлен в массив
excluded_regions, навигационная сетка региона будет игнорироваться при поиске пути.Если регион RID добавлен в массив
included_regions, то навигационная сетка региона будет учитываться при поиске пути, а все остальные невключенные регионы также будут игнорироваться.Если регион одновременно включен и исключен, он считается исключенным.
Фильтры регионов очень эффективны для повышения производительности в сочетании с фрагментами навигационных регионов, выровненными по сетке. Таким образом, фильтр можно настроить так, чтобы он включал только фрагмент начальной позиции и окружающие его фрагменты, а не всю навигационную карту.
Даже если цель находится за пределами этих окружающих фрагментов (всегда можно добавить дополнительные "кольца"), поиск пути попытается проложить путь к ближайшему к цели полигону. Обычно это создаёт полу-пути, идущие в общем направлении, которые достаточно хороши, и всё это за малую долю производительности по сравнению с полным поиском по карте.
Следующее дополнение к базовому скрипту запроса пути демонстрирует идею интеграции сопоставления фрагментов региона с фильтрами регионов. Это не полноценный рабочий пример.
extends Node2D
# ...
var chunk_id_to_region_rid: Dictionary[Vector2i, RID] = {}
func query_path(p_start_position: Vector2, p_target_position: Vector2, p_navigation_layers: int = 1) -> PackedVector2Array:
# ...
var regions_around_start_position: Array[RID] = []
var chunk_rings: int = 1 # Increase for very small regions or more quality.
var start_chunk_id: Vector2i = floor(p_start_position / float(chunk_size))
for y: int in range(start_chunk_id.y - chunk_rings, start_chunk_id.y + chunk_rings):
for x: int in range(start_chunk_id.x - chunk_rings, start_chunk_id.x + chunk_rings):
var chunk_id: Vector2i = Vector2i(x, y)
if chunk_id_to_region_rid.has(chunk_id):
var region: RID = chunk_id_to_region_rid[chunk_id]
regions_around_start_position.push_back(region)
query_parameters.included_regions = regions_around_start_position
# ...
extends Node3D
# ...
var chunk_id_to_region_rid: Dictionary[Vector3i, RID] = {}
func query_path(p_start_position: Vector3, p_target_position: Vector3, p_navigation_layers: int = 1) -> PackedVector3Array:
# ...
var regions_around_start_position: Array[RID] = []
var chunk_rings: int = 1 # Increase for very small regions or more quality.
var start_chunk_id: Vector3i = floor(p_start_position / float(chunk_size))
var y: int = 0 # Assume a planar navigation map for simplicity.
for z: int in range(start_chunk_id.z - chunk_rings, start_chunk_id.z + chunk_rings):
for x: int in range(start_chunk_id.x - chunk_rings, start_chunk_id.x + chunk_rings):
var chunk_id: Vector3i = Vector3i(x, y, z)
if chunk_id_to_region_rid.has(chunk_id):
var region: RID = chunk_id_to_region_rid[chunk_id]
regions_around_start_position.push_back(region)
query_parameters.included_regions = regions_around_start_position
# ...
Обрезка пути и ограничения
Совет
Разумно установленные ограничения могут существенно повысить производительность на больших навигационных картах, особенно когда цели оказываются недостижимыми.
Обрезка возвращала пути на определенные расстояния.
Параметры запроса позволяют обрезать возвращаемые пути до определённой длины. Эти параметры обрезают путь в ходе постобработки. Поиск пути по-прежнему выполняется так же, как если бы он был полной длины, поэтому его качество остаётся неизменным. Обрезание длины пути может быть полезно для создания путей, лучше соответствующих ограниченному игровому процессу, например, тактическим играм с ограниченной дальностью перемещения.
Свойство
path_return_max_lengthможно использовать для ограничения возвращаемого пути до определенной максимальной длины.Свойство
path_return_max_radiusможно использовать для обрезки возвращаемого пути внутри радиуса окружности (2D) или сферы (3D) вокруг начальной позиции.
Параметры запроса позволяют ограничить поиск пути, ограничив его определённым расстоянием или количеством искомых полигонов. Эти параметры предназначены для повышения производительности и напрямую влияют на поиск пути.
Свойство
path_search_max_distanceможно использовать для остановки поиска пути при выходе за указанное расстояние от начальной позиции.Свойство
path_search_max_polygonsможно использовать для остановки поиска пути при достижении указанного номера искомого полигона.
Когда поиск пути останавливается из-за достижения предела, путь сбрасывается и создается путь от начального многоугольника до найденного на данный момент многоугольника, наиболее близкого к целевой позиции.
Предупреждение
Хотя это и положительно сказывается на производительности, слишком низкие значения пределов поиска пути могут крайне негативно повлиять на качество. В зависимости от конфигурации полигона и шаблона поиска, возвращаемые пути могут идти в совершенно неверном направлении, а не в направлении цели.