そうだ、ゲームを作ろう

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

第15回ぷちコン「たぬ吉の大冒険」振り返りその3<GameplayEvent、GameplayTask編>

注》この記事はこの記事の続きです。

この記事では第15回ぷちコンの振り返りとして、「GameplayEvent」と「GameplayTask」の解説をしていきます。

 

お品書き

・GameplayEventについて

・GameplayTaskについて

・GameplayEvent、GameplayTaskを実装してみる

注》今回解説する部分は結構ネットワーク分野と関連しているため、自分でも理解できていない部分が比較的多いです。ご容赦ください。 

 

・GameplayEventについて

GameplayEventはGameplayAbilitySystemにおいてGameplayTagを使用した特殊な使い方をできるStructureです。GameplayEventの情報はよくPayloadと呼ばれ中身はこうなっています。

f:id:wvigler:20210501033754p:plain

EventTagはGameplayEventにおいて最も中心的な役割を持つGameplayTagです。これについては後で解説します。

そして実は以下の項目全ては一応の型は決められているものの、どう使うかはユーザーに委ねられています。例えばInstigatorというActor型の項目があります。攻撃などの時は例えば攻撃する側がここに登録されることが多いし、その方が問題も起きにくいと思いますが、しかし攻撃される側を登録しても全く影響はありません。このデータ型をどのように使うかはほぼ完全に使用者次第となっています(少しだけ例外はありますが…)。そしてもちろん普通のVariable変数として扱うことも可能です。

f:id:wvigler:20210501042712p:plain

さて、問題は一番上のEventTagでして、これだけは少し毛色が違います。GameplayEventは基本的にSendGameplayEventToActorというノードを介して扱います。

f:id:wvigler:20210501042218p:plain

SendGameplayEventToActorは特定のActorにGameplayEventの情報を送るものですが、EventTagに特に何の指定もしていない場合、EventTagにはSendGameplayEventToActorのEventTagが入ります。ではActorに送ったPayloadですがその受け取りはどのような形で行われるのでしょうか?

使い方の一つはGameplayAbilityのActivateです。そう、GameplayEventからGameplayAbilityをActivateすることができます。それではこれから"GAS_Test"プロジェクト内でZキーを押すとActivateしていた攻撃を今度はGameplayEventからActivateさせられるようにしてみましょう。

f:id:wvigler:20210501043500p:plain

まずGASCharacterのEventGraph内です。元の状態はこうなっています。これを…

f:id:wvigler:20210501043804p:plain

このようにします。Payloadはとりあえず何も入れなくて大丈夫です。もちろんこれだけではZキーを押しても何も起こりません。次はGPA_Attackに移動します。

f:id:wvigler:20210501044023p:plain

さあこれが元の状態です。まずは先頭のEventActivateAbilityを削除します。このノードがあると絶対にGameplayEventによるActivateは起こらないようになっています!必ず削除してください!そして今度はEventActivateAbilityFromEventというノードを同じように配置します。

f:id:wvigler:20210501044342p:plain

しかしまだ何も起こりません。GameplayEventによるActivateを引き起こすには更にClassDefaultsのTriggersカテゴリのAbilityTriggersを追加し、TriggerTagにキーとなるEventTagを設定します。それから、これは初期状態でそうなっているので問題ないかと思いますが、TriggerSourceはGameplayEventにしておきます。

f:id:wvigler:20210501045031p:plain

さあ、これで準備が整いました。実行してZキーを押してみましょう。以前と同じように攻撃が発動するはずです。この方法の利点はPayloadによって多くの情報をAbilityに持ち込める点にあるでしょう。

f:id:wvigler:20210501045640p:plain

これだけの情報をActorからAbility内に持ち込めます。また、この方法でAbilityを呼び出すと、AbilityのClassDefaults->TagsカテゴリにあるSourceRequired/BlockedTagsとTargetRequired/BlockedTagsに意味が生まれます。

f:id:wvigler:20210501050139p:plain

PayLoad内にInstigatorTagsとTargetTagsという項目がありますが、これがつまりAbility内におけるSourceTagsとTargetTagsですRequiredの場合はInstigatorTagsやTargetTagsにそのTagが登録されていなければ発動せず、逆にBlockedの場合はInstigatorTagやTargetTagsにそのTagが登録されていれば発動しません。

GameplayEventについてはWaitGameplayEventというノードを使う活用方法もあるのですが、WaitGameplayEventはGameplayTaskに属するノードなので、次はGameplayTaskについて解説します。

・GameplayTaskについて

正直良く分かってないんだ。

GameplayTaskはGameplayAbilitySystem内で非同期処理を実現するための仕組みで、特にAbility内のみで呼ばれるものはAbilityTaskと呼ばれます。というか自作しない限り全部これです。

Taskは自作することもできますが、すでにかなりの数がノードとして実装されています。GameplayAbility内で"wait"で検索してみると多さが分かるでしょう。まあDelegateのようなことをするものだということは分かるし、名前である程度使い方が分かるものも多いですが、Targeting関連の部分は正直自分にはよく分かりませんし、更に言うと…

f:id:wvigler:20210501053104p:plain

この2つのノードの違いは何かと聞かれても、僕には説明できません。AbilityTaskノードには全て付いていますが、AsyncTaskのPinが付いている理由も分かりません。繋がるノードは多いようなので、恐らく何かできるんでしょう(適当)。

自作についてはえーっと…(目が泳ぐ)…あー…

ま、とはいえある程度使い方が分かるノードも存在しています。今回はその中でGameplayEventの情報を受け取るWaitGameplayEventを使用していきます。

・GameplayEvent、GameplayTaskを実装してみる

今まで作成してきたAbilityの中身はおおよそこんな感じでした。

f:id:wvigler:20210501055127p:plain

PlayMontageにいろいろなExecutionPinが付いているので、EndAbilityを繋げることができていますが、ではこういったPinが無かった場合どうすればいいでしょう?

具体的にはTimelineを使用してアニメーションを作り、Timelineが終了したらEndAbilityしたいのです。しかしTimelineはAbilityでは扱えずActor内での実装になります。まあTimelineを発動するだけなら特に問題にはならないのですが、問題は発動したTimelineの終了をどうAbilityに知らせるか、です。現状でこれを通知する手段がありません。

今回はGameplayEventとGameplayTaskであるWaitGameplayEventを使用してこの通知を行い、「武器が大きくなる」というTimelineを使用したアニメーションを作成します。

まずは下準備として装備した武器の大きさを小さくします。

f:id:wvigler:20210501060218p:plain

0.5倍にすればまるでナイフのようになります。それではTimelineを作成しましょう。

f:id:wvigler:20210501094348p:plain

Timelineの中身はこうなっています。

f:id:wvigler:20210501094959p:plain

新しいAbilityを作成します。名前はGPA_ExpandWeaponとしました。

f:id:wvigler:20210501070601p:plain

f:id:wvigler:20210501073407p:plain

とりあえずノードをここまで組みます。本来はBPInterfaceを用意した方がいいのですが、面倒なのでCastしました。続いてWaitGameplayEventを使用し、

f:id:wvigler:20210501081212p:plain

このようにします。Payloadに情報を乗せることはできますが、この場合は必要ありません。Tagsカテゴリはこのように…

f:id:wvigler:20210501082903p:plain

これをAbilityListに登録して…

f:id:wvigler:20210501091700p:plain

CキーでActivateするようにします。

f:id:wvigler:20210501094048p:plain

そしてTimelineが終了したらSendGameplayEventToActorで"Event.OnTimelineFinished"のTagを自身に送るようにします。

f:id:wvigler:20210501094739p:plain

実行してみると…

f:id:wvigler:20210501095059p:plain

Cキーを押す度に剣が大きくなったり小さくなったりしています。このようにActor内のTimelineの状態を取得するのにGameplayTask、GameplayEventは使用できます。

 

ふー、GameplayTaskとGameplayEventは比較的楽でしたね。というかTaskについてはまだ分かっていないことが多くて下手なこと書けないというのはありますが…ネットワークをうまく使えればGameplayTaskをC++でゴリゴリ自作するなんてことになるんでしょうか?

 

というわけで次回はGameplayCueです。やったー!最後だ!

 

ご指摘、ご質問、ご意見などあればコメントにお願いします。

 

続きの記事はこちら

wvigler.hatenablog.com