【Unity】NavMeshObstacleは障害物を作れるコンポーネント

NavMeshObstacle Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、

「NavMeshObstacleってなに?」

「NavMesh上で障害物を作りたい!」

というお悩みの方に向けた内容となっています。

Unityでは、AI Navigationのシステムを使うことで、NavMeshで指定した範囲であれば、オブジェクトを自動で移動させることができます。

このNavMesh上で障害物となるオブジェクトを配置する際に使うのが、NavMeshObstacleというコンポーネントです。

NavMeshObstacleを使うことで、指定した場所を通れなくさせたり、道を塞いで別の経路を探すようにさせることができます。

そこでこのページでは、UnityのAI Navigationシステムで使われるNavMeshObstacleについて、どんなものなのか、また使い方までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

NavMeshObstacleとは?

まずは、NavMeshObstacleがどういうものなのか紹介していきます。

NavMesh上で障害物を作れるコンポーネント

NavMeshObstacleとは、冒頭でも解説した通り、NavMesh上で障害物となるオブジェクトを作ることができるコンポーネントです。

そもそもNavMeshというのは、AI Navigationというシステムを使って、自動で移動するオブジェクトを作成する際に、移動できる範囲の面(道)を指定しているもので、NavMeshSurfaceというコンポーネントで設定することができます。

そして、NavMeshの範囲上で目的地を指定してあげることで、その場所に向かって最短経路を計算して、NavMeshAgentを紐づけたオブジェクトを自動で動かすことができます。

このNavMesh上に、障害物となるオブジェクトをそのまま配置していても、NavMeshAgentは気にせずにそのオブジェクトを通り抜けて、移動するような仕組みになっています。

そこで、その障害物となるオブジェクトに対して、NavMeshObstacleコンポーネントを紐づけておくことで、NavMeshAgentが移動することができない障害物と認識させることができます。

NavMeshに穴を作り経路を変更できる

NavMeshObstacleコンポーネントは、先ほどのようにNavMesh上でそのオブジェクトを障害物として認識させる以外にも、NavMeshに穴を作るという機能もあります。

もう少し詳しく言うと、NavMeshObstacleでNavMeshの特定の範囲に穴を作ることで、自動で移動させるオブジェクトの経路探索の際に、その範囲を除外させるということができます。

例えば、以下のようにNavMeshで道を作り、宝箱のある場所まで自動でNavMeshAgentのオブジェクトを移動させるようにした場合、AI Navigationは最短経路を探してくれるので、直進の道を選んで進むことになります。

この直線の道の一部に、NavMeshObstacleを使ってNavMeshに穴となる場所を作った場合、その道のNavMeshが繋がらず通れなくなるので、以下のようにNavMeshAgentのオブジェクトは別の道を探して移動するようになります。

このように、NavMeshObstacleを使うことで、NavMesh上で移動できない場所を指定するだけでなく、経路を変更させたり迂回させたりすることができるようになります。

移動する障害物を作れる

NavMeshObstacleのもう一つの特徴として、その指定範囲を移動させたり変更させたり、新たに生成させるといったことができるようになります。

通常、NavMeshを使った処理を作る際は、ベイクと呼ばれる事前にその地形を焼き付けておく処理を行う必要があるため、そのNavMeshの範囲をゲーム実行中に変更させることはできません。

しかし、このNavMeshObstacleを付けたオブジェクトは、ゲーム実行中にNavMesh上を移動させたり変更したりすることができます。

例えば、NavMeshObstacleを使うことで、以下のように行きと帰りで、移動経路を変更させるといったことができます。

NavMeshObstacleの設定・使い方

ここからは、NavMeshObstacleの使い方や設定を紹介していきます。

NavMeshObstacleの設定

NavMeshObstacleを設定する際は、障害物となるオブジェクトに対してNavMeshObstacleのコンポーネントを紐づけていきます。

オブジェクトを選択した状態で、インスペクターウィンドウから「AddComponent」をクリック、

「Navigation」の中にある「NavMeshObstacle」を選択して、コンポーネントを紐づけてあげます。

NavMeshObstacleの設定は、インスペクターから以下の項目で行っていきます。

  • Shape:障害物・穴となる形状
  • Center:障害物・穴の中心地点
  • Size:障害物・穴の大きさ
  • Carve:NavMeshに穴を作る機能

Shape:障害物・穴となる形状

Shapeは、NavMeshObstacleで指定する範囲の形状を決めている項目で、以下の「Capsule」「Box」の2つから選ぶことができます。

Capsule

Box

このShapeはコライダーの形状と同じようなもので、シーンビューで表示される範囲の部分がNavMeshObstacleとなります。

基本的には、障害物や穴とするオブジェクトと同じような形状を選べば問題ないかと思います。

Center:障害物・穴の中心地点

Centerは、Shapeの形状の中心地点となる部分を決める項目で、そのオブジェクト自身の中心地点を基準にX,Y,Zで値を設定できます。

例えば、この値を(0, 0, 0)とした場合は、オブジェクト自身の中心地点を真ん中として、NavMeshObstacleの範囲が設定されていきます。

Size:障害物・穴の大きさ(範囲)

Sizeは、Shapeの形状の大きさを決める項目で、NavMeshObstacleが影響する範囲の大きさとなります。

ShapeがBoxの場合は、オブジェクト自身が持つ大きさを基準にして、X,Y,Zで立方体の大きさを指定していきます。

また、ShapeがCapsuleの場合は、同じくオブジェクト自身の持つ大きさを基準に、Radiusでカプセルの半径の長さ、Heightでカプセルの高さを指定していきます。

Carve:NavMeshに穴を作る機能

NavMeshObstacleを使ってNavMeshに穴を作る場合、このCarveにチェックを入れることで、指定した範囲のNavMeshを除外することができます。

つまり、その障害物のオブジェクトを使って、NavMeshAgentの移動経路を変更させるような場合は、Carveにチェックを入れておきます。

また、NavMeshObstacleが移動する場合に、穴の位置をフレーム単位で再計算することになりますが、その計算の処理を以下の項目で設定していくことができます。

  • Move Threshold:移動と判断する最小距離
  • Time To Stationary:静止と判断する秒数
  • Carve Only Stationary:静止時のみ穴を作るかどうか
Move Threshold:移動と判断する最小距離

Move Thresholdは、その値以上の距離をオブジェクトが移動した際に、穴の位置を移動させるように再計算する処理を行います。

このMove Thresholdを使って穴の位置を更新させる場合は、Carve Only Stationaryの項目はオフにします。

このMove Thresholdは、基本的にオブジェクトの移動が遅いような場合に、使われることが多いです。

Time To Stationary:静止と判断する秒数

Time To Stationaryは、指定した秒数よりも長くオブジェクトが移動しなかった場合は、静止していると判断して穴を作成します。

このTime To Stationaryを使って穴を作成する場合は、Carve Only Stationaryの項目をオンにします。

Carve Only Stationary:静止している時だけ穴を作る

Carve Only Stationaryは、オブジェクトが静止しているときだけ穴を作るかどうかを決めている項目です。

このCarve Only Stationaryにチェックを入れた場合、Time To Stationaryで指定した秒数以上、オブジェクトが止まっているときだけ穴が作られることになり、移動している時は穴が作られず障害物として機能します。

Carve Only Stationaryにチェックを入れない場合、静止以外の移動時も穴が作成されるようになり、Move Thresholdで指定した距離よりもオブジェクトが移動した場合に、穴の位置が更新されるようになります。

そのため、NavMeshObstacleを移動させずに、NavMesh上に穴を作成するだけであれば、このCarve Only Stationaryオンにしておくことで、無駄な計算処理が行われなくなります。

NavMesh上で障害物を配置してみる

実際に、Unity上で簡単なコース上を動くNavMeshAgentのオブジェクトを作り、NavMeshObstacleコンポーネントを使って途中で障害物を作成して、経路を変更する処理を作ってみます。

まずは、以下のようにNavMeshSurfaceコンポーネントを紐づけたオブジェクトを配置して、コースを作ります。

次に、このNavMesh上を自動で移動する赤のオブジェクトをコースの左下に配置しておき、NavMeshAgentコンポーネントを紐づけておきます。

そして、このオブジェクトに対して、以下のスクリプトをアタッチしておきます。

using UnityEngine;
using UnityEngine.AI;   // NavMeshAgentコンポーネントを使うのに記述

public class AgentController : MonoBehaviour
{
    NavMeshAgent agent;
    public Transform StartPos;  // スタート地点のTransformを取得
    public Transform GoalPos;   // ゴール地点のTransformを取得
    int direction = 1;  // 移動する方向を判定する変数

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();   // NavMeshAgentを取得
    }

    void Update()
    {
        if (direction == 1)
        {
            agent.SetDestination(GoalPos.position);     // ゴール地点に向かって自動で移動させる処理
            // 目標地点に到達した場合
            if (transform.position.x >= GoalPos.position.x)
            {
                direction = -1; // 反対方向に移動するように変更
            }
        }
        
        if (direction == -1)
        {
            agent.SetDestination(StartPos.position);    // スタート地点に向かって自動で移動させる処理
            // 目標地点に到達した場合
            if (transform.position.x <= StartPos.position.x)
            {
                direction = 1;  // 反対方向に移動するように変更
            }
        }
    }
}

20行目でNavMeshAgentコンポーネントで定義されているSetDestinationを使って、スタート地点からゴール地点までの経路を検索して、移動させる処理を行っています。

また、ゴール地点に到着したら、今度はスタート地点に戻るように、再度30行目でSetDestinationを使って移動するように処理を記述しています。

この時点でゲームを実行すると、

直線の道だけを使って、最短経路でスタート地点からゴール地点、ゴール地点からスタート地点へ移動する処理が反復して行われています。

次に、スペースキーを押したら直線の道が通れなくなるように障害物を置きたいので、以下のオブジェクトを配置して、NavMeshObstacleコンポーネントを紐づけておきます。

ここでは、以下のようにNavMeshObstacleを設定していて、Carveの項目にチェックを入れて穴を作成するようにしています。

そして、先ほどのNavMeshAgentを付けている赤のオブジェクトのスクリプトで、以下のように追記しておきます。

using UnityEngine;
using UnityEngine.AI;

public class AgentController : MonoBehaviour
{
    NavMeshAgent agent;
    public Transform StartPos;
    public Transform GoalPos;
    int direction = 1;
    public GameObject obstacle; // 障害物のオブジェクトを取得

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        obstacle.SetActive(false);  // スタート時は障害物をオフにする
    }

    void Update()
    {
        if (direction == 1)
        {
            agent.SetDestination(GoalPos.position);

            if (transform.position.x >= GoalPos.position.x)
            {
                direction = -1;
            }
        }
        
        if (direction == -1)
        {
            agent.SetDestination(StartPos.position);

            if (transform.position.x <= StartPos.position.x)
            {
                direction = 1;
            }
        }

        // スペースキーで障害物のオンオフを切り替える処理
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (obstacle.activeSelf == false)
            {
                obstacle.SetActive(true);
            }
            else if (obstacle.activeSelf == true)
            {
                obstacle.SetActive(false);
            }
        }
    }
}

ゲームのスタート時点では、15行目でNavMeshObstacleのオブジェクトがオフになるように、SetActiveメソッドを使って処理を記述しています。

また、スペースキーを押したら、NavMeshObstacleのオブジェクトのオンオフが切り替わるように、再度SetActiveメソッドの処理を行っています。

これでゲームを実行してみると、

最初は直線の経路だけを通って、スタート地点とゴール地点を移動しますが、スペースキーを押して障害物が出てきた後は、迂回してスタート地点とゴール地点を移動するように、経路が変更されているのが分かります。

まとめ

このページでは、UnityのNavMeshObstacleコンポーネントについて、どんなものなのか、また使い方までをまとめていきましたが、いかがでしたでしょうか?

NavMeshObstacleとは、NavMesh上で障害物となるオブジェクトを作成できるコンポーネントで、NavMeshAgentのオブジェクトがその範囲を移動できなくなります。

また、NavMeshObstacleでは、NavMeshに穴を作成する機能もあり、NavMeshAgentの経路を変更して移動させるようにすることができます。

AI Navigationを使ったゲームを作る際は、NavMeshObstacleを使うことで、障害物や経路変更の仕組みを作ることができるので、非常に活用できる機能です。

最後までお読みいただきまして、ありがとうございました!

コメント