これまでのヒューマノイドシリーズ(①移動〜④モーキャプ)で、1体のキャラを動かす技術を積み上げてきました。今回は応 用編として、街を自動で歩き回るNPC(通行人)を、UnityのNavMesh(経路探索システム)で作ります。障害物を 避け、決めたルートを巡回する群衆を、少ないコードで実現します。
NavMeshとは
NavMesh(ナビメッシュ)は、Unityが用意する経路探索(パスファインディング)の仕組みです。歩ける床面 を「ナビメッシュ」として焼き込んでおくと、NPCは目的地までの最短ルートを自動計算し、壁や建物を避けて歩いてくれます。自 分で迷路探索を書く必要はありません。
登場する主役は2つです。
- NavMesh:歩ける床の地図(シーンに焼き込む)
- NavMeshAgent:その地図上を移動するNPC本体
ステップ1:NavMeshを焼き込む(Bake)
まず歩ける床をNavMeshとして登録します。
- 地面・道路など歩かせたいオブジェクトを選び、インスペクターでStatic(Navigation Static)にする
- メニュー Window > AI > Navigation を開く
- Bake タブで Agent Radius(NPCの太さ)などを設定し、Bake ボタンを押す
- 床が青く塗られればOK。これが歩ける範囲です
※ Unityの新しいバージョンでは、AI Navigationパッケージを導入し、床にNavMeshSurfaceコンポーネントを付け
てBakeする方式になります。やることは同じ「歩ける床を焼く」です。
ステップ2:NPCにNavMeshAgentを付けて目的地へ歩かせる
NPCのGameObjectに NavMeshAgent
コンポーネントを追加し、SetDestinationで行き先を指定するだけで歩き出します。
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class SimpleWalker : MonoBehaviour
{
[SerializeField] Transform destination;
NavMeshAgent _agent;
void Start()
{
_agent = GetComponent<NavMeshAgent>();
if (destination != null) _agent.SetDestination(destination.position);
}
}
これだけで、NPCは障害物を避けて目的地まで歩きます。経路探索・衝突回避はNavMeshAgentが内部でやってくれます。
ステップ3:ウェイポイントを巡回させる(通行人らしく)
1点に向かうだけでは止まってしまいます。街の通行人らしくするには、複数の経由地点(ウェイポイント)を順番に、 あるいはランダムに巡回させます。
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class CityNPC : MonoBehaviour
{
[SerializeField] Transform[] waypoints; // 歩道に置いた経由地点
[SerializeField] float arriveDist = 0.5f;
NavMeshAgent _agent;
int _index;
void Start()
{
_agent = GetComponent<NavMeshAgent>();
if (waypoints.Length > 0)
{
_index = Random.Range(0, waypoints.Length); // 開始位置をばらけさせる
GoNext();
}
}
void Update()
{
// 目的地に着いたら次のウェイポイントへ
if (!_agent.pathPending && _agent.remainingDistance <= arriveDist)
GoNext();
}
void GoNext()
{
_index = (_index + 1) % waypoints.Length; // 順番に巡回
_agent.SetDestination(waypoints[_index].position);
}
}
ランダムに歩かせたい場合は、GoNextを_index = Random.Range(0, waypoints.Length);に変えるだ
けです。複数のNPCで開始インデックスをばらけさせると、群衆が同じ動きをせず自然に見えます。
ステップ4:歩行アニメと連動させる
NavMeshAgentは「位置」を動かしますが、見た目のアニメは別です。Agentの速度をAnimatorに渡して、歩行アニメを再生しまし
ょう。シリーズ①で使ったSpeedパラメータがそのまま使えます。
void Update()
{
// 移動速度を正規化してAnimatorへ(①の歩行ブレンドと同じ)
float speed = _agent.velocity.magnitude / _agent.speed;
animator.SetFloat("Speed", speed, 0.1f, Time.deltaTime);
if (!_agent.pathPending && _agent.remainingDistance <= arriveDist)
GoNext();
}
歩行アニメの土台づくりはシリーズ①で詳しく解説しています。NavMeshAgentと組み合わせると、計算した移動に自然な足の動き が乗ります。
実践で詰まった落とし穴:地面スナップ
NPCを地面に乗せ直すとき、上空から下方向にレイを飛ばして「最初に当たった面」に乗せると、街路樹・街灯・屋根の 上に乗ってしまうことがあります。これらは天辺が高く、地面より先にヒットするためです。
対策は、Physics.RaycastAllで全ヒットを取り、最も低いY(=本来の地面)を採用すること。
「最初のヒット」を使うと、高所に乗る→救済で落とす、を繰り返す不具合になります。
// 指定座標の「本当の地面」のYを返す
float GroundYAt(float x, float z)
{
Vector3 origin = new Vector3(x, 100f, z);
var hits = Physics.RaycastAll(origin, Vector3.down, 200f);
float lowest = 0f;
bool found = false;
foreach (var h in hits)
{
if (!found || h.point.y < lowest) { lowest = h.point.y; found = true; }
}
return lowest;
}
よくあるハマりどころ
| 症状 | 原因 |
|---|---|
| NPCが動かない | N avMeshが焼かれていない/Agentが青い面の外にいる |
| 床をすり抜ける/浮く | 地面がNavMeshに含まれてい ない、地面スナップが高所を拾っている |
| 目的地に行けない | 経路が途切れている(段差・隙間)。Agent Radius/段差設定を見直す |
| 歩いてるのにアニメが止まる | Agentの速度をAnimatorに渡していない |
| 全員同じ動きで不自然 | 開始ウェイポイントをばらけさせる/速度に個体差をつける |
まとめ
- 歩ける床をNavMeshにBakeし、NPCにNavMeshAgentを付ける
SetDestinationで目的地へ。経路探索と回避は自動- 複数ウェイポイントを巡回させ、開始位置をばらけさせると群衆が自然に
- Agentの速度をAnimatorに渡して歩行アニメと連動
- 地面スナップはRaycastAllで最も低い面を採用(高所誤判定を防ぐ)
NavMeshを使えば、街中を自然に歩き回る通行人を少ないコードで量産できます。シリーズ①〜④で作った1体の作り込みと組み合 わせれば、賑わいのある街のシーンが完成します。まずは床を1つBakeして、NPCを1体歩かせるところから始めてみてください。

