Unityでは標準で経路探索を行うための強力な機能が、NavMeshで提供されています。
それなりのケースではNavMeshで十分なのですが、ちょっと凝ったことをしようとするとできない場合があります。
自分の場合は、「味方キャラは侵入をできないが、自キャラはなんの問題もなく通過できるエリアを動的に作りたい」というやりたいことに対して、NavMeshだと実現するのが難しそうだったので、A* Pathfinding Projectというアセットを導入しました。
今回の記事では、A* Pathfinding Project(フリー版と一部Pro版)の導入について書きたいと思います。
前述の自分がやりたかった機能まで説明すると込み入るので、NavMeshと同等の部分を実現するところまで説明したいと思います!
(経路探索がよくわからない人は、最初にUnity標準のNavMeshを触ることをおすすめします!)
環境
Unity 2021.1.1.2f1
A* Pathfinding Project Free版・Pro版 4.2.15
A* Pathfinding Project検証中です。
— asiram@unityゲーム開発 (@asiramasiram) May 9, 2021
色がついているエリアがペナルティエリア。
そのエリアをまたぐと最短ですが、ペナルティが効いて回り道します。
ペナルティエリア内に目的地を置くとしぶしぶ(?)、ペナルティエリアを通って目的地に行ってくれます。
これが動的にできてばいい感じ...!#Unity pic.twitter.com/3GY26aOfst
↑こんなことがA* Pathfinding Projectでできます。(が、今回の記事はこれの前段までの紹介にとどめています)
A* Pathfinding Projectとは
アセットストア:https://assetstore.unity.com/packages/tools/ai/a-pathfinding-project-pro-87744
Free版:https://arongranberg.com/astar/download
- 経路探索が行えるアセット
- 経路の定義に使用できるグラフの種類が豊富(Unity標準のNavMeshでは、メッシュを使用するもの一択)
- Unity標準と同等のNavMeshGraph
- Gridで定義するGridGraph
- 点で経路を決定するPointGraph などなど
- Free版も存在するが、AI同士の衝突回避が使用できない(これに目をつむればFree版でも結構色々できる)
- ここからFree版とPro版、NavMeshの機能比較が見られます
- ドキュメントもそれなりに豊富に用意されています(全部英語ですが)
- 軽量に動作し、mobileでも問題なく動きます(もちろんフィールドが広すぎないことは前提ですが)
- ランタイムでの経路再スキャンもある程度軽量なので、mobileでもいけそうです
導入してみる
では早速導入してみましょう。
Free版を使用して、対象を追いかけるAIを作ってみようと思います。
Free版のインポート
まずは適当にUnityプロジェクトを作成、Free版が配布されているサイトからDLします。
unitypackageがダウンロードされるので(パッケージマネージャではないです)、ファイルをクリックしてインポートしましょう。
余裕がある人は、「ExampleScenes」に入っているサンプルを見てみると参考になります。
適当にフィールドを作成
経路探索させるには当然ながら歩けるフィールドがないとダメですよね。
今回は、Unity標準のPlaneやCubeなどを使ってフィールドを雑に作ります。
ここで、重要なのがコライダーがアタッチされているオブジェクトを使用することです。
(Unity標準のPlaneやCubeなどは最初からコライダーが設定されています)
これから作成するGridGraphはコライダーの存在で経路を判別するようなので、コライダーが存在しないとそこは通れる経路になってしまいます。
こんな感じにしてみました。...センスとかはないので期待しないようにしてください。
中央にあるCubeを長くしたものは障害物の扱いのつもりで置きました。
ここで、ついでに各オブジェクトにレイヤーの設定をしておきましょう。
地面にGround
、障害物にObstacles
と設定しました。
経路探索のコンポーネントに、レイヤーを指定するときに使用します。
Pathfinderコンポーネントの設定
次に、経路探索を行うためのコアのコンポーネントを設定していきます。
適当に空のオブジェクトを作成、Pathfinder
コンポーネントを追加します。
(AddComponentするときはPathfinderと出ますが、本当の名前はAStarPath
です)
インスペクターに各種項目が表示されるのでGraphsを展開、Add New GraphのGrid Graph
を選択します。(今回はGrid Graphを使用します)
ボタンを押すと、ずらずらと設定項目が表示されます。(表示されない時は、さらにメニューを展開してください)
今回はこんな感じで設定しました↓
初期設定から変えたのは、Width(nodes)
、Depth(nodes)
、Diameter
、Obstacle Layer Mask
、Mask(Height Testingの設定)
です。
この状態で、シーンタブもしくはインスペクターのPathfinder
コンポーネント下部のScan
ボタンを押すと以下のように経路が設定されます。
青い部分が歩ける経路、赤い点が歩けない場所(経路と障害物の境界)です。
(色がつかない人は、Show Graphs
にチェックを入れるもしくは、SettingsのDebug - Graph ColoringをSolid Color
にすると出るかもしれません)
主要な設定項目について簡単に解説します。
Shape
ノードの形状。大体はGridで良さそうですが、用途によりHexagonal
とかにしても良いかもです。
Grid
だと四角に区切られて経路が決定するので、斜めに配置してある障害物に対して若干ムラが出てしまう時に使うと...いいのかも...です(あんまり良い使い方がわかってないです)
Width(nodes), Depth(nodes)
ノードのそれぞれの方向の数です。
経路を作りたいフィールドの広さに合わせて、増減させましょう。
当然ですが、ノードが増えれば増えるほど経路のスキャンが重くなりますのでご注意を。
Node Size
グリッドを作るノード一つの大きさです。
大きくすれば計算負荷は小さくなりますが、経路と障害物の境界が大まかになったり、移動経路がバラけづらくなります。
Center, Rotation
ノードが配置される中央の点と、回転です。
Collision Testing
歩ける範囲の検知を行うかどうかです。
経路探索を行う時は基本オンで良さそうです。
Diameter, Height/Length
歩くオブジェクトの大きさと高さの定義です。
Obstacle Layer Mask
障害物としてみなすレイヤー設定。
今回は、先ほど追加したObstacles
レイヤーを指定しています。
必要に応じて複数選択できます。
Height Testing
高い位置に登ることができるかどうかの判定を行うかどうかの設定です。
オフにすると、高い場所もそのまま経路として判断されるようになります。
これも通常はオンのままで使うのが良さそうです。
Mask(Height Testingの設定)
Height Testingに使用するレイヤーマスクです。
今回は先ほど追加したGround
レイヤーを指定しています。
経路探索を行うAIの設定
経路が設定できたので、次は経路を歩くAIの設定を行いましょう。
Unity標準のNavMeshのNavMeshAgent
にあたるものを設定していきます。
これまた適当に3Dオブジェクトを用意します。
そのオブジェクトにAIPath
をアタッチします。
AIPathをアタッチすると、自動的にSeeker
コンポーネントもアタッチされます。
↑設定はこんな感じです。今回はMax Speed
、Gravity
ぐらいしか変えていません。
よく使いそうな項目を簡単に解説します。
Radius, Height
AIの半径と高さです。
オブジェクトの大きさに合わせて設定しましょう。
Can Search, Can Move
経路探索を行えるか、移動できるかのフラグです。
Can Moveをオフにすれば(もちろんスクリプトからも可能)、移動を強制的に停止することができます。
Max Speed, Rotation Speed
最大移動速度と、回転速度です。
Slow Down Distance
目的地の手前どれくらいの距離で、減速するかの設定です。
減速がいらない場合は0にしましょう。
End Reached Distance
目的地の手前どれくらいで移動を終えるかの設定です。
Constrain Inside Graph
グラフ内に移動を制限する設定です。
基本的にAIは、設定された経路しか歩かないのですが、衝突回避のために押し出されて経路外に出てしまうことがあるようです。
この時に、このオプションが有効化されていると、経路内から出ないようにしてくれるようです。
また、自前で移動を行う際(AIPathのMove()メソッドによる移動)に、このオプションを設定しておくと経路外に出てしまうことがなくなります。
Gravity
重力を使用するかどうかの設定です。
基本的には使わなくて良さそうなので、自分はオフにしています。
目標を追跡するための設定
AIの経路探索設定までできたので、そのAIが目標を追いかけるようにする設定を追加しましょう。
スクリプトで追いかける目標を設定する場合は、AIPath.destination
に目標の座標を設定すればOKです。
今回は、最初から用意されているAIDestinationSetter
を使用して、インスペクター上から目標を設定できるようにします。
先ほどAIPathを設定したオブジェクトに、AIDestinationSetter
をアタッチします。
そして、追跡目標として適当なオブジェクトを作成して(今回はSphereを使いました)、AIDestinationSetter
のTargetに設定します。
追跡!
これで目標を追跡するための設定は終わったので、実行してみましょう。
シーンビューで、追跡目標にしたオブジェクトを動かすと、AIも移動しますね。
衝突回避の設定
さて、目標の追跡まではできました。
一見いい感じに動いていますが、AIが二体以上いる場合に同じ目標に対して追跡させると、見事に重なり合います。
これは、衝突回避の機能が有効になっていないからです。
↑こんな感じに、重なって移動してしまいます(緑色AIと青色AIが重なっています。わかりづらくてすみません...)。
ということで、衝突回避の機能をつけてあげようと思いますが、ここからは有料版が必要です。
以降の内容はアセットストアのA* Pathfinding Project Proがすでに導入済み前提で進めます。
(衝突回避を有料版onlyにする...商売上手ですね!)
衝突回避コアコンポーネントの追加
まずはPro版の導入ですが、アセットの購入後にパッケージマネージャからインポートするだけでOKです。
自分はFree版から上書きインストールしましたが、特に問題はなさそうです。
では、衝突回避に必須なコアコンポーネントRVOSimulator
を追加していきます。
空のオブジェクトを作成、RVOSimulator
をアタッチします。
こちらのコンポーネントは、デフォルト設定のままでも十分使用できるので、今回は設定は特にいじりません。
AIに衝突回避コンポーネント追加
最後に、AIにRVOController
をアタッチしましょう。
すでにAIPath
がアタッチされているので、Radius
、Height
、Center
はAIPathの値が使用されると書いてありますね。
この状態で実行してみましょう!
無事二つのAIが重なり合うことなく、目標を追いかけていますね。
gifの最後に、目標到達後に青いAIが目標地点をぐるぐる回り始めますが、これは緑のAIが目標地点にいていつまで経っても目標地点に到達できないためです。
これを防ぐには、AIPathのEnd Reached Distance
をもう少し大きく設定して、目標地点の手前で移動を終えるようにするのが一つの手段だと思います。
まとめ
結構長くなってしまいましたが、これでA* Pathfinding Projectによる最低限の経路探索ができるようになりました。
正直、ここまでのことならUnity標準のNavMeshでも問題なくできるのですが、ここからA* Pathfinding Project独自の設定を入れていくところがある種本番ですね...!
また余裕のある時に、ページ冒頭のtweetのようなペナルティエリア設定などの紹介ができればいいなと思います。