そうだ、ゲームを作ろう

現状や学んだことなど記録するブログ。間違ってたらごめんね

AIで色々悩んでみた:NavMesh編

f:id:wvigler:20191114111324p:plain

ではNavMeshです。

NavMeshはAIをもったPawnが動作と移動ができる範囲を定義します。なのでいくらBehaviorTreeでAIを組み込んでも、NavMeshがなくてはうんともすんとも言いません。

 

・NavMeshBoundsVolume

NavMeshの基本はNavMeshBoundsVolumeというVolumeです。Geometoryなどと同じくBrushSettingのパラメーターカテゴリで形状とXYZを決めます。あんまり複雑な形状にするメリットというのも思いつかないので基本的には初期値のBoxで十分だと思いますが…

NavMeshはNavMeshBoundsVolumeで指定された範囲に地面を這うようにして展開されます。Viewportで"P"を押すか"Show→Navigation"にチェックを入れることで、現在NavMeshの展開されている範囲を表示させることができます。ちなみに時々何かの加減でNavMeshの生成が止まってしまう場合がありますが、NavMeshBoundsVolumeを少し動かしてやることで再生成されます。

f:id:wvigler:20191119090802p:plain

ThirdPersonTemplete全体にNavMeshBoundsVolumeを配置したところです。緑色の部分にNavMeshが生成されています。

ちなみにNavMeshは地面に沿って"繋がっている範囲"を表示してくれるので、AIとは関係無くても"ちゃんとレベルが全部繋がっているか?"の確認にも使ったりします。

 

NavMeshBoundsVolume(と後述のNavLinkProxyもですが)にはSupportedAgentsというパラメーターがあります。

f:id:wvigler:20191119120625p:plain

これは通常allで固定されているのですのが、ProjectSettingのEngine->NavigationSystem->Agents->SupportedAgentsで追加することができます。

f:id:wvigler:20191119121340p:plain

これはPawnの種類によってNavMeshを分けるという機能です。かなり設定できる項目も多いので、詳しくは

qiita.com

このあたりを参考にしてください。

 

・RecastNavMesh-Default

NavMeshBoundsVolumeを生成するとRecastNavMesh-DefaultというActorが自動的に配置されます。これはNavMeshをViewportに表示させる際、どのように表示させるかや、生成の細かい設定を司るActorです。様々な設定項目があるのですがそれはドキュメントにおまかせするとして、ここでは例として上の画像の問題を解決しましょう。

上の画像を見ると、NavMeshが階段部分で途切れていることが分かります。これは階段部分がNavMeshの生成する高さに対して急すぎて、ここだけ壁のように認識されているということです。実際問題としてこのマップでAIを設定してもPawnは階段の上には登らず、入口付近で引き返してしまいます。

ではどうするかというと、要はNavMeshの生成する高さをもう少し高くすればいいのです。RecastNavMesh-DefaultのGenerationカテゴリ->CellHeightを調整します。

f:id:wvigler:20191119093225p:plain

初期値では10ですが、これを30に調整してやると…

f:id:wvigler:20191119093516p:plain

このように綺麗に繋げることができます。

 

・NavLinkProxy

先程、初期設定においてThirdPersonTempleteの階段部分は"壁"のように認識されているからAIは通れない、という話をしましたが、では実際に壁になっていたらどうでしょう?

例えば人間が操るキャラクターであれば途中に崖があっても飛び降りることができます。しかしAIはNavMeshが途切れているので飛び降りることができません。そこは"通り抜けられない場所"と認識されているからです。では先程のようにCellHeightで高さをもの凄く上げてみたらどうでしょう?今度はNavMeshがPawnに引っかからなくなるためAI自体が動作しません。実はMaxStepHeightというRecastNavMesh-Defaultのパラメーターを上げると繋げること自体はできたりします。(物凄く汚いNavMeshになりますが…)そうすると崖から飛び降りることはできるかも知れません。しかし別の問題が発生します。

AIにとってNavMeshが繋がっている場所は"通り抜けられる場所"です。しかし"崖"というものは下方向から見れば"壁"であるため、飛び降りることはできても通り抜けることはできません。崖の下にいるAIは登れるはずもない壁に対して延々体当たりをし続けます。彼にとってそこは"通り抜けられるはずの場所"だからです。

こういった問題を解決するのがNavLinkProxyというActorです。これはふたつの地点を設定し、そこに限定して擬似的にNavMeshを繋げるという動作をします。

f:id:wvigler:20191119102309p:plain

NavLinkProxyを貼ったところです。判定の大きさや位置、数も設定することができます。これでRightとLeftの二点間だけは擬似的にNavMeshが繋がっているので、AIはここを通り抜けることができます。しかし、これだけではLeft側からRight側へも繋がっていると認識されてしまうため、崖の下側からも登ろうとしてきてしまいます。

これを解決するにはDetailのSmartLinkカテゴリ->LinkDirectionを設定する事が必要です。

f:id:wvigler:20191119103147p:plain

この例の場合はRightToLeftを選択することで、崖の下側からPawnが登ろうとしてきてしまう問題を解決することができます。

ちなみになのですが、例えば「崖の下に来たらジャンプで登りたい」などということをしたいなと思った場合にはEventReceiveSamartLinkReachedというNavLinkProxyBP内のEventから実装することができるようです。

f:id:wvigler:20191119104321p:plain

 

・NavModifierVolumes

NavMeshは通常Collisionによって遮蔽されます。これで問題ない場合もあるのですが、マグマや毒沼などCollisionがなくても通り抜けられない設定にしたいことがあります。その場合出てくるのがNavModifierVolumesです。これはGeometryのsubtractive設定のようにNavMeshをくり抜いたり、その他特殊な設定をするためのVolumeです。

ということで取り敢えずNavMeshの中にこのVolumeを置いてみましょう。

f:id:wvigler:20191119110143p:plain

はい。ということでこういう感じのものです。とても分かりやすいですね。

このVolumeにはいくつか種類があり、AreaClassというパラメーターで選択します。

f:id:wvigler:20191119110933p:plain

デフォルトの設定はNavArea_Nullです。これはNavMeshを無効化してくり抜いたような形を作り出します。

NoneとNavArea_DefaultはどちらもNavMeshに対して何もしません。

NavArea_LowHeightは情報が少ないのですが…Nullと基本的には変わらないような…一応NavMesh自体は残るNullという感じですかね?必要な時あるのかな?NavLinkProxyの方にもこれ系のパラメーターがいくつかあるのでそれとの兼ね合いかも?違うかな?

NavArea_Obstacleはちょっと特殊でAIに「なるべくなら通りたくない場所」を設定します。要はAIがそこを通るための"コスト"が増えます。ふたつ重ねると更に通りにくくなるためある程度制御可能です。

 

以上ですが、うーむ…NavMeshはある程度短くなると思ったんですが、なんか今までで一番長いような…

これからやる知覚系が多分一番長くなるんですがね~。