敵キャラクターをプレイヤーへ向かわせる最初の実装は、たいていtransform.positionをプレイヤーへ直進させるコードです。そして最初のバグもすぐ来ます—— 敵が壁に引っかかったまま、こちらをじっと見ている。
壁や障害物を避けて目的地への最短ルートを自分で見つける——この 経路探索(Pathfinding) をUnityで実現する標準機能がNavMesh(ナビゲーションメッシュ) です。この記事では、NavMeshのベイクから追跡AI、そして「巡回→発見→追跡」の実践パターンまでを解説します。
この記事でわかること
- NavMeshシステムの3点セット: NavMesh/NavMesh Agent/NavMesh Obstacle
- NavMeshSurface でのベイク(Unity 6の現行ワークフロー)
SetDestination()だけで動く追跡AI- 巡回→発見→追跡→捜索——見失っても粘る、実戦的な敵AIパターンの組み立て
remainingDistanceでの到着判定と、動かないときに見る3つの計器
動作確認環境: Unity 6 (6000.x)/AI Navigation 2.x
NavMeshシステムの3点セット
NavMeshシステムは3つの登場人物で構成されます。「地図」「旅人」「通せんぼ」 と覚えるのが早道です。

- NavMesh(地図): AIが移動可能なエリアを示す多角形メッシュ。シーンのジオメトリから自動生成(ベイク)されます。
- NavMesh Agent(旅人): NavMesh上を移動する能力を持つコンポーネント。目的地を渡すと、経路計算・移動・他エージェントとの衝突回避まで自動でこなします。
- NavMesh Obstacle(通せんぼ): 動的な障害物用のコンポーネント。閉じたドアや倒れた柱などに付けると、エージェントが迂回するようになります。
Step 1: NavMeshをベイクする(NavMeshSurface)
現行のUnity(2022.3以降/Unity 6)では、AI NavigationパッケージのNavMeshSurfaceコンポーネントを使うのが標準ワークフローです。
tips: 古いチュートリアルにある「
Window > AI > NavigationウィンドウでNavigation Staticを設定してBake」という手順はレガシー版です。考え方は同じですが、これから作るプロジェクトではNavMeshSurfaceを使いましょう。
- パッケージの確認:
Window > Package Managerで AI Navigation(com.unity.ai.navigation)がインストールされていることを確認します(Unity 6では通常デフォルトで入っています)。 - NavMeshSurfaceの追加: 地面となるオブジェクト(または空のGameObject)に
NavMeshSurfaceコンポーネントを追加します。 - ベイク対象の指定:
Collect Objectsで対象範囲(All Game Objectsなど)を選びます。壁や障害物もジオメトリとして収集され、「通れない場所」として反映されます。 - ベイク実行: Inspectorの
Bakeボタンを押すと、シーンビューに青いポリゴンが表示されます。これがAIの歩ける範囲です。

ベイク設定はエージェントの「体格」
ベイク設定の実体は、「どんな体格のキャラが通れる地図を作るか」 です。
Agent Radius: エージェントの半径。これより狭い隙間は通行不可になります。Agent Height: エージェントの身長。これより低い天井の下は通行不可になります。Max Slope: 登れる坂の最大角度。Step Height: 乗り越えられる段差の高さ。
これらは Window > AI > Navigation の Agents タブで、Agent Type(体格の種類)ごとに決めます。NavMeshSurfaceの Agent Type で、どの体格の地図を焼くかを選びます。NavMesh Agentコンポーネントにも Radius と Height がありますが、こちらはその個体の体の大きさで、青いメッシュの形は変わりません。
「敵が狭い通路を通ってくれない」ときは、たいていAgent Radiusが通路幅に対して大きすぎます。
Step 2: NavMesh Agentの設定
AIキャラクターにAdd Component > Navigation > NavMesh Agentを追加し、移動の性格をInspectorで決めます。
| プロパティ | 役割 | 目安 |
|---|---|---|
Speed | 最大移動速度 | 歩く住人なら2前後、追跡する敵なら4〜6 |
Angular Speed | 回転速度 | 速い敵ほど大きく(曲がり角でモタつかない) |
Acceleration | 加速度 | 低いと発進・方向転換がゆったりに |
Stopping Distance | 目的地の手前どれだけで止まるか | 既定は0。追跡する敵は0.5〜、攻撃AIなら攻撃レンジに |
Radius / Height | このエージェント個体のサイズ | エージェント同士の衝突回避に影響 |
Step 3: スクリプトからAgentを制御する
移動指示は1行です。SetDestination()に目的地を渡すだけで、経路探索も移動も回避もエージェントが引き受けます。
using UnityEngine;
using UnityEngine.AI; // NavMesh関連のクラスに必要
[RequireComponent(typeof(NavMeshAgent))]
public class EnemyChase : MonoBehaviour
{
private NavMeshAgent agent;
private Transform playerTarget;
void Start()
{
agent = GetComponent<NavMeshAgent>();
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
playerTarget = player.transform;
}
}
void Update()
{
if (playerTarget != null)
{
// プレイヤーの現在位置を目的地に設定し続ける
agent.SetDestination(playerTarget.position);
}
}
}
これだけで、敵は障害物を避けながらプレイヤーを追いかけ始めます。壁に引っかかっていたあの敵が、曲がり角を先回りするように回り込んできます。
tips: 歩行アニメーションと連動させるなら、
agent.velocity.magnitudeをAnimatorのSpeedパラメータに渡すのが定番です(Animator Controllerの記事 参照)。
実践:巡回→発見→追跡→捜索の敵AIを組む
実際のゲームの敵は「ずっと追跡」ではなく、普段は巡回し、プレイヤーを見つけたら追跡し、近づいたら攻撃するという状態の切り替えで動いています。ステルスゲームの警備兵、RPGのフィールドモンスター、タワーディフェンスの侵攻ユニット——ジャンルを問わずこの骨格は共通です。

見失った瞬間に即・巡回へ戻る敵は、少し嘘くさく見えます。ここでは「最後に見た場所まで行って、数秒粘ってから戻る」捜索の動きまで入れて、人間くさい警備兵に仕上げます。
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class GuardAI : MonoBehaviour
{
[SerializeField] private Transform[] patrolPoints; // 巡回地点
[SerializeField] private float sightRange = 8f; // 発見距離
[SerializeField] private float attackRange = 1.5f; // 攻撃距離
[SerializeField] private float searchTime = 3f; // 見失った現場で粘る秒数
private enum Mode { Patrol, Chase, Search }
private Mode mode = Mode.Patrol;
private NavMeshAgent agent;
private Transform player;
private int patrolIndex = 0;
private Vector3 lastKnownPosition;
private float searchTimer;
void Start()
{
agent = GetComponent<NavMeshAgent>();
// Playerが未配置でも落ちないように(タグ検索はnullを返し得る)
GameObject playerObj = GameObject.FindGameObjectWithTag("Player");
if (playerObj != null) player = playerObj.transform;
if (patrolPoints.Length > 0)
agent.SetDestination(patrolPoints[patrolIndex].position);
}
void Update()
{
if (player == null || patrolPoints.Length == 0) return;
bool canSee = Vector3.Distance(transform.position, player.position) <= sightRange;
bool arrived = !agent.pathPending && agent.remainingDistance <= agent.stoppingDistance;
switch (mode)
{
case Mode.Patrol:
if (canSee) { lastKnownPosition = player.position; mode = Mode.Chase; break; }
if (arrived)
{
patrolIndex = (patrolIndex + 1) % patrolPoints.Length;
agent.SetDestination(patrolPoints[patrolIndex].position);
}
break;
case Mode.Chase:
if (canSee)
{
// 見えている間は「最後に見た場所」を更新し続ける
lastKnownPosition = player.position;
agent.stoppingDistance = attackRange;
agent.SetDestination(player.position);
}
else
{
// 見失った → 最後に見た場所へ向かい、捜索モードへ
agent.stoppingDistance = 0.5f;
agent.SetDestination(lastKnownPosition);
searchTimer = searchTime;
mode = Mode.Search;
}
break;
case Mode.Search:
if (canSee) { lastKnownPosition = player.position; mode = Mode.Chase; break; }
if (arrived) searchTimer -= Time.deltaTime; // 現場に着いてから数える
if (searchTimer <= 0f)
{
agent.SetDestination(patrolPoints[patrolIndex].position);
mode = Mode.Patrol;
}
break;
}
}
}
ポイントは3つです。
- 到着判定は
remainingDistance:!agent.pathPending && agent.remainingDistance <= agent.stoppingDistanceが「着いた」の定番イディオムです。経路計算が終わる前に判定しないようpathPendingを確認します。 - 攻撃レンジは
stoppingDistanceで作る: 追跡時のStopping Distanceを攻撃距離にしておけば、「密着せず、剣の届く距離で止まって攻撃」が自然に作れます。 lastKnownPositionは「見えている間ずっと」更新: 見失った瞬間の値がそのまま捜索の目的地になります。プレイヤーからすると「さっきまでいた場所を調べに来る」ので、ぐっと知能を感じる動きになります。
Playして、追いかけられている途中で、警備兵から8m(sightRange)より遠くへ逃げてみてください(引き離せるように、警備兵の Speed はプレイヤーより遅くしておきます)。警備兵はあなたが最後に見えた場所まで来て、3秒ほどそこに留まり、何もいなければ持ち場の巡回へ戻っていきます。このコードの「見える」は距離だけなので、8m以内なら壁の裏に隠れても追ってきます。「壁の裏に隠れたら見失う」「正面の扇形でだけ発見する」にしたい場合は、角度チェックと Raycastによる遮蔽判定 を組み合わせます(完成形は 視野・索敵システムの記事 へ)。状態がさらに増えてきたら Stateパターンの記事 の出番です。
動かないときに見る「3つ の計器」
NavMeshのAIが動かないとき、コードを疑う前に見る場所が3つあります。isOnNavMesh と pathStatus は、Debug.Log で出して確かめます。
| 症状 | 見る計器 | よくある犯人と直し方 |
|---|---|---|
| そもそも1歩も動かない | agent.isOnNavMesh | falseならAgentがNavMeshの外にいます(空中スポーン・ベイク漏れ)。agent.Warp(位置)でメッシュ上に乗せ直す |
| 目的地の手前で止まる | agent.pathStatus | PathPartial(途中までしか行けない)やPathInvalid(到達不能)。Agentは行けるところまで行って止まります。目的地や巡回点を、青いメッシュがつながった場所に置き直す |
| なんだかカクカク重い | SetDestinationの呼び出し頻度 | 毎フレームの再計算が積もっています。動くプレイヤーの追跡は毎フレームでも、固定の巡回点は到着時に1回だけで十分です |
おまけ:先に知っておくと良いこと
- NavMesh Obstacleの
Carve: 動的な障害物にObstacleを付けてCarveを有効にすると、NavMeshに一時的な穴が開き、エージェントが迂回を再計算します。閉じるドア・破壊可能な柵などに。既定のCarve Only Stationaryがオンだと、動いている間は穴を開けず、止まってから開けます。 - 段差やジャンプは
NavMesh Link: 崖から飛び降りる・溝を飛び越えるなど、地 続きでない場所の接続はNavMesh Linkで定義します。 - 体格の違うエージェント: 「人間用」と「巨大ボス用」で通れる幅が違う場合は、Agent Typeを複数定義してNavMeshSurfaceをそれぞれベイクします。
- エージェントを直接動かしたいとき: ノックバックなど物理的な移動を混ぜる場合、
agent.Warp()やagent.isStoppedを使います。TransformやRigidbodyで直接動かすとNavMesh上の位置と食い違って暴れる原因になります。 - Terrain の上でも焼ける: 起伏のある地形も、NavMeshSurface で焼けば歩ける範囲になります(Terrain入門)。
まとめ
NavMeshを使えば、経路探索アルゴリズムを自作せずに「賢く動く敵」が作れます。
- 3点セット: NavMesh(地図)+NavMesh Agent(旅人)+NavMesh Obstacle(通せんぼ)。
- ベイクは
NavMeshSurface(現行ワークフロー)。ベイク設定はエージェントの「体格」。 - 移動指示は
SetDestination()1行。回避も経路も自動。 - 追跡では
Stopping Distanceを0のままにしない。攻撃レンジとしても使える。 - 実戦の敵AIは「巡回→発見→追跡→捜索」。到着判定は
remainingDistanceイディオム、動かないときはisOnNavMeshとpathStatusの計器を見る。
敵AIだけでなく、味方NPCの追従、街の住人の巡回、RTSのユニット移動——「キャラクターが自分で歩く」場面すべてで使える基盤技術です。