ai-navigation について
Godot 4.3+でAIナビゲーションを実装する開発者向けのスキルです。GDScriptとC#で2D・3Dの経路探索やステアリングを扱い、ビヘイビアツリーや追跡・攻撃パターンも紹介します。
主な機能
- 01NavigationAgent2D/3Dの経路探索
- 02ナビゲーションのベイクとレイヤー
- 03Seek、Flee、Arrive、Wanderのステアリング
- 04ウェイポイント巡回パターン
- 05ビヘイビアツリーと追跡・攻撃パターン
活用シーン
- 012Dまたは3Dの敵に経路移動を設定
- 02動的なレベルでナビゲーションメッシュを再構築
- 03巡回・追跡・攻撃ロジックを実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill ai-navigationインストール後、Claude Code で/ai-navigationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/async-baking.mdbehavior-trees.mdchase-attack.mdnavigation-agent-3d.mdpatrol-patterns.mdsteering-behaviors.md詳細
Godot 4.3+ の AI ナビゲーション
NavigationAgent2D/3D、ステアリング動作、ビヘイビアツリー、パトロールパターンについて説明します。すべての例は、非推奨 API を使用せず、Godot 4.3+ を対象としています。
関連スキル: AI の状態管理には state-machine、モジュール式の AI 動作には component-system、移動物理パターンには player-controller、経路探索のベクトルとステアリングの計算には math-essentials、ビジュアルエディターを備えた BT + HSM には limboai、軽量な GDScript ビヘイビアツリーには beehave が適しています。ステアリング/ナビゲーションではなく、構造化されたビヘイビアツリーについては、limboai と beehave の比較表を参照してください。
1. ナビゲーションのセットアップ
シーン構成
World (Node2D or Node3D)
└── NavigationRegion2D (or NavigationRegion3D)
├── TileMapLayer / StaticBody2D (geometry)
└── Enemy (CharacterBody2D with NavigationAgent2D child)
NavigationRegion2D / NavigationRegion3D
- シーンに NavigationRegion2D(または NavigationRegion3D)ノードを追加します。
- NavigationPolygon(2D)または NavigationMesh(3D)リソースを割り当てます。
- NavigationPolygon エディターで歩行可能なエリアを描画するか、3D で NavigationMesh の境界を設定します。
- 編集時にメッシュをベイクします: NavigationRegion ノードを選択し、ツールバーで Bake NavigationPolygon(2D)または Bake NavigationMesh(3D)をクリックします。
- ワールドが動的に変化する場合は、実行時にベイクします:
# 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 ビルドでは、スレッド化されたベイクを利用できません。
GDScript と C# のバックグラウンドスレッドでのベイクの例(2D および 3D)の全容は、references/async-baking.md を参照してください。
非同期ベイクを使用する場面: プロシージャル生成されるレベル、破壊可能な地形、または実行時にナビゲーションメッシュを再構築する必要があるシーンに適しています。メッシュのベイク中もゲームは動作し続けます。
ナビゲーションレイヤー
ナビゲーションレイヤーを使うと、さまざまなエージェントタイプ(地上部隊、飛行ユニット、大型の敵)ごとに歩行可能なエリアを分けられます。
# 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を設定し、値を一致させてください。レイヤーの不一致は、エージェントが経路を見つけられない最も一般的な原因のひとつです。
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 | 回避判定に使用するエージェントの衝突半径 |
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() を呼び出し、Y 軸方向を平坦化して重力を分離することで、NavigationAgent3D を動かします。avoidance_enabled が有効な場合、希望速度を nav_agent.velocity に渡し、velocity_computed ハンドラー内で移動します。無効な場合は、move_and_slide() を直接呼び出します。目的地は target_position で設定し、is_navigation_finished() で完了を確認します。
GDScript と C# による
NavigationAgent3Dを使った敵の例の全容は、references/navigation-agent-3d.md を参照してください。
4. ステアリング動作
ナビゲーションメッシュを使わずに自然な動きを実現する、フレームごとの軽量な計算(seek、flee、arrive、wander)です。返されたベクトルを合計して組み合わせるか、1つを選んで各 _physics_process のタイミングで velocity に設定します。
references/steering-behaviors.md で、seek、flee、arrive(減速ランプ付き)、wander(円投影によるジッター)の GDScript および C# の完全な実装を確認できます。
5. パトロールパターン
NavigationAgent2D に Marker2D のウェイポイント配列と、各ポイントで一時停止するための Timer を組み合わせることで、すっきりしたパトロールループを作成できます。wait_timer.timeout でインデックスを進め、次のウェイポイントを nav_agent.target_position に設定し、is_navigation_finished() に基づいて移動を制御します。
references/patrol-patterns.md で、ウェイポイントチェーンと待機タイマーによる一時停止を備えたパトロールの GDScript および C# の完全な実装を確認できます。
6. ビヘイビアツリーの概念
ビヘイビアツリー(BT)は、各ティックで評価されるノードのツリーです。主なノードタイプは3つあります。
| タイプ | 成功する条件 | 失敗する条件 |
|---|---|---|
| Sequence | すべての子ノードが成功する(AND) | いずれかの子ノードが失敗する |
| Selector | いずれかの子ノードが成功する(OR) | すべての子ノードが失敗する |
| Action | リーフアクションが完了する | リーフが失敗を報告する |
Sequence は「Aを実行し、次にB、次にCを実行する」という処理を表します。Selector は「Aを試し、失敗したらBを試し、それも失敗したらCを試す」という処理を表します。
references/behavior-trees.md で、軽量なBTの完全な実装(BTNode基底クラスとSequence/Selector/Action)と、「追跡またはパトロール」を行う敵の実装例を、GDScriptとC#の両方で確認できます。
7. 追跡+攻撃パターン
NavigationAgent2D とステートマシンを組み合わせます。FSM の基盤については、state-machine スキルを参照してください。
ステート
| ステート | 開始条件 | 終了条件 |
|---|---|---|
| PATROL | デフォルト/プレイヤーが逃げた | プレイヤーが detect_range に入る |
| CHASE | プレイヤーが detect_range 内にいる | プレイヤーが attack_range に入る、またはプレイヤーが逃げた |
| ATTACK | プレイヤーが attack_range 内にいる | プレイヤーが attack_range の外に出た |
references/chase-attack.md で、GDScript および C# の完全な実装(PATROL → CHASE → ATTACK の遷移、攻撃クールダウンタイマー、パトロールのウェイポイントの進行、プレイヤーが逃げたときにパトロールへ戻る処理)を確認できます。
大規模なプロジェクトでは、state-machine スキルを使って各ステートを独自のノードクラスに分け、親から
NavigationAgent2Dの参照を渡してください。
8. 専用の2Dナビゲーションサーバー(Godot 4.5+)
Godot 4.5 より前は、NavigationServer2D は薄いフロントエンドとして、すべての処理を内部で3Dナビゲーションサーバーに委譲していました。Godot 4.5 では両者が完全に独立したサーバーに分離されました。この変更は透過的で、API の変更もコードの移行も不要ですが、実用上のメリットが2つあります。
- 独立性: 2Dナビゲーションが3Dサーバーを経由しなくなり、2Dマップ、リージョン、エージェントは独自の実装で処理されます。
- 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. よくある落とし穴
| 落とし穴 | 症状 | 対処法 |
|---|---|---|
| ナビゲーションメッシュがベイクされていない | エージェントが動かず、経路が見つからない | 実行前にNavigationPolygon/NavigationMeshをベイクするか、シーンの読み込み後に実行時にbake_navigation_polygon()を呼び出します |
agent_radiusが大きすぎる | エージェントが出入り口や狭い通路を通れない | NavigationPolygon/NavigationMeshのagent_radiusを小さくして再ベイクします。エージェント自身のradiusは回避専用であり、経路探索には影響しません |
| 回避時のジッター | 他のエージェントの近くでエージェントがつまずいたり、振動したりする | time_horizon_agentsを大きくする(2~4秒を試してください)か、エージェントのmax_speedを少し下げます |
| 経路の再計算頻度が高すぎる | フレームごとにCPU負荷が急増し、エージェントの動きが遅くなる | Timer(0.2~0.5秒)を追加し、毎物理フレームではなく、タイマーの発火時にのみtarget_positionを設定します |
| ナビゲーションレイヤーが間違っている | エージェントが一部の領域を無視する、または経路が見つからない | NavigationRegionとNavigationAgentのnavigation_layersビットマスクが一致していることを確認します |
| NavigationServerの準備が整う前にターゲットを設定している | 最初のフレームで経路が空になる | マップは最初の物理フレームで同期されます。_ready()内で、target_positionを設定する前にawait get_tree().physics_frame(またはNavigationServer2D.map_changed)を待ちます |
| 3Dで重力が無視される | エージェントが浮いたり、床に沈んだりする | 重力による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. チェックリスト
- NavigationPolygon/NavigationMeshリソースを持つNavigationRegion2D/3Dをシーンに追加している
- ナビゲーションメッシュをベイクしている(エージェントが経路を必要とする前に、エディター上または実行時に)
- NavigationRegionとNavigationAgentの
navigation_layersビットマスクが一致している -
NavigationAgent2D/NavigationAgent3Dが敵ノードの子になっている -
get_target_position()ではなく、毎物理フレームget_next_path_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を指定してカスタムエクスポートテンプレートをビルドし、エクスポートサイズを削減できる