【Unity】ナビゲーションシステムの技術的詳細とパフォーマンス上の注意点

Unityのナビゲーションシステムの技術的詳細とパフォーマンス上の注意点についてまとめました。

Unity2019.2.6

はじめに

ナビゲーションシステムはとても便利ですが、仕組みを知らずに使うとパフォーマンスに影響を及ぼしがちです。
そこでこの記事ではUnityのナビゲーションシステムの技術的な仕組みを簡単に解説し、
パフォーマンスを落とさないために注意すべき点について説明します。

ナビゲーションシステムの基本的な知識については本筋と外れるため本記事では説明しません。
こちらについては以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

ナビゲーションシステムによる経路探索の仕組み

さて、いま以下のように地面と障害物が配置されているとします。

f:id:halya_11:20191019160453p:plain

最短経路を求める際、ナビゲーションシステムはまずすべての地面と障害物を集めます。

そしてこれらから通行可能なエリアを計算しポリゴンとして表します。
この処理をベイクと言い、出力されたポリゴンをナビメッシュと呼びます。

f:id:halya_11:20191019160655p:plain

このとき、ポリゴンはかならず凸多角形になっています。
凸多角形にしておけば、ポリゴンの内側にある二点間は確実に移動可能であることが保証されるためです。

f:id:halya_11:20191019161136p:plain

さてここまでは通常、非ランタイムで行われる処理です。
(NavMesh building componentsを使うとランタイムでビルドすることもできます)

次にランタイムで目標地点が設定されると、ナビメッシュに対してA*アルゴリズムを使った経路探索が行われます。
これはGlobal Navigationと呼ばれて、比較的重い処理になります。
これにより、現在の位置のポリゴンから次にどのポリゴンに進めば最短経路になるかがわかります。

ちなみにA*に関しては本記事では説明しませんが、下記にまとめていますので必要に応じて参照してください。

light11.hatenadiary.com

ここまでの処理で、次に目指すべき位置、その次に目指すべき位置・・とゴールまでの経路が決定されます。
あとはこれを辿るだけなのですが、他のエージェントや動的に動く障害物に衝突しないように進む必要があります。
これはLocal Navigationと呼ばれ、Global Navigationよりも軽い処理になります。

まとめるとこんな感じになります。

f:id:halya_11:20191019161732p:plain

以上がナビゲーションシステムによる経路探索の仕組みです。

障害物を配置する際にはパフォーマンスに注意

GameObjectにNav Mesh Obstacleコンポーネントをアタッチすると、そのオブジェクトを障害物にできます。

f:id:halya_11:20191019143508p:plain

これでエージェントはこの障害物をLocal Navigationで衝突判定するので通過できなくなります。
ただ、上図から見て取れるようにこの状態ではNav Mesh自体には変化は加わっていません。
つまり、Global Navigationの際には通過できる経路として認識されてしまいます。

つまり、たとえばこの障害物により袋小路が作られてしまった場合には、
エージェントはまずGlobal Navigationにより算出された経路に従い袋小路を進んでいきますが、
障害物に近づいたときにはじめてLocal Navigationにより進めないことが判明し、そこで止まってしまいます。

障害物の形にNav Meshをくり抜くには、Nav Mesh ObstacleのCarveにチェックを入れます。

f:id:halya_11:20191019144518p:plain

これにより動的な障害物をNav Meshに影響させることができますが、ここでパフォーマンスの問題が出てきます。
まず一点目にこのくり抜く処理自体の処理負荷が高いことです。
また、Nav Meshの構成が変わるということは、Global Navigationの計算をやり直す必要があります。
これは上述の通り比較的重い処理なので、頻繁に行われるとパフォーマンスに影響します。

これらの理由から、基本的にはCarveのチェックは外してLocal Navigationのみで処理するべきです。
上述の袋小路のような状況が起こる場合のみ、Carveにチェックするべきです。

障害物でNav MeshをCarveする場合の設定

このように動的な障害物は注意して扱わないとパフォーマンスに影響するため、
Carveする条件などが細かく設定できるようになっています。

f:id:halya_11:20191019155021p:plain

まずCarve Only Stationaryにチェックを付けると、
障害物が動いてから止まった後、一定時間が経過した時点でNav Meshのくり抜き処理を行います。
この「一定時間」はTime To Stationaryで設定できます。

f:id:halya_11:20191019155421g:plain

また、Carve Only Stationaryのチェックを外すと、
障害物が一定距離を移動した時点でNav Meshのくり抜き処理が行われます。
この「一定距離」はMove Thresholdで設定できます。

f:id:halya_11:20191019155733g:plain

参考

docs.unity3d.com

docs.unity3d.com