Navigation System
NavigationAgent3D
Basic Pathfinding
extends CharacterBody3D
@onready var nav_agent = $NavigationAgent3D
@export var speed = 5.0
@export var target: Node3D
func _ready():
nav_agent.path_desired_distance = 0.5
nav_agent.target_desired_distance = 1.0
nav_agent.radius = 0.5
nav_agent.height = 1.8
nav_agent.set_navigation_map(get_world_3d().navigation_map)
func _physics_process(delta):
if target:
nav_agent.target_position = target.global_position
if nav_agent.is_navigation_finished():
return
var next_pos = nav_agent.get_next_path_position()
var direction = (next_pos - global_position).normalized()
direction.y = 0
velocity = direction * speed
if velocity.length() > 0:
look_at(global_position + velocity)
move_and_slide()
NavigationRegion3D
Setup in Editor
- Add NavigationRegion3D node
- Create NavigationMesh resource
- Bake navigation mesh
Runtime Baking
extends Node3D
@onready var nav_region = $NavigationRegion3D
func _ready():
NavigationServer3D.region_bake_navigation_mesh(
nav_region.navigation_mesh,
get_world_3d()
)
NavigationAgent2D
2D Pathfinding
extends CharacterBody2D
@onready var nav_agent = $NavigationAgent2D
@export var speed = 200.0
@export var target_position: Vector2
func _ready():
nav_agent.target_position = target_position
func _physics_process(delta):
if nav_agent.is_navigation_finished():
return
var next_pos = nav_agent.get_next_path_position()
var direction = (next_pos - global_position).normalized()
velocity = direction * speed
move_and_slide()
AI Patrol System
extends CharacterBody3D
@export var patrol_points: Array[Vector3] = []
@export var speed = 3.0
@export var wait_time = 2.0
@onready var nav_agent = $NavigationAgent3D
@onready var wait_timer = $Timer
var current_point_index = 0
var is_waiting = false
func _ready():
wait_timer.wait_time = wait_time
wait_timer.one_shot = true
wait_timer.timeout.connect(_on_wait_finished)
if patrol_points.size() > 0:
nav_agent.target_position = patrol_points[0]
func _physics_process(delta):
if is_waiting:
return
if nav_agent.is_navigation_finished():
start_waiting()
return
var next_pos = nav_agent.get_next_path_position()
var direction = (next_pos - global_position).normalized()
direction.y = 0
velocity = direction * speed
move_and_slide()
func start_waiting():
is_waiting = true
wait_timer.start()
func _on_wait_finished():
is_waiting = false
current_point_index = (current_point_index + 1) % patrol_points.size()
nav_agent.target_position = patrol_points[current_point_index]
Obstacle Avoidance
extends CharacterBody3D
@onready var nav_agent = $NavigationAgent3D
func _ready():
nav_agent.avoidance_enabled = true
nav_agent.neighbor_distance = 10.0
nav_agent.max_neighbors = 10
nav_agent.time_horizon = 2.0
nav_agent.avoidance_layers = 1
nav_agent.avoidance_mask = 1
func _physics_process(delta):
nav_agent.target_position = target.global_position
var next_pos = nav_agent.get_next_path_position()
var direction = (next_pos - global_position).normalized()
velocity = direction * speed
move_and_slide()
Dynamic Navigation
Adding Obstacles at Runtime
extends Node3D
@onready var nav_region = $NavigationRegion3D
func add_obstacle(position: Vector3, size: Vector3):
var obstacle = NavigationObstacle3D.new()
obstacle.position = position
obstacle.radius = size.x / 2
obstacle.height = size.y
add_child(obstacle)
nav_region.bake_navigation_mesh()
Best Practices
- Use NavigationAgent: Handles pathfinding automatically
- Bake nav mesh: For static environments
- Dynamic obstacles: Use NavigationObstacle3D
- Agent radius: Set correctly for obstacle avoidance
- Path smoothing: Interpolate between path points
- Multiple layers: For different movement types (ground, water, air)