NavMesh
NavMesh(Navigation Mesh)はシーン形状から焼いた歩行可能メッシュです。AI Agent が経路探索し、障害物を避けながらレベル上を自動移動するための土台になります。図解とシミュレーターで Bake と SetDestination の流れを体感し、実践的にしっかり学べます。自学にも。
想像してみてください...
NavMesh は 3D 世界の道路地図です。プレイ前に Unity が歩行可能領域(青)と遮断領域(壁、障害物)を「塗り」ます。AI が A から B へ行くとき、その地図を参照し A-star で最適経路を走ります——壁を回り、階段を上り、崖を避けます。NavMeshAgent はその地図上の GPS ドライバーです。
概念の詳細
NavMesh Baking: Unity は静的シーン形状(“Navigation Static” マーク)を解析して歩行可能メッシュにします。主要パラメータ: Agent Radius (壁との最小クリアランス——隙間が 2×radius より狭いと通れない)、 Agent Height(頭高——低い天井の下は這えない)、Max Slope (登れる最大傾斜)、Step Height(最大段差)。Bake は Window → AI → Navigation → Bake。
NavMeshAgent は NPC/AI に付け、NavMesh を照会します。主な API:
agent.SetDestination(target)
で経路探索開始、
agent.remainingDistance
で残り距離、
agent.pathStatus
(Complete/Partial/Invalid)。speed、acceleration、stoppingDistance
が動きを制御します。
NavMeshObstacle は動的ブロッカー——再 Bake 不要。Carve を有効にすると実行時に NavMesh に穴を開けます(CPU 増)、Carve オフなら回避のみ(メッシュは変わらず周回)。ドア開閉や移動車両に適します。
動的 NavMesh(実行時再 Bake)には NavMesh Components package
(GitHub の Unity-Technologies)と
NavMeshSurface
を使い、領域を Bake して増分更新します。Unity 6 ではマルチサーフェス NavMesh が標準パッケージに統合されます。
NavMesh システムの部品
NavMesh
シーン形状からベイクした歩行可能メッシュ。Static で、ベイク後は変わらない。
Navigation Static flagNavMeshAgent
AI 用コンポーネント — 経路検索、自動移動、他エージェント回避(avoidance)。
SetDestination(target)NavMeshObstacle
動的障害物 — 再ベイク不要。Carve モードは実行時に NavMesh に穴を開ける。
Carve = true/falseハンズオン手順
静的ジオメトリをマーク
静的メッシュ(壁、床、プラットフォーム)を選択 → Inspector → Static ドロップダウン → Navigation Static(ライトマップが必要なら Contribute GI も)。
NavMesh を Bake
Window → AI → Navigation → Bake タブ → Agent Radius/Height/Slope を調整 → Bake。Scene ビューに青い NavMesh が現れます。
NPC に NavMeshAgent を追加
Add Component → NavMeshAgent。Agent Size を Bake 設定に合わせる。Rigidbody がある場合は gravity をオフ(移動は Agent が担当)。
スクリプトから Destination を設定
agent.SetDestination(target.position)。到着判定は agent.remainingDistance <= agent.stoppingDistance。
動くブロッカーに NavMeshObstacle
Add Component → NavMeshObstacle → Shape: Capsule/Box → NavMesh をライブ更新するなら Carve(ドア、駐車車両)。
インタラクティブシミュレーター
マップをクリックして目的地を設定——Agent が A* で NavMesh 上を経路探索します。
コード例
基本シンプルな NavMeshAgent AI——プレイヤーを追跡し、stopping distance で停止。
using UnityEngine;
using UnityEngine.AI;
public class EnemyAI : MonoBehaviour
{
public Transform player;
public float chaseRange = 15f;
public float attackRange = 2f;
public float updateRate = 0.2f; // Recompute path every 0.2s
NavMeshAgent agent;
Animator anim;
float nextUpdate;
void Awake()
{
agent = GetComponent<NavMeshAgent>();
anim = GetComponent<Animator>();
}
void Update()
{
float dist = Vector3.Distance(transform.position, player.position);
if (dist < chaseRange && Time.time >= nextUpdate)
{
nextUpdate = Time.time + updateRate;
agent.SetDestination(player.position);
}
// Drive Animator from agent speed
anim.SetFloat("Speed", agent.velocity.magnitude);
if (dist < attackRange)
{
agent.ResetPath(); // Stop moving
anim.SetTrigger("Attack");
}
}
}コード例
上級NavMesh 上の巡回 AI——ウェイポイントループとプレイヤー検知。
using UnityEngine;
using UnityEngine.AI;
public class PatrolAI : MonoBehaviour
{
public Transform[] waypoints;
public float detectRange = 8f;
public Transform player;
NavMeshAgent agent;
int current = 0;
enum State { Patrol, Chase, Investigate }
State state = State.Patrol;
void Awake() => agent = GetComponent<NavMeshAgent>();
void Start() => GotoNextWaypoint();
void Update()
{
float dist = Vector3.Distance(transform.position, player.position);
switch (state)
{
case State.Patrol:
if (dist < detectRange) { state = State.Chase; break; }
if (!agent.pathPending && agent.remainingDistance < 0.5f)
GotoNextWaypoint();
break;
case State.Chase:
agent.SetDestination(player.position);
if (dist > detectRange * 1.5f)
state = State.Investigate;
break;
case State.Investigate:
if (!agent.pathPending && agent.remainingDistance < 0.5f)
{ state = State.Patrol; GotoNextWaypoint(); }
break;
}
}
void GotoNextWaypoint()
{
if (waypoints.Length == 0) return;
agent.SetDestination(waypoints[current].position);
current = (current + 1) % waypoints.Length;
}
}📌 要点
- ▸NavMesh = 静的形状から Bake した歩行マップ
- ▸毎フレーム SetDestination() は重い——コルーチンで間引く
- ▸remainingDistance: 残りの経路長
- ▸NavMeshObstacle + Carve = 実行時の穴
- ▸NavMeshAgent 使用時は Rigidbody.gravity をオフ
⚠️ よくあるミス
❌ SetDestination は正しいのに Agent が動かない
目的地が NavMesh 外、または NavMesh が未 Bake
✅ NavMesh.SamplePosition() でメッシュ上の最近傍点を取る
❌ Agent が壁を通る、または詰まる
NavMeshAgent の Agent Radius が Bake 半径より小さい
✅ NavMeshAgent の Agent Radius を Bake の Agent Radius に合わせる