Gaming Life

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

UE4 DelayとForLoopについて色々実験した

 ゲーム作ってると数秒おきにアクションを起こす、みたいなことをしたくなる。SideScrollerテンプレートだとこれをSet Timer By Function Nameを使って解決してるが直感的にはForLoopのLoop Bodyに処理とDelayを置いて解決したい。だがこの方法ではうまくいかないことはUE4を多少触ったことがある人はわかると思う。

 というわけでDelayとForLoopを組み合わせた実験をしてみた。まだ完全な理解に至ってるわけじゃないが実験を通じなんとなくはDelayのことがわかったので実験結果を簡単にまとめてみる。

実験

 ForLoopとSequenceとPrintStringとDelayを色々組み合わせてPrintの表示の様子をみる。以降「0.2秒間を開ける」を「0.2s」と表記する。

1 f:id:ai_gaminglife:20171216141253p:plain

結果:Hello->Hello->Hello->Worldと間をおかず表示される

2 f:id:ai_gaminglife:20171216141324p:plain

結果:World->0.2s->Hello

3 f:id:ai_gaminglife:20171216141606p:plain

結果:Hello->Hello->Hello->World->0.2s->Hiroyuki

4 f:id:ai_gaminglife:20171216141651p:plain

結果:0.2s->Hello->0.3s->World

5 f:id:ai_gaminglife:20171216141734p:plain

結果:0.2sec->World->0.3s->Hello

6 f:id:ai_gaminglife:20171216141850p:plain

結果:0.2s->Hello->0.3s->World

7 f:id:ai_gaminglife:20171216141945p:plain

結果:0.2s->World->0.3s->Hello

 これだけ結果を見せればDelayがどんな挙動をするかは分かるはず。某ブログではDelayを「中断処理」ではなく「登録処理」と考えるとよいと書いてあったがまさにそのとおりだと思う

それでも

 Set Timerは個人的にわかりにくいのでちゃんとDelayで待ってからLoop Bodyの処理を順にできるFor Loopがほしい。そこで更に調べるとこんなマクロを組めば直感どおりの挙動をしてくれることが分かった。

f:id:ai_gaminglife:20171216142441p:plain

 とりあえずFor loop With Delayと名付けた。これを使えば

f:id:ai_gaminglife:20171216142533p:plain

結果:Hello->0.2s->Hello->World->0.2sec->Hello

と表示される。

 しかしこのマクロもそこそこ複雑なのでやはりSet Timerを使ったほうがよいという結論に至った。

 (12/18追記)

 Twitterですごくいい方法を教えてもらった。

 知らなかった…すごくいい方法なので今後これを使っていきたい

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月中にはこのゲームを遊べるようにしたい。

ゲーム向けな美味しい乱数を生成する

 色々思うところがあって乱数について学んだことをスライドにまとめてみた。

 本来大学のLT大会で発表予定だったのですが長過ぎるのでブログで公開する。(次回のLT大会でこれのショートor発展verをやる予定)

 ネタが多めだが数式は殆ど使ってないので読みやすいはず。感想、間違ってるとこなどあればコメントTwitterで教えてください。

UE4 ちょっと見栄えのいいワープ機能を実装してみる

先日の第三回UE4GameJamに参加して以降、他の人の組んだBPを読み込んだり、いろんな機能を実験していたのだがその中で色々応用の効きそうなBPが実装できたので自分用のメモがてらブログにまとめる。

環境

使用ver: UE4 1.8.2

使用テンプレート: TwinStickShooter

ワープポイントのエフェクト

 今回は無料で手に入る「InfinityBladeEffect」内の「P_Summon_Portal」を利用。InfinityBladeEffectは実用的な上中身を見てるだけでCascadeの勉強になるので神。

ワープ機能を実装する

 これは ヒストリアさんのブログ が詳しいので参考にされたし。ただしこの記事では一つのActorを継承したBP「BP_WarpPoint」に「Box Collision」を二つ持たせて解決してるがこれでは使い勝手が悪いので、いわゆる「ダイレクトブループリント通信」を利用した。

 「BP_WarpPoint」型の変数を用意してDetailsから「Instance Editable」にチェックを入れる。これにより設置したレベル上でその変数の中身を変えることができる。 (ダイレクトBP通信については公式 がとても詳しいのでそちらを見るべき )

 レベル上にこの「BP_WarpPoint」を二つ設置し(それぞれ①、②とする)Details->Defaultから先程用意した「BP_WarpPoint」型の変数に①には②、②には①をセットする。これでヒストリアさんのブログで紹介されていたワープ機能と同等の機能が得られるはず。

ワープ中入力を受け取らないようにする&黒画面フェードアニメを実行する

 ワープ中に移動できるようにすると変なので

ワープポイントに入る ->移動を禁止 ->黒画面フェードアウト ->ワープ ->黒画面フェードイン ->移動禁止を解除する

という一連の機能を実装してみた。以下、順を追って説明する。

①移動を禁止する  いろいろな方法があるが今回は「SetActorTickEnabled」を利用した。このノードは「Enabled」がFalseの時入ってくる「Target」のEvent Tickが実行できないようにする。今回はこれで問題なく動くが、ワープ中もTickを実行したい場合はフラグを立てて分岐させれば良さそう。

②黒画面フェードアウト、フェードイン  これは alwaiさんのブログ が非常に詳しいです。ほぼこのサイトのままなのでフェードアウト、フェードインアニメーションの作成の仕方は省略。

 ブログの手順に従って作成したUMGアニメーション、「Fadeout」、「Fadein」を以下の関数で呼び出します。

f:id:ai_gaminglife:20171129022103p:plain f:id:ai_gaminglife:20171129022120p:plain

 最終的には以下のようになりました。

f:id:ai_gaminglife:20171129022131p:plain f:id:ai_gaminglife:20171129022135p:plain

 実際動かした動画。

まとめ

  • InfinityBladeEffectは神。
  • ほぼ独学でここまでやってきたけど3ヶ月も使ってると流石に慣れてきた。
  • 劇場版アニメゴジラ早く見たい

 最近はメトロイドライクなゲームの開発がストップしているので次回もTwinStickShooterテンプレートを改造するか、乱数について色々実験するかも。

UE4進捗1-4 MaterialInstanceDynamicを使ってマテリアルの色を変える

 pythonの勉強を始めたせいでUE4を触る時間が少なくなっているがブログを更新していかないと企画すらボツになってしまいそうなので大して変わってないが記事を上げる。    Blenderで自作のモデルを作成、マテリアルの編集は主にUE4でやっている。中身は殆ど aiweiさんのブログ のままなので実装はそちらを参考にしてください。変更点はこのモデルは贅沢にもマテリアル二つで構成されているため「Get Material」「Create Dynamic Material Instance」のElement Indexを1に変更したことくらい(これで一時間ほど悩んだ)。

UE4進捗1-3 爆弾を打ち出す方向を決める(1)

現在作業中。Twitterで経過報告はしてますが中身はまだ見せられるようなものではないので、もう少しブラッシュアップしたらブログ更新します。   最初独力でできる気がせず、 UE4ANSWERHUBで質問させて頂きました。するとすぐに詳細な回答が帰ってきました。回答してくださったPAVI_PAVIさん、unaunagiさんに対してこの場を借りてお礼申し上げます。

UE4進捗1-2 SideScrollerテンプレートでエイム方向を制御する

 こんにちは。未だにブログでの口調が決められません。暫く統一感のない文章になると思いますがじき直して行くので許してください。

 前回記事で、UnrealEngine4付属のSideScrollerテンプレートを少し改造して弾を撃てるようにしましたが、プレイヤーが向いている方向にしか撃てない、テンプレートのままのBPだと同じX軸上にしか敵はいないのに、左から右にキャラの向きを変える時一時的に正面を向いてしまい、その向きで弾を撃つと手前方向に飛んでしまうという不具合が有りました。今回はそれを直していこうと思います。

プレイヤーが正面を向いてしまう問題を解決する

f:id:ai_gaminglife:20171019004042p:plain

 これはすぐ解決しました。「Input MoveRight」の出力ピンであるAxis Valueに-90をかけた値を「Make Rotator」ピンのZにつなげてRotatorを得ます。それを「set ActorRotation」に繋げば終わり。上の画像では間に色々処理をしていますがエイム制御に用いているので次で説明。

弾を8方向に発射できるようにする

 まずコンテンツブラウザからblueprint -> enumrationを選んで新たに作成しましょう。わかりやすいように AimDirection と名前を付けました。  enumとは列挙型のことで、わからない方は各々調べてください。なんせ私もこれを作っている途中で知ったので……  AimDirectionの中身はこのようになっています。

f:id:ai_gaminglife:20171019004058p:plain

 以降は込み入ってるのでざっくり方針を説明して実際のBPの画像を示すことにしましょう。

  1. Project Setting -> Input から MoveUp を新たに定義。中身はW(Scale:1.0)、S(Scale:-1.0)。

  2. 「InputAxis MoveRight」の出力ピンからBranchで場合分け。それに応じて SideScrollerCharacter 内で新たに定義したAim Direction型の変数 Horizontal Aimをセットする。

  3. 「InputAxis MoveUp」の出力ピンからBranchで場合分け。それに応じてAimDirection型の変数 Vertical Aimをセットする。

  4. 「Event Tick」から「Switch on AimDirection」を使って9パターンに実行ピンを分ける。AimDirection型の変数 FinalAim Directionに対応する文字をセット。

  5. 確認のためFinal Aim Directionの中身を表示する関数「Debug Aim Direction」を実行。

  6. 再び「Switch on AimDirection」を実行し、それぞれ対応するVector型変数 Aim Vectorをセットする。 例:「Up」の時は(0.0, 0.0, 1)、「Right Down」の時は(0.0, 1.0, -0.3)

  7. 弾のBP、BP_ShotFireに新たに Vector型変数、 SpawnVectorを定義。これは「編集可能」と「スポーン時に公開」にチェックを入れる必要がある。

  8. 7の作業をするとスポーン時にActorに引数を渡せるようになる。Spawn Actorに、Aim Vectorをコピー。

  9. 「Construction Script」に「Set Veloctity in Local Space」を繋げる。これでProjectileMovementコンポーネントの方向を動的にセットできる。引数にはSpawn Vectorを使用。

f:id:ai_gaminglife:20171019004111p:plain f:id:ai_gaminglife:20171019004123p:plain f:id:ai_gaminglife:20171019004138p:plain f:id:ai_gaminglife:20171019004151p:plain

これで出来たはずです。

振り返り

  • enumは超便利。
  • バクダンはこの方法だと投げ飛ばす感じにならないので今後の修正点。
  • 動きながらしか弾が打てない。