PavilionDV7の雑多なやつ

Qiitaから移行しました。UE4に関する記事から興味のあることまで色々書きます。

【UE4】Flow Field Pathで大量のエージェントを移動させる

はじめに

World of War Z をプレイしていて大量のゾンビをナビゲーションする方法に興味を持ったので調べてみました。

wwzgame.com

一度のラッシュで襲ってくるゾンビはざっくり200~400体程度かと思います。それだけのゾンビがそれぞれプレイヤーに向かう経路探索をしているとは考えにくく、そもそも大量のゾンビが経路探索をしたとしても求められる経路はほとんど同じです。

一度の経路探索で数百、数千のゾンビが目標への経路を共有できればかなりの負荷を削減出来ることになります。そんな課題に有効な手段である Flow Field Pathfinding を調べて実装してみたのでプロジェクトの解説をしていきます。

結果

プロジェクトはこちら

バージョン:UE4.24.3

1drv.ms

Flow Field とは

基本的にはグリッドベースで動作するアルゴリズムであり、ゴールへと向かう正規化されたベクトルを格納したデータを生成します。これをFlow Fieldと呼びます。Flow Fieldを可視化すると目的地へ水が流れるような経路を見ることが出来ます。

f:id:PavilionDV7:20200505225146p:plain

Flow Fieldは一度生成すると全てのセルがゴールへと向かうベクトルを持つためAIがどのセルでスポーンしようともゴールへと移動出来ます。 グリッドベースなので複雑なレベルであればグリッドの解像度を上げることで詳細なFlow Fieldを得られ、処理負荷が気になるなら解像度を下げれば簡単に解決出来ます。Flow Fieldの詳細度と処理負荷はトレードオフの関係ですがエージェントの移動ベクトルの計算方法を工夫することで高い解像度を必要とせず滑らかな移動を確保することも出来ます。(Bilinear補間による移動ベクトルの計算)

Flow Field の生成

Flow Fieldは単純なステップで生成できます。

  1. 任意の解像度のグリッドを生成
  2. グリッドにゴールを与える
  3. 全てのセルにゴールまでの距離を記録(改変したダイクストラ法を利用)
  4. ゴールから周囲のセルを取得し最も距離が少ないセルへと向かうベクトルを作成
  5. 4番を全てのセルがベクトルを作成するまで繰り返す

これらの手順はソースコード SectorFlowFieldGrid.h/cpp にて実装されています。詳細な実装箇所は以下の通りです。

  1. FSectorFlowFieldGrid::Initialize
  2. FSectorFlowFieldGrid::GenerateFlowField
  3. FSectorFlowFieldGrid::GenerateDijkstraGrid
  4. FSectorFlowFieldGrid::GenerateFlowField

巨大なフィールドに対応させる

これまでに紹介したFlow Fieldの生成方法でFlow Field Pathfindingは実装出来たことになるのですがいくつか課題があります。

  1. グリッドの解像度が高くなったとき(500x500や1000x1000等)は無視できない負荷が掛かる
  2. フィールドが大きくなるにつれ「通る必要の無いエリア」が生まれそれが大部分を占めるようになる

f:id:PavilionDV7:20200506104249p:plain

特に2番目の課題は厄介です。どうにかして「確実に通過するエリア」「通過する必要のないエリア」を割り出さなくてはなりません。 この課題に対しては グリッドの階層構造 HPA*アルゴリズム を取り入れることで解決出来ます。

グリッドの階層構造

大きなグリッドを小さなグリッドに分割し1グリッドあたりの処理負荷を分散します。(100x100 を 10x10x10 に分割する)

プロジェクト中のグリッド階層構造の様子は以下の通りになります。

f:id:PavilionDV7:20200506123102p:plain

HPA* アルゴリズム

HPA*は https://www.researchgate.net/publication/228785110_Near_optimal_hierarchical_path-finding_HPA で登場したアルゴリズムであり正しくは Hierarchical Path-Finding A-star と呼びます。

HPA は経路探索そのものは通常のAアルゴリズムと同じですが経路探索に至るまでの準備が工夫されています。

-- ここから準備 --

  1. 大きなグリッドを小さなグリッドに分割(プロジェクトではセクターと呼ぶ)
  2. セクター間にリンクを作成する(プロジェクトではポータルと呼ぶ)
  3. セクター内のポータルネットワークを構築(同セクターのポータル同士で経路探索を行い通行可能かチェックする)

-- ここまで準備 --

-- ここから実行時 --

  1. スタートとゴールが属するセクターを割り出す
  2. スタートからゴールへとポータルを使った経路探索を行う

-- ここまで実行時 --

1番から3番は実行前に処理が可能ですのでグリッドやリンクデータを何らかのファイルに保存しておけば実行時には経路探索時の処理のみ行えば良いです。

f:id:PavilionDV7:20200506151059p:plain

上記の手順の実装箇所は以下の通りです。

  1. FWorldCellGrid::Initialize
  2. FWorldCellGrid::GenerateSectorPortal & FWorldCellGrid::GenerateSectorEdgePortal
  3. FSectorFlowFieldGrid::AddIntraPortal & FSectorFlowFieldGrid::IsTraversalAllowed

ポータルを生成する

ポータル生成の手順は以下の通りです。

  1. セクターの辺を取得
  2. 取得した辺と隣接するセクターの辺を取得
  3. 辺に障害物が無いかチェック
  4. 障害物があった場合、障害物が無かったセルの中心にポータルを生成
  5. 1から繰り返す

f:id:PavilionDV7:20200506154726p:plain

f:id:PavilionDV7:20200506154732p:plain

実装箇所は以下の通りです。

1番~3番. FWorldCellGrid::GenerateSectorPortal

4番. FWorldCellGrid::GenerateSectorEdgePortal

ポータルネットワークの構築

ポータルネットワークの構築は以下の通りです。

  1. セクターが持つポータルを取得
  2. 取り出したポータルと他のポータルとの経路探索を行う
  3. 経路が存在する場合、接続済みのポータルとして記録
  4. 経路が存在しなければそのまま
  5. 1から繰り返す

f:id:PavilionDV7:20200506155604p:plain

図はネットワーク構築の様子。 1番と2番は通行可能なのでそれぞれ接続しますが1番と3番については完全に分断されており到達は不可能なため接続は行われません。


実装箇所は以下の通りです。

1番~4番. FSectorFlowFieldGrid::AddIntraPortal

2番の経路探索. FSectorFlowFieldGrid::IsTraversalAllowed

Flow Field x HPA*

Flow Fieldの生成とHPA*を組み合わせた際の最終的な手順は以下の通りです。

  1. 階層構造のグリッドを構築
  2. ポータルを生成
  3. ポータルネットワークの構築
  4. 上位層のグリッドにスタートとゴールを与える
  5. スタートからゴールまでに通るセクターを求める
  6. 求めたセクターに対しFlow Fieldを生成

HPA* アルゴリズムの項でも書きましたがポータルネットワークの構築までは前処理として事前に処理を実行して結果を記録することが出来ます。

おわりに

Flow Field と HPA* を組み合わせることで大きなフィールドにも対応でき無駄を大きく省くことが出来ました。実行時には全ての手順を処理する必要はなくいくつかの工程は前処理で省くことが可能です。 配布しているプロジェクトには実装していませんがフィールドの動的な変化についても

  1. 変化のあったセクターを検知
  2. ポータルネットワークの再構築
  3. Flow Fieldの再生成

という手順で実現出来るかなと思います。

プロジェクトの制約として 平面もしくは緩い起伏のある地面 での使用を考慮しているためスロープや螺旋階段といったレベルには対応していません。今後はそのような立体的な構造にも対応していきたいと思います。

参考資料

http://www.gameaipro.com/GameAIPro/GameAIPro_Chapter23_Crowd_Pathfinding_and_Steering_Using_Flow_Field_Tiles.pdf

http://www.gameaipro.com/GameAIPro/GameAIPro_Chapter24_Efficient_Crowd_Simulation_for_Mobile_Games.pdf

https://leifnode.com/2013/12/flow-field-pathfinding/

https://howtorts.github.io/2014/01/04/basic-flow-fields.html

https://www.slideshare.net/CarlosFuentes102/hpa-59931470

https://upcommons.upc.edu/handle/2099.1/24157