使用 NavigationPathQueryObject
小技巧
Path query parameters expose various options to improve pathfinding performance or lower memory consumption.
They cater to more advanced pathfinding needs that the high-level nodes can not always cover.
See the respective option sections below.
NavigationPathQueryObjects can be used together with NavigationServer.query_path()
to obtain a heavily customized navigation path including optional metadata about the path.
与获取正常的NavigationPath相比,这需要更多的设定,但可以让你根据项目的不同需求定制寻路并提供路径数据。
NavigationPathQueryObjects consist of a pair of objects, a NavigationPathQueryParameters object holding the customization options
for the query and a NavigationPathQueryResult that receives (regular) updates with the resulting path and metadata from the query.
NavigationPathQueryParameters 的 2D 和 3D 版本分别为 NavigationPathQueryParameters2D 和 NavigationPathQueryParameters3D。
NavigationPathQueryResult 的 2D 和 3D 版本分别为 NavigationPathQueryResult2D 和 NavigationPathQueryResult3D。
创建基础路径查询
参数和结果都与 NavigationServer.query_path() 函数成对使用。
For the available customization options, see further below. See also the descriptions for each parameter in the class reference.
While not a strict requirement, both objects are intended to be created once in advance, stored in a persistent variable for the agent and reused for every followup path query with updated parameters.
Reusing the same objects improves performance when frequently creating objects or allocating memory.
The following script creates the objects and provides a query_path() function to create new navigation paths.
The resulting path is identical to using NavigationServer.map_get_path() while reusing the objects.
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
路径后期处理选项
Path post-processing differences depending on navigation mesh polygon layout.
A path query search travels from the closest navigation mesh polygon edge to the closest edge along the available polygons. If possible it builds a polygon corridor towards the target position polygon.
原始"搜索"多边形走廊路径未充分优化,通常不适合智能体移动(例如导航网格多边形上的最近边缘点可能导致智能体在大多边形上绕行)。查询提供的多种 path_postprocessing 选项可提升路径质量。
PATH_POSTPROCESSING_CORRIDORFUNNEL后处理通过**在可用多边形走廊内**沿角落收缩路径来缩短路径。这是默认的后处理方法,通常也最实用,因为它提供了**可用多边形走廊内**的最短路径结果。如果多边形走廊本身不理想(例如由于导航网格布局欠佳),漏斗可能吸附到意外的多边形角落导致绕行。
PATH_POSTPROCESSING_EDGECENTERED后处理强制所有路径点位于**可用多边形走廊内**被跨越多边形边缘的中点。This post-processing is usually only useful when used with strictly tile-like navigation mesh polygons that are all evenly sized and where the expected path following is also constrained to cell centers, e.g. typical grid game with movement constrained to grid cell centers.
The
PATH_POSTPROCESSING_NONEpost-processing returns the path as is how the pathfinding traveled inside the available polygon corridor.This post-processing is very useful for debug as it shows how the path search traveled from closest edge point to closet edge point and what polygons it picked. A lot of unexpected or suboptimal path results can be immediately explained by looking at this raw path and polygon corridor.
路径简化
小技巧
Path simplification can help steering agents or agents that jitter on thin polygon edges.
Path point difference with or without path simplification.
If simplify_path is enabled a variant of the Ramer-Douglas-Peucker path simplification algorithm is applied to the path.
This algorithm straightens paths by removing less relevant path points depending on the simplify_epsilon used.
Path simplification helps with all kinds of agent movement problems in "open fields" that are caused by having many unnecessary polygon edges. E.g. a terrain mesh when baked to a navigation mesh can cause an excessive polygon count due to all the small (but for pathfinding almost meaningless) height variations in the terrain.
Path simplification also helps with "steering" agents because they only have more critical corner path points to aim for.
警告
Path simplification is an additional final post-processing of the path. It adds extra performance costs to the query so only enable when actually needed.
备注
Path simplification is exposed on the NavigationServer as a generic function. It can be used outside of navigation queries for all kinds of position arrays as well.
路径元数据
小技巧
禁用不必要的路径元数据选项可提升性能并减少内存占用。
路径查询可为每个路径点返回额外元数据。
The
PATH_METADATA_INCLUDE_TYPESflag collects an array with the primitive information about the point owners, e.g. if a point belongs to a region or link.The
PATH_METADATA_INCLUDE_RIDSflag collects an array with the RIDs of the point owners. Depending on point owner primitive, these RIDs can be used with the various NavigationServer functions related to regions or links.The
PATH_METADATA_INCLUDE_OWNERSflag collects an array with theObjectIDsof the point owners. These object IDs can be used with @GlobalScope.instance_from_id() to retrieve the node behind that object instance, e.g. a NavigationRegion or NavigationLink node.
By default all path metadata is collected as this metadata can be essential for more advanced navigation gameplay.
E.g. to know what path point maps to what object or node owner inside the SceneTree.
E.g. to know if a path point is the start or end of a navigation link that requires scripted takeover.
基础路径功能通常无需元数据。可选择禁用元数据收集以提升性能并降低内存消耗。
区块的排除与包含
小技巧
Region filters can greatly help with performance on large navigation maps that are region partitioned.
Query parameters allow limiting the pathfinding to specific region navigation meshes.
若大型导航地图被合理分区为较小区域,可大幅提升性能(路径搜索初期即可跳过大量多边形检查)。
By default and if left empty all regions of the queried navigation map are included.
If a region RID is added to the
excluded_regionsarray the region's navigation mesh will be ignored in the path search.If a region RID is added to the
included_regionsarray the region's navigation mesh will be considered in the path search and also all other regions not included will be ignored as well.If a region ends up both included and excluded it is considered excluded.
Region filters are very effective for performance when paired with navigation region chunks that are aligned on a grid. This way the filter can be set to only include the start position chunk and surrounding chunks instead of the entire navigation map.
Even if the target might be outside these surrounding chunks (can always add more "rings") the pathfinding will try to create a path to the polygon closest to the target. This usually creates half-paths heading in the general direction that are good enough, all for a fraction of the performance cost of a full map search.
The following addition to the basic path query script showcases the idea how to integrate a region chunk mapping with the region filters. This is not a full working example.
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
# ...
路径的裁剪与限制
小技巧
Sensibly set limits can greatly help with performance on large navigation maps, especially when targets end up being unreachable.
Clipping returned paths to specific distances.
Query parameters allow clipping returned paths to specific lengths. These options clip the path as a part of post-processing. The path is still searched as if at full length, so it will have the same quality. Path length clipping can be helpful in creating paths that better fit constrained gameplay, e.g. tactical games with limited movement ranges.
The
path_return_max_lengthproperty can be used to clip the returned path to a specific max length.The
path_return_max_radiusproperty can be used to clip the returned path inside a circle (2D) or sphere (3D) radius around the start position.
Query parameters allow limiting the path search to only search up to a specific distance or a specific number of searched polygons. These options are for performance and affect the path search directly.
The
path_search_max_distanceproperty can be used to stop the path search when going over this distance from the start position.The
path_search_max_polygonsproperty can be used to stop the path search when going over this searched polygon number.
When the path search is stopped by reaching a limit the path resets and creates a path from the start position polygon to the polygon found so far that is closest to the target position.
警告
While good for performance, if path search limit values are set too low they can affect the path quality very negatively. Depending on polygon layout and search pattern the returned paths might go into completely wrong directions instead of the direction of the target.