Gaming Life

一日24時間、ゲームは10時間

UE4 ビヘイビアツリーで巡回+攻撃する敵AIを作るのに苦労した話

 UE4にはビヘイビアツリーと呼ばれるAIを簡単に作れる機能がある。 人工知能の作り方 によるとXBoxで2004年に発売された『Halo2』の開発のため発案、GDCで発表されて以降ゲームAIの分野に置いて最もポピュラーな方法として確立されたらしい。

 ビヘイビアツリーではキャラクターの行動をBehavior(日本語で振る舞いという意味)を基本として捉えツリー構造で表現する。原理としては、ルート(根)から実行順序を決める中間ノードを介して、枝の末端に実際の振る舞いを記述。中間ノードに従い末端の振る舞いを順に実行するだけの実にシンプルなもの。UE4ではこのツリー構造を視覚的に作ることができるので非常に楽にAIを実装することができる……というのが初心者なりの理解。  ビヘイビアツリーの更に詳しい原理や成り立ちは前述の本を読めばだいたい理解できるし、UE4での実装の仕方は皆大好き極め本や 公式のクイックスタートガイド(日本語化されている)を見れば大丈夫。

 だと思ってた。

 極め本や公式クイックスタートガイドを見ながら、上記程度の単純なAIを実装できたがツイートの日付を見ればわかる通りたったこれだけでかなりの時間を要してしまった。復習のため、また、今後勉強を始める方の為に躓いた点と解決策を書いておく。

実装したい機能

  • 使用テンプレート: TwinStickShooter
  • バージョン: UE4 ver4.18.1
  • 普段は指定の巡回ポイントを順に回る。巡回ポイントについたらその場で数秒停止。周りを監視する
  • プレイヤーを発見したら1秒程度停止する(攻撃準備の表現)。その後その場にとどまり0.2秒刻みでProjectileを発射して攻撃する
  • プレイヤーが視界から消えたら攻撃を止め5秒その場で停止、見つからなければ巡回行動に戻る

 完成版ビヘイビアツリー(画像)

f:id:ai_gaminglife:20171213212358p:plain

1.宙に浮いているキャラクターの移動

 極め本や公式のチュートリアルでは地面に足を付けたCharacterを動かしているのでそのままナビメッシュを利用すれば移動できる。だが今回使用するTwinStickShooterテンプレートは宙に浮いているPawnのためそのままではナビメッシュを利用できない。「Move to Location」や「Move to Actor」の「Use PathFinding」のチェックを外せばナビメッシュなしに移動できるのだが直線的にしか移動できないので後で不具合が起きそう。そもそもこの問題に直面していた時チェックを外しても移動すらできなかった。

 これは画像のようにCharacterクラスを無理やり使って解決した。

f:id:ai_gaminglife:20171213212423p:plain

 見た目上宙を浮いていればいいじゃないかと考えCharacterクラスのSkeltal Meshを空(カラ)に、新たにStatic Meshコンポーネントを追加してコリジョンを画像のように置いて解決した。ちなみに「Use PathFinding」のチェックを外しても動かなかったのはCharacter Movement Componentを追加するのを忘れてただけだった(なんじゃそれ)。

2.プレイヤーを最初に発見した時のみ起こす行動の分岐方法

   プレイヤーを最初に見つけた時のみ構えの動作をとってから(まだ構えのモーションは未実装)攻撃に移行させようとしたのだがこの分岐をどうやればいいのか最初は全くわからなかった。

 今回はブラックボードに新たにBool型Key「FirstSearch」を追加して解決。

 AIControllerで「Set Value as Bool」を使ってTrueに初期化しておき、   f:id:ai_gaminglife:20171213212501p:plain

 自作のデコレータ「TrueCheck」「FalseCheck」でKeyの情報を取ってきて分岐させた。

f:id:ai_gaminglife:20171213212523p:plain f:id:ai_gaminglife:20171213212529p:plain

(PerfomConditionCheckAI関数をオーバーライドしている)

 後は適切なときに「FirstSearch」をTrue/Falseに切り替えれば良いだけ。

 3.IsAtLocationについて

 チュートリアルのこの頁 では「IsAtLocation」というデコレータを使っているのだが これがどういう意味なのかわからない。未だによくわからないので今回は使わなかった。(どなたか教えてくださると助かります)

(追記)alweiさんにTwitterで教えて頂きました

 だそうです。確かにこれは便利だ…時間があればこれを使ったものも作りたい。

 

4.Projectileをプレイヤーが視界にいる間連射する

 これはビヘイビアツリーのAttackタスク内で以下の画像の通りにして実現した。

f:id:ai_gaminglife:20171213212552p:plain

 プレイヤーの位置を格納している「TargetLocation」から自身のLocationを引いて正規化(normlize)した値と「FindLookatRotation」で自身からプレイヤーへの向きを得て「FireShot」関数を呼び出す。  FireShotの中身はこんな感じ。ほぼテンプレそのまま。 f:id:ai_gaminglife:20171213212558p:plain f:id:ai_gaminglife:20171213212616p:plain

 このAttackタスクを実行後にWaitタスクが実行されるように置いて、連射できるようにした。

まとめ

  「IsAtLocation」デコレータや タスク内でTimerやDelayを実行するとどういう扱いになるかなどまだ良くわからないところもあるがとりあえずここまでで最低限やりたかった機能が実装できた。独自の方法のためもっと効率のいい方法はあると思うががむしゃらにやってみると案外うまくいくのでとりあえず効率無視でやってみる、と言うのが大切だと改めて感じた。

 12月中にはこのゲームを遊べるようにしたい。