mcpField
A
ai-navigation

ai-navigation

github.com/jame581开发889180 次安装4 天前更新
代码仓库

关于 ai-navigation

面向 Godot 4.3+ 开发者的 AI 导航技能,涵盖 2D/3D 寻路、转向行为与巡逻模式,并提供 GDScript 和 C# 示例。还介绍行为树及追击、攻击状态组合。

主要功能

  1. 01NavigationAgent2D/3D 寻路
  2. 02寻路网格烘焙与导航分层
  3. 03Seek、Flee、Arrive 和 Wander 转向
  4. 04路点巡逻模式
  5. 05行为树与追击攻击模式

适用场景

  1. 01为 2D 或 3D 敌人设置路径移动
  2. 02在动态关卡中异步重建导航网格
  3. 03实现巡逻、追逐和攻击逻辑

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jame581/godotprompter --skill ai-navigation

安装后在 Claude Code 中输入/ai-navigation确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
async-baking.md
behavior-trees.md
chase-attack.md
navigation-agent-3d.md
patrol-patterns.md
steering-behaviors.md

详细介绍

Godot 4.3+ 中的 AI 导航

涵盖 NavigationAgent2D/3D、转向行为、行为树和巡逻模式。所有示例均面向 Godot 4.3+,不使用已弃用的 API。

相关技能: state-machine 用于管理 AI 状态,component-system 用于实现模块化 AI 行为,player-controller 用于了解移动物理模式,math-essentials 用于学习寻路向量和转向计算,limboai 用于通过可视化编辑器实现 BT + HSM,beehave 用于轻量级 GDScript 行为树。如需结构化的行为树(而非转向/导航),请参阅 limboai 和 beehave 中的对比表。


1. 导航设置

场景结构
World (Node2D or Node3D)
└── NavigationRegion2D (or NavigationRegion3D)
    ├── TileMapLayer / StaticBody2D (geometry)
    └── Enemy (CharacterBody2D with NavigationAgent2D child)
NavigationRegion2D / NavigationRegion3D
  1. 在场景中添加一个 NavigationRegion2D(或 NavigationRegion3D)节点。
  2. 为其指定 NavigationPolygon(2D)或 NavigationMesh(3D)资源。
  3. 在 NavigationPolygon 编辑器中绘制可行走区域,或在 3D 中配置 NavigationMesh 的边界。
  4. **在编辑时烘焙网格:**选择 NavigationRegion 节点 → 点击工具栏中的 Bake NavigationPolygon(2D)或 Bake NavigationMesh(3D)。
  5. 在运行时烘焙,适用于世界会动态变化的情况:
# 2D
$NavigationRegion2D.bake_navigation_polygon()

# 3D
$NavigationRegion3D.bake_navigation_mesh()
// 2D
GetNode<NavigationRegion2D>("NavigationRegion2D").BakeNavigationPolygon();

// 3D
GetNode<NavigationRegion3D>("NavigationRegion3D").BakeNavigationMesh();
异步导航烘焙

在大型地图上,导航烘焙可能导致帧率下降,因此默认情况下会在后台线程中烘焙区域:bake_navigation_polygon()(2D)和 bake_navigation_mesh()(3D)在未传入 false 时会使用 on_thread = true。连接区域的 bake_finished 信号(使用 CONNECT_ONE_SHOT),即可在网格准备就绪时获知。没有线程支持的 Web 构建无法使用线程烘焙。

请参阅 references/async-baking.md,了解完整的 GDScript 和 C# 后台线程烘焙示例(2D 和 3D)。

**适用异步烘焙的情况:**程序生成的关卡、可破坏地形,或任何需要在运行时重建导航网格的场景。网格烘焙期间,游戏仍会继续运行。

导航层

导航层可以将不同 Agent 类型(地面部队、飞行单位、大型敌人)的可行走区域分开。

# Assign layer bits on the NavigationRegion (Inspector or code)
# Layer 1 = ground, Layer 2 = air, Layer 3 = large

# On the NavigationAgent, set matching layers:
$NavigationAgent2D.navigation_layers = 1   # ground only
$NavigationAgent2D.navigation_layers = 2   # air only
$NavigationAgent2D.navigation_layers = 1 | 2  # both (bitwise OR)
// Assign layer bits on the NavigationRegion (Inspector or code)
// Layer 1 = ground, Layer 2 = air, Layer 3 = large

var navAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
navAgent.NavigationLayers = 1;       // ground only
navAgent.NavigationLayers = 2;       // air only
navAgent.NavigationLayers = 1 | 2;   // both (bitwise OR)

在 NavigationRegion 和 NavigationAgent 上设置 navigation_layers,确保两者相匹配。层不匹配是 Agent 找不到路径的最常见原因之一。


2. NavigationAgent2D 基本用法

GDScript
extends CharacterBody2D

@export var speed: float = 120.0

@onready var nav_agent: NavigationAgent2D = $NavigationAgent2D


func _ready() -> void:
	# velocity_computed fires when avoidance calculates a safe velocity
	nav_agent.velocity_computed.connect(_on_velocity_computed)


func _physics_process(delta: float) -> void:
	if nav_agent.is_navigation_finished():
		return

	var next_pos: Vector2 = nav_agent.get_next_path_position()
	var direction: Vector2 = (next_pos - global_position).normalized()
	var desired_velocity: Vector2 = direction * speed

	if nav_agent.avoidance_enabled:
		# Hand desired velocity to the avoidance system; wait for the signal
		nav_agent.velocity = desired_velocity
	else:
		velocity = desired_velocity
		move_and_slide()


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


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

NavigationAgent2D 的关键属性:

属性用途
target_position世界坐标中的目标位置
path_desired_distance判定到达每个路径点所需的接近距离(默认值为 20)
target_desired_distance判定到达最终目标所需的接近距离(默认值为 10)
avoidance_enabled启用 RVO 障碍物规避
radius用于规避的 Agent 碰撞半径
time_horizon_agents规避预测时长(秒),可调整以减少抖动
C#
using Godot;

public partial class Enemy2D : CharacterBody2D
{
    [Export] public float Speed { get; set; } = 120f;

    private NavigationAgent2D _navAgent;

    public override void _Ready()
    {
        _navAgent = GetNode<NavigationAgent2D>("NavigationAgent2D");
        _navAgent.VelocityComputed += OnVelocityComputed;
    }

    public override void _PhysicsProcess(double delta)
    {
        if (_navAgent.IsNavigationFinished()) return;

        Vector2 nextPos = _navAgent.GetNextPathPosition();
        Vector2 direction = (nextPos - GlobalPosition).Normalized();
        Vector2 desiredVelocity = direction * Speed;

        if (_navAgent.AvoidanceEnabled)
            _navAgent.Velocity = desiredVelocity;
        else
        {
            Velocity = desiredVelocity;
            MoveAndSlide();
        }
    }

    private void OnVelocityComputed(Vector2 safeVelocity)
    {
        Velocity = safeVelocity;
        MoveAndSlide();
    }

    public void SetTarget(Vector2 targetPos) => _navAgent.TargetPosition = targetPos;
}

3. NavigationAgent3D 基本用法

CharacterBody3D 每个物理帧都会调用 get_next_path_position() 来驱动 NavigationAgent3D,并将方向在 Y 轴上的分量设为零,同时单独处理重力。启用 avoidance_enabled 后,期望速度会传入 nav_agent.velocity,并在 velocity_computed 处理函数中移动;否则会直接调用 move_and_slide()。通过 target_position 设置目标,并使用 is_navigation_finished() 检查是否已完成导航。

请参阅 references/navigation-agent-3d.md,了解完整的 GDScript 和 C# NavigationAgent3D 敌人示例。


4. 转向行为

每帧进行轻量级计算(seek、flee、arrive、wander),无需导航网格即可实现自然的移动效果。可以将返回的向量相加来组合这些行为,也可以每次 _physics_process 调用时选取一个并赋给 velocity。

完整的 GDScript 和 C# 实现见 references/steering-behaviors.md,包括 seek、flee、arrive(带减速渐变)和 wander(带圆周投影抖动)。


5. 巡逻模式

使用 NavigationAgent2D、一组 Marker2D 路点,以及一个用于控制每个点停留时间的 Timer,即可实现简洁的巡逻循环。在 wait_timer.timeout 时循环递增索引,将 nav_agent.target_position 设为下一个路点,并通过 is_navigation_finished() 控制移动。

完整的 GDScript 和 C# 路点链巡逻实现见 references/patrol-patterns.md,其中包含计时器暂停。


6. 行为树概念

行为树(BT)是一种每个刻度都会评估节点的树结构。三种核心节点类型:

类型成功条件失败条件
Sequence所有子节点均成功(AND)任一子节点失败
Selector任一子节点成功(OR)所有子节点均失败
Action叶节点操作完成叶节点报告失败

Sequence 表示“依次执行 A、B、C”。Selector 表示“尝试 A,否则尝试 B,否则尝试 C”。

完整的轻量级 BT 实现(BTNode 基类 + Sequence / Selector / Action)以及一个使用“追逐 OR 巡逻”的敌人示例,均可见 references/behavior-trees.md,其中提供 GDScript 和 C# 两种实现。


7. 追逐 + 攻击模式

将 NavigationAgent2D 与状态机结合使用。完整的 FSM 基础设施见 state-machine 技能。

状态
状态进入条件退出条件
PATROL默认 / 玩家逃脱玩家进入 detect_range
CHASE玩家处于 detect_range 内玩家进入 attack_range 或玩家逃脱
ATTACK玩家处于 attack_range 内玩家离开 attack_range

完整的 GDScript 和 C# 实现见 references/chase-attack.md,包括 PATROL → CHASE → ATTACK 状态转换、攻击冷却计时器、巡逻路点推进,以及返回巡逻状态的逃脱范围交接。

对于较大的项目,可以使用 state-machine 技能将每个状态提取为独立的节点类,并从父节点注入 NavigationAgent2D 引用。


8. 专用 2D 导航服务器(Godot 4.5+)

在 Godot 4.5 之前,NavigationServer2D 只是一个轻量前端,所有工作都由内部的 3D 导航服务器处理。Godot 4.5 将两者拆分为完全独立的服务器。这一变化不会影响使用方式——无需更改 API,也无需迁移代码——但带来了两项实际好处:

  • 独立性: 2D 导航不再经由 3D 服务器运行;2D 地图、区域和 Agent 均由各自的实现处理。
  • 缩小仅含 2D 游戏的导出体积: 可以编译不包含 3D 导航服务器的自定义导出模板(disable_navigation_3d=yes),从而减小二进制文件大小。
# No code change needed — NavigationServer2D calls work identically.
# The split is internal; you continue using NavigationServer2D as before.

# Example: query a path directly via the server (unchanged API).
# Not named get_path_to: that is Node.get_path_to(node) and cannot be overridden with another signature.
func find_path_to(target: Vector2) -> PackedVector2Array:
    var map: RID = get_world_2d().get_navigation_map()
    return NavigationServer2D.map_get_path(
        map,
        global_position,
        target,
        true  # optimize path
    )
// No code change needed — NavigationServer2D calls work identically.
public Vector2[] FindPathTo(Vector2 target)
{
    Rid map = GetWorld2D().NavigationMap;
    return NavigationServer2D.MapGetPath(map, GlobalPosition, target, true);
}

仅含 2D 的项目: 标准导出模板仍包含 3D 导航。若要移除它,请使用 disable_navigation_3d=yes SCons 选项编译自定义导出模板(Godot 4.5+)。只有在项目中完全未使用任何 3D 导航节点(NavigationRegion3D、NavigationAgent3D)时,这样做才安全。


9. 常见问题

问题症状解决方法
未烘焙导航网格Agent 停在原地;找不到路径运行前烘焙 NavigationPolygon/NavigationMesh,或在场景加载后于运行时调用 bake_navigation_polygon()
agent_radius 过大Agent 无法通过门口或狭窄走廊降低 NavigationPolygon/NavigationMesh 上的 agent_radius 并重新烘焙——Agent 自身的 radius 仅用于避障,不影响寻路
避障抖动Agent 靠近其他 Agent 时会卡顿或来回摆动增大 time_horizon_agents(尝试 2–4 s),或略微降低 Agent 的 max_speed
路径重新计算过于频繁每帧 CPU 占用激增;Agent 变慢添加一个 Timer(0.2–0.5 s),仅在计时器触发时设置 target_position,而不是每个物理帧都设置
导航层错误Agent 忽略某些区域或找不到路径确认 NavigationRegion 和 NavigationAgent 的 navigation_layers 位掩码一致
NavigationServer 尚未就绪就设置了目标第一帧路径为空地图会在物理帧中首次同步——在 _ready() 中,设置 target_position 前使用 await get_tree().physics_frame(或 NavigationServer2D.map_changed)
3D 中未应用重力Agent 悬浮或陷入地面始终单独累加来自重力的 velocity.y;只将导航方向的 X/Z 分量归零
烘焙导致帧率下降烘焙被强制放到主线程上(on_thread = false),或正在解析非常大的场景保持默认的多线程烘焙(bake_navigation_mesh());连接 bake_finished,且不要阻塞等待烘焙完成

⚠️ Godot 4.7 中的变化: NavigationServer3D.map_get_closest_point_normal(map, to_point) 现在会返回一个归一化向量(GH-119022)——此前返回的表面法线可能未归一化。通过对结果调用 normalized() 来补偿的代码仍可正常运行;依赖未归一化向量的模长的代码则会出错。


10. 检查清单

  • 已将 NavigationRegion2D/3D 添加到场景,并为其配置 NavigationPolygon/NavigationMesh 资源
  • 已烘焙导航网格(在编辑时烘焙,或在 Agent 需要路径前于运行时烘焙)
  • NavigationRegion 和 NavigationAgent 的 navigation_layers 位掩码一致
  • NavigationAgent2D/NavigationAgent3D 是敌人节点的子节点
  • 每个物理帧都调用 get_next_path_position(),而不是 get_target_position()
  • 当 avoidance_enabled 为 true 时,已连接 velocity_computed 信号
  • 移动到目的地前检查 is_navigation_finished(),以避免抖动
  • 跟随移动中的玩家时,通过节流计时器而非每帧更新路径目标
  • agent_radius 足够小,能通过关卡中最窄的通道
  • 重力独立于水平导航速度应用(仅限 3D)
  • 大型或动态地图保持默认的多线程烘焙(bake_navigation_mesh()、on_thread = true),并等待 bake_finished
  • 使用 Godot 4.5+ 的纯 2D 项目可以通过 disable_navigation_3d=yes 构建自定义导出模板,以减小导出体积

评论

支持 Markdown