Navigation & Pathfinding
Signals: path_changed, target_reached, navigation_finished, waypoint_reached, link_reached.
30.1 Navigation meshes and agents
Add a NavigationRegion2D/NavigationRegion3D and give it a navigation polygon/mesh (draw it, or bake it from colliders/geometry). TileSets can also define navigation polygons per tile.
Give each moving actor a NavigationAgent2D/3D child.
Set target_position, ask for get_next_path_position() every physics frame, move toward it.
Signals: path_changed, target_reached, navigation_finished, waypoint_reached, link_reached. Agents support avoidance (RVO) with radius, neighbor_distance, max_speed, and NavigationObstacle2D/3D. NavigationLink2D/3D connect separate regions (jumps, ladders, teleporters).
extends CharacterBody2D
@export var speed := 150.0
@export var target: Node2D
@onready var agent: NavigationAgent2D = $NavigationAgent2D
func _ready() -> void:
agent.path_desired_distance = 4.0
agent.target_desired_distance = 8.0
agent.velocity_computed.connect(_on_velocity_computed) # used when avoidance is enabled
_setup.call_deferred()
func _setup() -> void:
await get_tree().physics_frame # wait for the navigation map to sync on the first frame
agent.target_position = target.global_position
func _physics_process(_delta: float) -> void:
if target:
agent.target_position = target.global_position # re-pathing is throttled internally
if agent.is_navigation_finished():
return
var next := agent.get_next_path_position()
var desired := global_position.direction_to(next) * speed
if agent.avoidance_enabled:
agent.velocity = desired # result arrives via velocity_computed
else:
_on_velocity_computed(desired)
func _on_velocity_computed(safe_velocity: Vector2) -> void:
velocity = safe_velocity
move_and_slide()30.2 Baking at runtime
$NavigationRegion3D.bake_navigation_mesh() # 3D, re-bake after level changes (can run on a thread)
$NavigationRegion2D.bake_navigation_polygon() # 2D (4.2+ baking from source geometry)30.3 Querying paths directly
var map_rid := get_world_2d().navigation_map
var path: PackedVector2Array = NavigationServer2D.map_get_path(map_rid, start, goal, true)30.4 Grid pathfinding with AStarGrid2D
For tile-based games, AStarGrid2D is simple and fast:
set_point_weight_scale(cell, 3.0) makes swamps costlier. For arbitrary graphs (waypoints, hex grids, road networks) use AStar2D/AStar3D:
Godot 3 note: the Navigation2D node and get_simple_path() were replaced by NavigationServer2D/3D, regions, and agents.
var astar := AStarGrid2D.new()
func setup_grid(tiles: TileMapLayer) -> void:
astar.region = tiles.get_used_rect()
astar.cell_size = Vector2(16, 16)
astar.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES
astar.default_compute_heuristic = AStarGrid2D.HEURISTIC_MANHATTAN
astar.update() # must be called after changing region/cell_size
for cell in tiles.get_used_cells():
var data := tiles.get_cell_tile_data(cell)
if data and data.get_custom_data("solid"):
astar.set_point_solid(cell, true)
func find_path(from_cell: Vector2i, to_cell: Vector2i) -> Array[Vector2i]:
return astar.get_id_path(from_cell, to_cell) # cells; get_point_path() returns positionsvar graph := AStar2D.new()
graph.add_point(1, Vector2(0, 0))
graph.add_point(2, Vector2(100, 0))
graph.add_point(3, Vector2(100, 100))
graph.connect_points(1, 2)
graph.connect_points(2, 3)
print(graph.get_point_path(1, 3)) # [(0, 0), (100, 0), (100, 100)]