Unity NavMeshでマス目ごとに停止しながらターゲットに向かうエージェントの実装方法

このチュートリアルでは、UnityでNavMeshエージェントを使用し、ターゲットに向かってマス目に沿って1マスずつ進みながら、一時停止を繰り返す動作を実現します。エージェントが各マスの中心に到達するごとに停止し、指定の時間待機してから再び移動を再開するようにします。


実行結果

このチュートリアルを進めた時の動きを先に見てみましょう

チュートリアル

ステップ1: NavMeshエージェントの設定

3Dプロジェクトを新規で作成します

プロジェクト名はGridPathFollowerSampleとしておきます

シーンにPlaneオブジェクトを追加

シーンに「3D Object > Plane」を選択して、平面を追加します。これはエージェントが移動する床として使用します。

NavMeshをベイク

Hierarkyウィンドウで右クリックし、「AI > NavMesh Surface」を選択して、エージェントの移動エリアを作成します

「Bake」ボタンを押します。これでPlaneの上がNavMeshエリアとして設定され、エージェントが移動できるようになります。

エージェントオブジェクトを作成

Planeの上に移動するオブジェクトとして「3D Object > Capsule」などのオブジェクトを追加し、「NavMeshAgent」コンポーネントをアタッチします。

ターゲットオブジェクトを作成
Capsuleが追跡するターゲットを設定するため、「3D Object > Cube」などのオブジェクトを追加し、これを移動先ターゲットとして使用します。


ステップ2: スクリプトの作成

スクリプトを作成

新しいC#スクリプトを作成し、「GridBasedAgent.cs」と名付けます。

スクリプトをエージェントオブジェクトにアタッチ

エージェントオブジェクト(Capsule)に、作成した「GridBasedAgent.cs」スクリプトをアタッチします。


ステップ3: マス目ごとの移動ロジックの実装

以下のコードをGridBasedAgent.csに追加し、エージェントがX軸とZ軸のみに沿ってターゲットに最短距離で向かうようにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class GridBasedAgent : MonoBehaviour
{
    public Transform target; // ターゲットオブジェクト
    public float waitTime = 1.0f; // 各マスでの待機時間
    public float gridSize = 1.0f; // マス目のサイズ

    private NavMeshAgent agent;
    private Queue<Vector3> pathPoints; // マス目ごとの経路
    private bool isWaiting = false;
    public float arrivalThreshold = 0.2f; // マスへの到達とみなす距離

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        CalculateOptimizedGridPath(); // 最短経路を計算
    }

    void Update()
    {
        if (!isWaiting && pathPoints != null && pathPoints.Count > 0)
        {
            Vector3 nextPoint = pathPoints.Peek(); // 次のマスの位置を確認

            // Y軸を無視して距離を計算
            Vector2 currentPos2D = new Vector2(transform.position.x, transform.position.z);
            Vector2 nextPoint2D = new Vector2(nextPoint.x, nextPoint.z);

            // 現在の位置が目的のマスに到達しているか
            if (Vector2.Distance(currentPos2D, nextPoint2D) < arrivalThreshold)
            {
                pathPoints.Dequeue(); // 到達したマスを削除
                StartCoroutine(WaitAndMoveToNextPoint()); // 1マスごとに停止
            }
            else
            {
                agent.destination = nextPoint; // 次のマスへ移動
            }
        }
    }

    // ターゲットまでの最短経路をXとZに分解して計算
    private void CalculateOptimizedGridPath()
    {
        pathPoints = new Queue<Vector3>();

        Vector3 currentPos = transform.position;
        Vector3 targetPos = target.position;

        while (Mathf.Abs(targetPos.x - currentPos.x) > gridSize || Mathf.Abs(targetPos.z - currentPos.z) > gridSize)
        {
            // X方向とZ方向の距離を比較して、短い方を優先
            if (Mathf.Abs(targetPos.x - currentPos.x) > Mathf.Abs(targetPos.z - currentPos.z))
            {
                // X方向に移動
                currentPos = new Vector3(
                    currentPos.x + Mathf.Sign(targetPos.x - currentPos.x) * gridSize,
                    currentPos.y,
                    currentPos.z
                );
            }
            else
            {
                // Z方向に移動
                currentPos = new Vector3(
                    currentPos.x,
                    currentPos.y,
                    currentPos.z + Mathf.Sign(targetPos.z - currentPos.z) * gridSize
                );
            }

            pathPoints.Enqueue(currentPos); // キューに現在位置を追加
        }
    }

    // 各マスで停止して待機するコルーチン
    private IEnumerator WaitAndMoveToNextPoint()
    {
        isWaiting = true;
        agent.isStopped = true; // エージェントを停止
        yield return new WaitForSeconds(waitTime); // 指定の時間待機
        agent.isStopped = false; // エージェントの移動を再開
        isWaiting = false;
    }
}

コードの解説

  1. CalculateOptimizedGridPathメソッド
    • targetオブジェクトまでの経路を1マスごとに計算し、X軸とZ軸のみに沿って移動するようにしています。
    • targetの位置に対して、X方向またはZ方向の距離が短い方を優先して進むため、エージェントは最短経路で移動します。
  2. Updateメソッド
    • エージェントが次のマスに到達した際に停止し、waitTimeだけ待機するようにしています。
    • arrivalThreshold内に到達した場合に、キューから次のマスを取り出し、コルーチンを呼び出して待機させます。
  3. WaitAndMoveToNextPointコルーチン
    • 各マスごとにエージェントを停止し、指定のwaitTime後に再び次のマスへ移動します。

ステップ4: 実行と確認

  1. ターゲットオブジェクトの設定
    • エージェントオブジェクト(例: Capsule)を選択し、インスペクターでスクリプトの「Target」にターゲットオブジェクト(例: Cube)をドラッグして設定します。
  2. 再生して動作確認
    • 再生ボタンを押してシーンを実行し、エージェントがターゲットに向かってX方向かZ方向にのみ移動しながら、各マスで停止する様子を確認します。

注意点

  • waitTime: 各マスでの待機時間を調整して、エージェントの停止時間を設定できます。
  • gridSize: 1マスのサイズを設定します。通常は1.0に設定すると良いですが、環境に応じて変更してください。
  • arrivalThreshold: マスへの到達距離の閾値を適切に調整し、エージェントが意図通りに停止するようにします。

このチュートリアルに従えば、エージェントがターゲットに向かって最短経路をX・Z方向でのみ移動し、各マスで待機する動作が実現できます。

C#,Unity

Posted by hidepon