godot-navigation-pathfinding

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Navigation & Pathfinding

导航与寻路

NavigationServer-powered pathfinding with avoidance and dynamic obstacles define robust AI movement.
基于NavigationServer的寻路系统结合避障与动态障碍物处理,可实现稳定的AI移动。

Available Scripts

可用脚本

dynamic_nav_manager.gd

dynamic_nav_manager.gd

Expert runtime navigation mesh updates for moving platforms.
用于移动平台的专家级运行时导航网格更新脚本。

NEVER Do in Navigation & Pathfinding

导航与寻路的禁忌操作

  • NEVER set
    target_position
    before awaiting physics frame
    — NavigationServer not ready in
    _ready()
    ? Path fails silently. MUST
    call_deferred()
    then
    await get_tree().physics_frame
    .
  • NEVER use
    NavigationRegion2D.bake_navigation_polygon()
    at runtime
    — Synchronous baking freezes game for 100+ ms. Use
    NavigationServer2D
    for dynamic updates OR pre-bake.
  • NEVER forget to check
    is_navigation_finished()
    — Calling
    get_next_path_position()
    after reaching target = stale path, AI walks to old position.
  • NEVER use
    avoidance_enabled
    without setting radius
    — Default radius = 0, agent passes through others. Set
    nav_agent.radius = collision_shape.radius
    for proper avoidance.
  • NEVER poll
    target_position
    every frame for chase AI
    — Setting target 60x/sec = path recalculation spam. Use timer (0.2s intervals) or distance threshold for updates.
  • NEVER assume path exists — Target unreachable (blocked by walls)?
    get_next_path_position()
    returns invalid. Check
    is_target_reachable()
    or validate path length.

  • 绝对不要在等待物理帧前设置
    target_position
    — 如果在
    _ready()
    中NavigationServer未就绪,路径会静默失败。必须先调用
    call_deferred()
    ,再
    await get_tree().physics_frame
  • 绝对不要在运行时调用
    NavigationRegion2D.bake_navigation_polygon()
    — 同步烘焙会导致游戏冻结100毫秒以上。请使用
    NavigationServer2D
    进行动态更新,或者提前烘焙。
  • 绝对不要忘记检查
    is_navigation_finished()
    — 到达目标后调用
    get_next_path_position()
    会获取到失效路径,导致AI走到旧位置。
  • 绝对不要在未设置半径的情况下启用
    avoidance_enabled
    — 默认半径为0,Agent会直接穿过其他对象。请设置
    nav_agent.radius = collision_shape.radius
    以实现正确的避障。
  • 绝对不要为追逐型AI每帧轮询
    target_position
    — 每秒设置60次目标会导致路径重计算泛滥。请使用计时器(0.2秒间隔)或距离阈值来触发更新。
  • 绝对不要假设路径一定存在 — 如果目标被墙壁阻挡无法到达,
    get_next_path_position()
    会返回无效值。请检查
    is_target_reachable()
    或验证路径长度。

2D Navigation

2D导航

gdscript
undefined
gdscript
undefined

Scene structure:

Scene structure:

Node2D (Level)

Node2D (Level)

├─ NavigationRegion2D

├─ NavigationRegion2D

│ └─ Polygon2D (draw walkable area)

│ └─ Polygon2D (draw walkable area)

└─ CharacterBody2D (Enemy)

└─ CharacterBody2D (Enemy)

└─ NavigationAgent2D

└─ NavigationAgent2D


**Setup NavigationRegion2D:**
1. Add `NavigationRegion2D` node
2. Create **New NavigationPolygon**
3. Click "Edit" → Draw walkable area
4. Bake navigation mesh

**设置NavigationRegion2D:**
1. 添加`NavigationRegion2D`节点
2. 创建**新的NavigationPolygon**
3. 点击“编辑” → 绘制可行走区域
4. 烘焙导航网格

Basic AI Movement

基础AI移动

gdscript
extends CharacterBody2D

@onready var nav_agent := $NavigationAgent2D
@export var speed := 200.0

var target_position: Vector2

func _ready() -> void:
    # Wait for navigation to be ready
    call_deferred("setup_navigation")

func setup_navigation() -> void:
    await get_tree().physics_frame
    nav_agent.target_position = target_position

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return
    
    var next_position := nav_agent.get_next_path_position()
    var direction := (next_position - global_position).normalized()
    
    velocity = direction * speed
    move_and_slide()

func set_target(pos: Vector2) -> void:
    target_position = pos
    nav_agent.target_position = pos
gdscript
extends CharacterBody2D

@onready var nav_agent := $NavigationAgent2D
@export var speed := 200.0

var target_position: Vector2

func _ready() -> void:
    # Wait for navigation to be ready
    call_deferred("setup_navigation")

func setup_navigation() -> void:
    await get_tree().physics_frame
    nav_agent.target_position = target_position

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return
    
    var next_position := nav_agent.get_next_path_position()
    var direction := (next_position - global_position).normalized()
    
    velocity = direction * speed
    move_and_slide()

func set_target(pos: Vector2) -> void:
    target_position = pos
    nav_agent.target_position = pos

NavigationAgent Properties

NavigationAgent属性

gdscript
undefined
gdscript
undefined

Path recalculation

Path recalculation

nav_agent.path_desired_distance = 10.0 nav_agent.target_desired_distance = 10.0
nav_agent.path_desired_distance = 10.0 nav_agent.target_desired_distance = 10.0

Avoidance

Avoidance

nav_agent.radius = 20.0 nav_agent.avoidance_enabled = true
nav_agent.radius = 20.0 nav_agent.avoidance_enabled = true

Performance

Performance

nav_agent.path_max_distance = 500.0
undefined
nav_agent.path_max_distance = 500.0
undefined

Advanced Patterns

高级模式

Chase Player

追逐玩家

gdscript
extends CharacterBody2D

@onready var nav_agent := $NavigationAgent2D
@export var speed := 150.0
@export var chase_range := 300.0

var player: Node2D

func _physics_process(delta: float) -> void:
    if not player:
        return
    
    var distance := global_position.distance_to(player.global_position)
    
    if distance <= chase_range:
        nav_agent.target_position = player.global_position
        
        if not nav_agent.is_navigation_finished():
            var next_pos := nav_agent.get_next_path_position()
            var direction := (next_pos - global_position).normalized()
            velocity = direction * speed
            move_and_slide()
gdscript
extends CharacterBody2D

@onready var nav_agent := $NavigationAgent2D
@export var speed := 150.0
@export var chase_range := 300.0

var player: Node2D

func _physics_process(delta: float) -> void:
    if not player:
        return
    
    var distance := global_position.distance_to(player.global_position)
    
    if distance <= chase_range:
        nav_agent.target_position = player.global_position
        
        if not nav_agent.is_navigation_finished():
            var next_pos := nav_agent.get_next_path_position()
            var direction := (next_pos - global_position).normalized()
            velocity = direction * speed
            move_and_slide()

Patrol Points

巡逻点

gdscript
extends CharacterBody2D

@onready var nav_agent := $NavigationAgent2D
@export var patrol_points: Array[Vector2] = []
@export var speed := 100.0

var current_point_index := 0

func _ready() -> void:
    if patrol_points.size() > 0:
        nav_agent.target_position = patrol_points[0]

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        _go_to_next_patrol_point()
        return
    
    var next_pos := nav_agent.get_next_path_position()
    var direction := (next_pos - global_position).normalized()
    velocity = direction * speed
    move_and_slide()

func _go_to_next_patrol_point() -> void:
    current_point_index = (current_point_index + 1) % patrol_points.size()
    nav_agent.target_position = patrol_points[current_point_index]
gdscript
extends CharacterBody2D

@onready var nav_agent := $NavigationAgent2D
@export var patrol_points: Array[Vector2] = []
@export var speed := 100.0

var current_point_index := 0

func _ready() -> void:
    if patrol_points.size() > 0:
        nav_agent.target_position = patrol_points[0]

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        _go_to_next_patrol_point()
        return
    
    var next_pos := nav_agent.get_next_path_position()
    var direction := (next_pos - global_position).normalized()
    velocity = direction * speed
    move_and_slide()

func _go_to_next_patrol_point() -> void:
    current_point_index = (current_point_index + 1) % patrol_points.size()
    nav_agent.target_position = patrol_points[current_point_index]

3D Navigation

3D导航

gdscript
extends CharacterBody3D

@onready var nav_agent := $NavigationAgent3D
@export var speed := 5.0

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return
    
    var next_position := nav_agent.get_next_path_position()
    var direction := (next_position - global_position).normalized()
    
    velocity = direction * speed
    move_and_slide()
gdscript
extends CharacterBody3D

@onready var nav_agent := $NavigationAgent3D
@export var speed := 5.0

func _physics_process(delta: float) -> void:
    if nav_agent.is_navigation_finished():
        return
    
    var next_position := nav_agent.get_next_path_position()
    var direction := (next_position - global_position).normalized()
    
    velocity = direction * speed
    move_and_slide()

Dynamic Obstacles

动态障碍物

gdscript
undefined
gdscript
undefined

Add NavigationObstacle2D to moving objects

Add NavigationObstacle2D to moving objects

Scene:

Scene:

CharacterBody2D (MovingPlatform)

CharacterBody2D (MovingPlatform)

└─ NavigationObstacle2D

└─ NavigationObstacle2D

Navigation automatically updates around it

Navigation automatically updates around it

undefined
undefined

Signals

信号

gdscript
func _ready() -> void:
    nav_agent.velocity_computed.connect(_on_velocity_computed)
    nav_agent.navigation_finished.connect(_on_navigation_finished)

func _on_velocity_computed(safe_velocity: Vector2) -> void:
    velocity = safe_velocity
    move_and_slide()

func _on_navigation_finished() -> void:
    print("Reached destination")
gdscript
func _ready() -> void:
    nav_agent.velocity_computed.connect(_on_velocity_computed)
    nav_agent.navigation_finished.connect(_on_navigation_finished)

func _on_velocity_computed(safe_velocity: Vector2) -> void:
    velocity = safe_velocity
    move_and_slide()

func _on_navigation_finished() -> void:
    print("Reached destination")

Best Practices

最佳实践

1. Defer Navigation Setup

1. 延迟导航设置

gdscript
undefined
gdscript
undefined

✅ Good - wait for navigation to initialize

✅ Good - wait for navigation to initialize

func _ready() -> void: call_deferred("setup_nav")
func setup_nav() -> void: await get_tree().physics_frame nav_agent.target_position = target
undefined
func _ready() -> void: call_deferred("setup_nav")
func setup_nav() -> void: await get_tree().physics_frame nav_agent.target_position = target
undefined

2. Check if Path Exists

2. 检查路径是否存在

gdscript
if not nav_agent.is_target_reachable():
    print("Target unreachable!")
gdscript
if not nav_agent.is_target_reachable():
    print("Target unreachable!")

3. Use Avoidance for Crowds

3. 为人群使用避障功能

gdscript
nav_agent.avoidance_enabled = true
nav_agent.radius = 20.0
nav_agent.max_neighbors = 10
gdscript
nav_agent.avoidance_enabled = true
nav_agent.radius = 20.0
nav_agent.max_neighbors = 10

Reference

参考资料

Related

相关内容

  • Master Skill: godot-master
  • 大师技能:godot-master