Unityのナビゲーションシステムで最短経路を探索して進む知的なAIを簡単に作る方法をまとめました。
- はじめに
- ナビメッシュを生成する
- Bake時の詳細設定項目
- エージェントを動かす
- 動的に動く障害物を設定する
- Off-mesh Linkで非連続なNavMesh間を移動する
- Off-Mesh Linkを自動的に作る
- 参考
Unity2019.2.5
はじめに
Unityのナビゲーションシステムを使うと、地形から自動的に目的地までの最短距離を算出し、
障害物を避けながら目的地まで進む知的なキャラクター(エージェントと呼びます)を簡単に作成できます。
この記事ではナビゲーションシステムの基本的な使い方を紹介します。
前提として、検証用に以下のようにPlaneメッシュを組み合わせて地形を作っておきます。
ナビメッシュを生成する
ナビゲーションシステムを使うには、まずナビメッシュ(NavMesh)と呼ばれる経路探索用のデータを生成する必要があります。
NavMeshを生成するにはまず、地面となるGameObjectをNavigation Staticとしてマークします。
次にWindow > AI > NavigationからNavigationウィンドウを開きます。
ウィンドウが開いたらBakeタブを選択します。
次にこのウィンドウで基本的な設定をしていきます。
とりあえず以下の4つを理解すれば基本的なNavMeshが作れます。
項目名 | 説明 |
---|---|
Agent Radius | エージェントの半径 設定するとエッジの余白の幅が変わる |
Agent Height | エージェントの背の高さ 天井のある障害物を潜り抜けられるかどうかの判定に使われる |
Max Slope | エージェントが登れる勾配の最大値 |
Step Height | エージェントがどれだけ高い段差を登れるか |
適当に設定したらBakeボタンを押下してNavMeshを作ります。
Sceneビューを見ると正常に生成されていることが確認できます。
青い部分がナビメッシュです。
凸多角形のポリゴンで構成されていることが確認できました。
Bake時の詳細設定項目
さてNavigationウィンドウのBakeタブの下部にはさらに詳細な設定項目があります。
まずManual Voxel Sizeですが、これはNavMeshが粗くて地形のメッシュの形状と全然異なってしまうときに使います。
ボクセルサイズを小さくするほど精度が上がりますが、ベイク時間も比例して増加します。
この設定項目は通常は変えることはないと思います。
次にMin Region Areaです。
いま、次のように小さい独立したNavMesh領域が作られていたとします。
Min Region Areaはこのような独立したNavMeshの最小の大きさを定義する値です。
この値を大きくすると、小さいNavMeshしか作れない領域は無視されます。
また、低めの階段などは斜めのNavMeshとして生成されることが多いです。
これらをしっかり階段を登っているように扱いたい場合にはHeight Meshにチェックを入れます。
高さ用のメッシュ情報が別途生成されます。
エージェントを動かす
次に作成したNavMesh上にエージェントを配置して動かしてみます。
まず適当にCylinderを作成してNavMeshAgentコンポーネントをアタッチします。
このNavMeshAgentのvelocityに値を代入するとエージェントを動かすことができます。
using UnityEngine; using UnityEngine.AI; public class Example : MonoBehaviour { [SerializeField] private NavMeshAgent _agent; private void Update() { var velocity = Vector3.zero; if (Input.GetKey(KeyCode.LeftArrow)) { velocity.x += 5; } if (Input.GetKey(KeyCode.UpArrow)) { velocity.z += 5; } if (Input.GetKey(KeyCode.RightArrow)) { velocity.x -= 5; } if (Input.GetKey(KeyCode.DownArrow)) { velocity.z -= 5; } _agent.velocity = velocity; // Moveで相対位置を指定してもok //_agent.Move(); } }
また、NavMeshAgent.destination
に座標を入れるとそこまで最短距離で自動的に移動してくれます。
using UnityEngine; using UnityEngine.AI; public class Example : MonoBehaviour { [SerializeField] private NavMeshAgent _agent; private void Update() { if (Input.GetKeyDown(KeyCode.R)) { _agent.destination = Vector3.zero; } } }
動的に動く障害物を設定する
次に動的に動く障害物を設定してみます。
まず適当にCubeを生成し、NavMeshObstacleコンポーネントをアタッチします。
これで再生すると、障害物の部分は通れなくなっていることが確認できます。
ここで、上図から見て取れるようにこの障害物はナビメッシュ自体には変化を与えていません。
つまり、経路探索の際にはこの障害物の部分も経路として計算されてしまいます。
NavMeshを障害物によって削るにはNavMeshObstacleのCarveにチェックを入れます。
するとこの障害物の形にNavMeshが削り取られます。
ただし、NavMeshの形が変わるということはA*によるグローバルな経路探索もやり直しになるということを意味します。
これは比較的重い処理であり、頻繁に行われるとパフォーマンスに影響してしまいます。
そのためCarveは基本的にはOFFにして、必要なケースだけONにする必要があります。
これに関してはナビゲーションシステムの仕組みから理解する必要があるため、以下の記事にまとめました。
Off-mesh Linkで非連続なNavMesh間を移動する
今このように非連続なNavMeshが生成されているとします。
このような場合にはオフメッシュリンクを使うと、離れたNavMesh間が経路として繋げられます。
使い方としては、適当なGameObjectにOffMeshLinkコンポーネントをアタッチして、結びたい二点をStartとEndにアサインします。
NavMeshを表示した状態で二つのオフメッシュリンクの周りに円が表示されていて、かつ線でつながれていればOKです。
この状態で、離れたNavMesh上の位置にdestinationを設定して再生します。
オフメッシュリンクにより離れたNavMeshがつながれていることが確認できました。
Off-Mesh Linkを自動的に作る
段差から飛び降りるためのオフセットリンクと、ジャンプして溝を飛び越えるためのオフセットリンクはベイク時に自動生成することもできます。
これらを自動生成するには、まず対象のGameObjectを選択した状態でNavigationウィンドウのObjectからGenerate OffMeshLinksにチェックを入れます。
次にBakeタブのGenerated Off Mesh Linksに、飛び降りることが可能な高さとジャンプ可能な距離を入力します。
設定が終わったらあとはベイクすれば、オフメッシュリンクが自動的に生成されます。