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 positions
var 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)]