ハコブくんNavMeshと周回路による自律走行の実装プロセス
導入
初期段階では、ロボットを四角い部屋に配置していましたが、これではハコブくんが壁に突き当たり行動が停滞します。箱庭ではなく、ロボットが常に前進し続けられる流れを作るために、既存の壁オブジェクトを組み替えて、病院内の通路を想定したものへとアップデートする必要があります。
※薬剤搬送ロボットハコブくんは技術検証用の仮想設定です。 実用化目的ではなく、制御・UIデザイン・HRI(人間とロボットの相互作用)の本質を探求するコンセプトとして定義しています。
壁オブジェクトの配置換え
外周を定義:回遊性の導入
ハコブくんが特定の地点で止まることなく、永遠に周回し続けられる環境を整えます。そのためには、中央に通行不可能なエリアを設け、その周囲を道として開放する「ドーナツ型」の構造が最も効率的です。
実装のステップ: Hierarchy で空の GameObject である
CenterBlockを作成し、その子要素としてCubeを追加します。このCubeのScaleを調整して中央に配置し、既存のWall_LeftやWall_Rightといった壁オブジェクトを外側へと押し広げることで、中央の島と外壁の間にハコブくんが通れるだけの十分なスペースを確保します。設計の意図: 中央に障害物を配置することで、最短距離を突き進もうとするロボットのパスプランニングに「回避と巡回」の動機を与えます。ハコブくんの機体幅に対して適切な余裕を持たせた通路幅を設定することで、物理演算による不必要な接触判定を回避し、スムーズな走行テストを可能にすることが狙いです。
計測ツール
現在の設定リスト(Unity > Tools, Grid and Snapなど)の中には、標準の「Measureツール」が有効化されていません。 Unity 6の一部バージョンやプロジェクト設定によっては、手動で追加が必要な場合があります。
ツールを探して時間を浪費するのはもったいないので、現場すぐに計測する代わりの方法を考えて実行します。
Cubeを定規にして測る

これが一番確実で、視覚的にもわかりやすい方法です。
手順
Hierarchyで右クリック > 3D Object > Cubeを作成。名前を
Ruler_3mに変更。Scale の X(またはZ)を
3に設定します(これで長さ3mの棒になります)。これを通路に置いてみます。
メリット: ツールを探す手間がなく、ハコブくんを走らせた時に「あとどれくらい余裕があるか」が常に目視でわかります。
巡回ルート
CenterBlock が配置され、ハコブくんが「ぐるぐる回れる」コースになりました。
巡回ルートの目印を置く
ハコブくんに「どこを、どの順番で通るか」を教えるための目印を設置します。
ルートの親オブジェクトを作る
Hierarchyで
Create Emptyを行い、名前をPatrolRouteにします。
経由地を配置する
その中にさらに
Create Emptyを4つ作り、WP1,WP2,WP3,WP4と名付けます。配置のコツ: 周回路の「4つの角」の中央付近に、ハコブくんが通ってほしい順番で並べてください。
高さを合わせる
各WPの Position Y は、ハコブくんの高さと同じ(およそ 1.0 くらい)にしておくと、移動がスムーズになります。
自動巡回スクリプトの適用
準備が整いましたので、いよいよハコブくんを周回路で動かします。
スクリプトの作成: Projectウィンドウで右クリック > Create > C# Script を選択し、名前を
HakobuPatrolにします。
コードの書き込み: HakobuPatrol.cs
C#
using UnityEngine;
public class HakobuPatrol : MonoBehaviour
{
[Header("巡回設定")]
public Transform[] waypoints; // HierarchyからWP1〜WP4を割り当てる
public float moveSpeed = 2.0f; // 移動速度
public float rotationSpeed = 5.0f; // 回転速度(曲がる時のスムーズさ)
public float stopDistance = 0.2f; // 次の地点へ切り替える距離
private int currentTargetIndex = 0;
void Update()
{
// 経由地が設定されていない場合は何もしない
if (waypoints.Length == 0) return;
Patrol();
}
void Patrol()
{
// 現在の目標地点を取得
Transform target = waypoints[currentTargetIndex];
// 1. 移動の処理
// 目標地点のXとZだけを使い、Y(高さ)は自分自身の現在の値を維持する
Vector3 targetPos = new Vector3(target.position.x, transform.position.y, target.position.z);
transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime);
// 2. 回転の処理
// 目標地点の方向を向き続ける
Vector3 direction = target.position - transform.position;
if (direction != Vector3.zero)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
// Slerp(球面線形補間)を使うことで、ゆっくりとなめらかに回転する
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
// 3. 次の経由地への切り替え(高さを無視して判定)
float distanceToTarget = Vector2.Distance(
new Vector2(transform.position.x, transform.position.z),
new Vector2(target.position.x, target.position.z)
);
if (distanceToTarget < stopDistance)
{
currentTargetIndex = (currentTargetIndex + 1) % waypoints.Length;
}
} // ← ここが足りなかった「Patrol関数の終わり」
} // ← ここが「クラス全体の終わり」Waypointsを表示させる手順
Hakobu_kun オブジェクトの Inspector には、以前から入っていた Hakobu Move スクリプトは表示されていますが、新しく作成した Hakobu Patrol スクリプトがまだアタッチ(追加)されていないようなので、まだ Waypoints という項目が表示されていません。以下の手順で表示させることができます。
Projectウィンドウにある
HakobuPatrol(緑色の「#」アイコンのスクリプト)を、HierarchyのHakobu_kunに直接ドラッグ&ドロップしてください。
Inspectorを確認する
Hakobu_kunを選択して Inspector の一番下の方を見ると、Hakobu Patrol (Script)という項目が追加されているはずです。
そのスクリプトの中に、「Waypoints」 という名前の横に小さな「▶」がついた項目が現れます。
表示された後の設定方法
Size(要素数)を入力する
Waypointsの横にあるSizeという欄に 「4」 と入力して Enterキーを押します。
WPを割り当てる
すると、下に
Element 0〜Element 3という空枠が出てきます。そこへ、Hierarchyにある
WP1〜WP4を一つずつドラッグ&ドロップして入れれば完了です。Element 0〜3に、HierarchyのWP1〜WP4を順番にドラッグして割り当てます。
再生ボタンを押せば、ハコブくんが自律的に病院(テストコース)のパトロールを開始するはずです。
ハコブくんが浮いてしまう

ハコブくんがまだ空を飛んだり、その場で回ったりしてしまっています。原因は「物理演算(Rigidbody)」と「スクリプト」が激しくケンカしてしまっている状態だと考えられます。
特に、Rigidbodyの Use Gravity(重力)がオンのままスクリプトで位置を固定しようとしているため、挙動が荒ぶっています。ハコブくんをしっかりと着地させ、スムーズに走らせるための「調整リスト」をまとめました。
Rigidbodyの「物理」を完全にオフにする(最重要)
現在のようにスクリプト(transform.position)で移動を制御する場合、Rigidbodyの物理機能がオンになっていると、お互いに位置を奪い合ってガタガタ震えたり浮いたりします。
Is Kinematic にチェックを入れる:
Rigidbodyコンポーネントの中にあるIs Kinematicという項目にチェックを入れてください。
これで「物理的な衝突や重力」の影響を受けなくなり、スクリプトの命令だけを聞くようになります。浮遊問題はこれで解決します。
Constraints(制約)の再設定
Is Kinematic を入れた場合でも、念のため以下の設定を確認してください。
Freeze Position:
Yにチェック。Freeze Rotation:
XとZにチェック(Yはチェックを外したままにします。Yを固めると曲がれなくなります)。
既存スクリプトHakobu Moveをオフにする
Hakobu Move (Script) にチェックが入っているのをオフにします。
このスクリプトの左上のチェックボックスを外して、完全に OFF にします。
Hakobu PatrolとHakobu Moveが同時に動いていると、ハコブくんがどちらの方向を向くべきか混乱して「回るだけ」の状態になります。
看護師カプセルの設定
看護師(Nurseオブジェクト)もハコブくんと同じ通路を歩かせるために、ハコブくんの巡回ロジックを再利用して「看護師さん専用の巡回システム」を構築します。
今の状態は、看護師さんの座標が中央の CenterBlock 内に固定されているだけなので、ハコブくんと同じように WP(経由地) を目指して動くように設定すれば解決します。
看護師さん専用の巡回スクリプトを準備する
人間らしい動きを再現する:NurseHumanMovement.cs
このスクリプトは、経由地(WP)に到着したあと、すぐに次の点へ向かうのではなく、数秒間その場で立ち止まって周囲を確認するような動作を挟みます。
C#
using UnityEngine;
using System.Collections;
public class NurseHumanMovement : MonoBehaviour
{
[Header("巡回リスト")]
public Transform[] waypoints;
[Header("人間らしい動きの設定")]
public float walkSpeed = 1.5f; // ロボットより少し遅めの歩行速度
public float rotationSpeed = 3.0f; // ゆっくりと向きを変える
public float waitTimeMin = 1.0f; // 到着時の最小待ち時間
public float waitTimeMax = 3.5f; // 到着時の最大待ち時間
private int currentTargetIndex = 0;
private bool isWaiting = false;
void Update()
{
if (waypoints.Length == 0 || isWaiting) return;
MoveTowardTarget();
}
void MoveTowardTarget()
{
Transform target = waypoints[currentTargetIndex];
// 1. 水平移動(高さを維持)
Vector3 targetPos = new Vector3(target.position.x, transform.position.y, target.position.z);
transform.position = Vector3.MoveTowards(transform.position, targetPos, walkSpeed * Time.deltaTime);
// 2. 体の向きを目標へ(ゆっくり回転)
Vector3 direction = targetPos - transform.position;
if (direction != Vector3.zero)
{
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
// 3. 到着判定と「一休み」
if (Vector2.Distance(new Vector2(transform.position.x, transform.position.z),
new Vector2(target.position.x, target.position.z)) < 0.3f)
{
StartCoroutine(WaitAndProceed());
}
}
IEnumerator WaitAndProceed()
{
isWaiting = true;
// 到着した地点で少し待機(人間が周囲を確認したり、一息つく間を作る)
float waitTime = Random.Range(waitTimeMin, waitTimeMax);
yield return new WaitForSeconds(waitTime);
// 次の目標をセット(1周するようにループ)
currentTargetIndex = (currentTargetIndex + 1) % waypoints.Length;
isWaiting = false;
}
}ハコブくんと逆方向に歩かせる
逆方向に歩かせると、通路でハコブくんと看護師さんが何度も「すれ違う」ことになり、以前作った「接近時の発光ギミック」のテストがしやすくなります。
設定方法:
WaypointsのSizeを 4 にします。
ハコブくんが
WP1→2→3→4の順なら、看護師さんはWP4→3→2→1の順でドラッグ&ドロップして割り当てます。
看護師さんの物理設定をハコブくんと合わせる
看護師さんが床を突き抜けたり、勝手に回転したりするのを防ぐため、ハコブくんの設定と同じように調整します。
Is Kinematic:
RigidbodyのIs Kinematicにチェックを入れます。Constraints:
Freeze Position YとFreeze Rotation X / Zにチェックを入れます。高さを調整: 看護師さんの Position Y を、ハコブくんと同じ
1.1前後(カプセルの中心が地上に来る高さ)に設定します。
看護師カプセルが中央のCenterBlockを突き抜けてしまう問題発生

現在の移動ロジックが「障害物を避ける」のではなく、次の目標地点(WP)へ向かって「直線距離」で進もうとしているためです。
人間らしい自然な動きで周回路を維持するには、Unityの「NavMesh(ナビメッシュ)」という自動ナビゲーション機能を使うのが最も確実でスマートな方法です。
歩ける場所をUnityに教える
まず、中央のブロックを「通れない場所」、周回路を「歩ける道」としてUnityに認識させます。
NavMesh Surface オブジェクトを作成
Hierarchyウィンドウの何もない場所を 右クリック > AI > NavMesh Surface を選択します。これで、シーン内にナビゲーションを管理する専用の司令塔が生まれます。
インスペクターで「Bake」を実行
作成した
NavMesh Surfaceを選択した状態で、Inspectorを見てください。そこに大きく
Bakeというボタンが表示されています。これをクリックします。
青い床を確認
成功すると、Sceneビュー上の通路(周回路)が青いメッシュで覆われます。
CenterBlockの部分は青くなっていないはずです。これで「看護師さんが通れる道」が出来ました。
修正点:中央を通行禁止にする最新の手順

CenterBlock がまだ歩けるエリアになってしまっているようなので修正します。
中央の
Cubeを選択するHierarchy で
CenterBlockの中にあるCubeをクリックします。
NavMesh Modifierを追加する
Cubeの Inspector の一番下にあるAdd Componentボタンを押し、検索窓にNavMesh Modifierと入力して追加します。これが Unity 6 における「個別の通行設定」を行う専用のコンポーネントです。
エリアを書き換える
追加された
NavMesh Modifierの中にあるOverride Areaにチェックを入れます。その下の
Area TypeをNot Walkableに変更します。
再度
Bakeする
Hierarchy で
NavMesh Surfaceを選び直し、Inspector 右下にあるBakeボタンを押します。
看護師さんに「知能」を与える
次に、看護師さんがその青い道に沿って歩くようにコンポーネントを追加します。
NavMesh Agentの追加:
Nurseオブジェクトを選択し、Add Componentから NavMesh Agent を追加します。設定:
Radius(半径)を 0.5 程度、Speedを 1.5 程度に調整します。これで、看護師さんは自動的に壁や中央ブロックを回避して歩くようになります。
