[UE4/UE5] Gameplay Abilityでボタンを離すまで処理を実行し続ける/Wait Input Releaseはいいぞ/InputとGASのバインド

Post 2022年2月11日金曜日

C++ GameplayAbility UE4 UE5 Unreal Engine


Gameplay Abilities System (通称GAS) の非同期処理についてです。

GAS自体の導入は割愛するので、そちらについては別サイトを御覧くださいませ…。

いつもの注意点ですが、GASはまだ4.27.2時点では正式実装に至っていません。噂ではUE5で正式実装するようなので、今から身につけておくのは大事でしょう。


※注※ この記事ではC++を弄るのが必須です。(ちょっとだけ)

※2/14追記

Youtuberの方で、独自プラグインを制作してC++を弄らずにWait Input Releaseを実現されている方がいましたのでリンクを貼っておきます。(自分は未検証です)


お約束
この記事作成にあたって使用した主なUnreal Engine バージョンUE4.27.2

本日のゴール

ボタンを押している間、処理を実行し続ける


参考文献

UE4ガイドブック(英語)

https://unreal.gg-labs.com/wiki-archives/networking/gameplay-abilities-and-you


もっとちゃんとやりたい方

https://game-dev-study.hatenablog.com/entry/2022/01/04/183632

GASでダッシュアビリティを実装しよう!(アビリティにキー入力を関連付ける) / らくするさん

この記事あるの知ってたら、このクソ記事書かなかったのに…(後悔)

ちょっとこの記事より大変ですけど、もっとちゃんと実装できます。


そもそも非同期処理ってなんだべさ

GASって、実は本来1フレームの処理しかできないらしいんですよね。ですが、Ability Tasksによって処理の幅は大きく広がっています。

例えば有名な「Play Montage and Wait」もAbility Tasksの一つです。

つまり「このアクションが終了したらAbilityを終了する」というような処理がカンタンに実現できます。


ボタンを押している間、処理を実行し続ける

①まずはAbility Tasksを使わない方法からいきましょう。

無限ループ + Event Dispatcher
無限ループとEvent Dispatcherを組み合わせた処理となります。

ここではBP_LittleDragonってのが操作キャラクターのブループリントになりますが、こっち側には下記のように実装します。

これでも十分やっていけそうですね。

②標準のGAS + Gameplay Tasksの場合が次になります。

Repeat Actionを使った処理の継続実行

これで0.01秒の間隔で、100回Print Stringが実行され続けます。(つまり1秒間)

Async Taskが変数にPromoteされていますが、この変数から、End Taskというノードを実行可能で、End Abilityをする前にこの非同期処理を終わらせることができるらしいですが、なんかうまく動きませんでした。(なんじゃらそりゃほい)

やってることはシンプルです。On Perform Actionに実行し続けたい処理を記述しておきます。

イベントディスパッチャーで、割り込んでEnd Abilityを実行します。

このイベントディスパッチャーは、このAbilityの所有者にしておいて、キーを離したときにイベントをCallするように設定すれば動きますね。


ていうかこの実装だと1秒しか実行できないじゃないか、ふざけんなという方もいらっしゃるでしょう。そんな方はOn finishedから直前のところまでノードを引っ張っていけば無限ループができるのでOKです。

無限ループ版
※ちなみに直接Repeat Actionの入力ノードには繋げないので
中継点を作んないとうまくできません

ところがこれでもよくない点があるんですよね。それは実行時間です。Repet Actionの回数自体はAction Numberから引っ張ってこれるんで、そこから計算できるんですが、無限ループにした場合は0からスタートになってしまうんで、計算がめんどいのと、処理時間が厳密にならなくなります。まあそんな厳密である必要はないと思いますが…。

他にはhistoria様のブログで紹介されていたコンボ攻撃の記事を元に、Add Gameplay Tagノードを作って、Wait Gameplay Tag Addノードを使うというのもできそうですね。まぁやってはいませんが。


そこでWait Input Release

このノードはもっとカンタン。InputがReleaseされたタイミングで、On Releaseの処理を実行します。それまでは特になにもしないという感じのノードです。感覚的にはイベントディスパッチャーに近いですね。

※こちらはRepeat機能はないので、無限ループは自分で作る必要はあります。

Wait Input Release
あーめっちゃカンタンですな。

だいぶシンプルになったのと、Time HeldのOutputがあるので、実行時間(というより待ち時間?)も入手可能です。これによって効果を変えたりがカンタンにできるわけですな。


でも

ノードがカンタンすぎないか?と勘の言い方なら気づくと思います。

Inputのキーも指定してないし、どうやってキーのRelaseを設定するんだ?と

これが非常にめんどいのです。


そもそもWait Input Releaseやる価値はあるか?

C++弄れる方ならやって損はないと思います。

私含め、そうでない方は、んーなんとも言えないですね。やってみて意外に簡単でしたが、制約もありますし。

ただめちゃめちゃノードはきれいになります。先程のRepeat Actionの例だと、イベントディスパッチャーの実行のため、対象のアクターのBP内にCallのノードを置かないといけませんが、Wait Input Releaseの場合はそれも不要になります。

だからなんだって話なんですが…

無限ループを作るような処理では、正直
Repeat Actionで十分かなと思ってます。

ただ、Time Held のOutputを使いたいとき(※)や、無限ループではなくただ単に待機してその間にモーションを流し続けるような処理みたいな場合は、こっちのノードのほうが使い勝手がいいです。攻撃の前のタメモーションみたいな感じですね。


※Time HeldはOn Release時にしか取り出せないのでご注意を。Time Heldが10秒になったらこのイベントを実行、みたいなことは(多分)できません。

例えば、Time Heldの値をGameplay Effectで保存しておく、なんてこともできそうですね。タメ攻撃のためのタメ時間を保存しておき、自由に取り出せる、あるいはタメを追加できるという設計です。

参照 : [UE4] [UE5] GameplayAbility について中辛でhttps://qiita.com/koorinonaka/items/47a8b263f4abe90c8eac



さっそくWait Input Releaseを実装していく

さて、では話がそれましたが実装していきましょう。

GAS自体の導入は割愛しますね。


Wait Input Releaseを実装するには、InputとGASを結びつける必要があります。

この作業をすると、ボタンを押すと自動的にGASが起動するようになります。


全体像はこんな感じ

全体像

1.まずは『プロジェクト名.h』あるいは『キャラクター.h』を開いてください。

このコードを#includeのあとに追加します。

UENUM(BlueprintType)

enum class AbilityInput : uint8

{

    // 1 Jump

    None            UMETA(DisplayName = "None"),

    // 2 Cancel

    Boin            UMETA(DisplayName = "GA_Boin"),

    // 3 Throw

    Throw        UMETA(DisplayName = "GA_Throw"),

};

この追加する項目は下記の通り、適宜変えてください。

設定項目
※よくよく見返してみると、参考文献にはプロジェクト名.hじゃなくてキャラクター.hのヘッダーに記述しろってかいてあったので修正。(検証済)


2. それぞれInputの名前に符合するように、プロジェクト設定を弄っておきます。

ただ、私のプロジェクトではJumpは、GASではなく通常のアニメーションBPで設定するつもりなので、プロジェクト名.hの一番上の項目はNoneにしているわけです。

このように、この順番は完全に一致する必要があり、Inputを設定したくない場合はNoneを入れる必要があるっぽいです。

Inputには適当なキーを設定してあげてください。

3. GASを用意してください。

これは言わずもがな。NoneはGASを設定しないことを示しているので、用意する必要はないです。(ていうか用意したら実行されちゃうので用意しちゃだめです)


4. 操作キャラクター.cppを弄っていきます。

操作するキャラクター、例えばThirdPersonCharacter.cppを弄っていきます。

その中に、こんな感じで「SetupPlayerInputComponent」っていう項目があるのでその{ }の中を弄っていきます。


この{ }内の末尾に下記を加えるだけです。

AbilitySystem->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbilityInputBinds("ConfirmInput", "CancelInput", "AbilityInput"));

結果的にこんな感じになります。


5. 操作キャラクターのBPを弄っていきます。

操作するキャラクター、例えばThirdPersonCharacterのBPを弄っていきます。

クラスのデフォルトから、Abilitiesの設定をしますが、その順序がポイントです。今回Inputを設定したいのは、BoinとThrowですが、このBoinとThrowが必ず二番目と三番目になるようにしないといけません。

この順番は、Inputの設定の順番と符合している必要があるんです。

なかなかに面倒な制約です。

一番目、Noneでもいいんじゃないかと思ってましたがうまく動きませんでした。なので適当なGAを設定する必要がありそうです。

プロジェクト名.hでちゃんと一番目をNoneにしていれば、勝手にこの一番上のGAが動くことはないのでご安心を…

4番目以降は、特に気にする必要なくGASを自由に設定できますので。



全部終わったらコンパイルして、エラーがでなければ実行してみましょう。おそらくキーを押すと自動的にGASが実行されるはずです。


あとはその自動的に実行されるGASにWait Input Releaseを設定するだけです。

お疲れさまでした。


なんで勝手にキーとバインドされるの?

これまじで不思議ですよね。

今回処理らしい処理ってこれしか書いてないんですよ

AbilitySystem->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbilityInputBinds("ConfirmInput""CancelInput""AbilityInput"));

つまりこの「BindAbilityActivationToInputComponent」さんがうまくやってくれているのです。この「BindAbilityActivationToInputComponent」さんは、ASCつまりAbility System ComponentさんのC++の中で記述されていて予め用意されている機能なんですね。


制約多くね?

そうなんですよね。ConfirmとかCancel使えないですしおすし。

なのでちゃんとやりたい方は、https://game-dev-study.hatenablog.com/entry/2022/01/04/183632

こちらを参照しましょうね。GameplayAbilityの拡張とかあって、ちょっと大変だけど。


おしまい