UE4の勉強記録

UE4の勉強の記録です。個人用です。

12章9節 GameplayAbilities API – アクターのゲームプレイ能力(actor’s gameplay abilities)をゲームコントロール(game controls)からトリガー(trigger)する。Part1

<前文>

f:id:kazuhironagai77:20181230183127p:plain

2週間ほど家庭の事情でUE4C++の勉強が出来ませんでした。2週間経つと結構勉強したくなくなりますね。もう一度やり始めるのは大変です。UE4C++に対する興味もかなり弱くなります。今回はリハビリのつもりでゆっくりやって行きます。

今回からGameplayAbilities APIの使用方法についてです。

今まで、必ずUE4APIを調べて来たのですが、GameplayAbilitiesのAPIが見つからないです。唯一見つかったのがここです。

f:id:kazuhironagai77:20181230183213p:plain

でも、これってGameplay Ability Systemの事でGameplayAbilitiesと違うのかもしれません。しかもこのサイト次にプラグインの方法を説明しています。良く分からないです。

そう言えば、アクションRPGのゲームの作り方を勉強したくてネットを見ていた時、Gameplay Ability Systemの話が出てきたような気がします。アクションRPGのゲームの作り方は難しくて途中で読むのを辞めてしまいました。特にこのGameplay Ability Systemの部分が難しかった記憶があります。今週はあんまりやる気もありません。今回のGameplayAbilities APIがGameplay Ability Systemの話でない事を祈るばかりです。

<本文>

<目的>

GameplayAbilities API を使用してアクターのゲームプレイ能力(actor’s gameplay abilities)をゲームコントロール(game controls)からトリガー(trigger)します。もうこの時点で何を言っているのかあんまり分かりません。取り敢えず出来るところまでやって行きましょう。

Step.0

今回は新しいプロジェクトを作成します。プロジェクト名はChapter12Part8とします。

今回のレシピは何をするのか今一良く分かっていないので、教科書をまずよく読んでみます。私が理解した限りではGameplayAbilities APIを使用する事でゲームプレイ中のボタンを押すなどのイベントでアクターなどのアビリティ―をC++の関数から呼び出す事が出来ると言っているみたいです。ウーン。このアビリティ―が何を指しているのか今一分かりませんが、プレイヤーが操作するアクターがプレイヤーが押すボタンによって前後左右の行動や攻撃や防御をする事をアビリティ―と呼ぶのならこれは当たり前の事で、増々???と成ってしまいます。

思い出して来ました。前にアクションRPGのゲームの作り方を勉強した時にGameplay Ability Systemが出て来て、これはGame Programming Patternsに出てくるType Objectの事なのではないかと思ったのでした。

f:id:kazuhironagai77:20181230183415p:plain

このtype Objectがどんなゲームデザインなのかもう忘れてしまったのですが、ゲームを作成するのに絶対必要なソフトウェアデザインだったはずです。それでGameplay Ability Systemを一生懸命勉強したのですが難しすぎてギブアップしました。

今、サラッとGame Programming PatternsのType Objectを読んでみたのですが、今回のレシピで述べているアクターのアビリティ―が関係あるのか良く分かりませんね。こんな複雑な内容は相当気合いが入っている時にしか勉強出来ないですから。特に今週は無理ですね。

取りあえず、アクターのアビリティ―は、アクターが前後左右の行動や攻撃や防御をプレイヤーの押したボタンなどによって行動する事を指していると仮定して次に行きます。

<準備>

本来ならば、教科書のGetting readyはサラッと読んで終わりにしてしまいますが、今回はGameplayAbilities APIが何なのか良く分からないので良く読んで以下にまとめました。

・ゲームのキャラのアビリティをEnumで表すらしいです。

・どのキーを押すとそのキャラクターが何をするのかを予め決定しておかないといけないそうです。

ウーン。あるキャラクターは攻撃ボタンを押したら火を噴きます。違うキャラクターは水を噴きます。これをGameplayAbilities APIを使ってプログラムするのでしょうか?まだ良く分かりませんね。

f:id:kazuhironagai77:20181230183528p:plain

まず、当然ですがUGameplayAbilityクラスからの派生クラスを作成するようです。上記の説明によるとこの派生クラスは一つのアビリティ―に対して一つ必要だそうです。

f:id:kazuhironagai77:20181230183548p:plain

f:id:kazuhironagai77:20181230183601p:plain

UGameplayAbilityクラスからの派生クラスを作成するについてオーバーライドについて説明しています。

InputPressedメンバー関数:       アビリティを発動するためのボタンに対応?

CheckCostメンバー関数:           魔法を使用するために十分なMPがあるかのチェック?

ApplyCostメンバー関数:            MPから魔法を使用するのに必要なMPを引く?

ApplyCooldownメンバー関数:   魔法を使ってから次の魔法が使えるまでの魔法が使えない時間の指定?

とか想像出来ますが実際はどうでしょうか?

f:id:kazuhironagai77:20181230183725p:plain

GameplayAbilitiesSetクラスからもオブジェクトを作成するようです。その目的については良く分かりませんが上記の説明についてはGameplayAbilityの派生クラスに対応するブループリントとアビリティ―を列挙したEnumを保持しDefaultInput.iniに指定した方法で動かす事が出来るそうです。

ここまでの説明で分かったことをまとめます。

・アビリティ―は列挙型(enum)で示します。

・UGameplayAbilityクラスからの派生クラスを作成します。この派生クラスで具体的なアビリティ―の内容を決定します。

・GameplayAbilitiesSetクラスからもオブジェクトを作成します。アビリティ―を列挙したenumとUGameplayAbilityクラスからの派生クラスを管理します。

このまとめがどこまで正しいか分かりませんが、全く情報がない場合よりマシですので正しいと仮定して次に行きます。

Step.1

f:id:kazuhironagai77:20181230183835p:plain

これ見るたびに必ず思い出すのですがこの教科書で最初にUE4APIを使用するレシピで、UE4APIを使用する場合は必ずBuild.csにそのAPI名を追加しなければならない事が書いてなかった事です。そのレシピがエラーになる原因を解明するのは本当に大変でした。

GameplayAbilities APIAPI名はGameplayAbilitiesだと思いますが念のためにサンプルコードの方も見てみましょう。

f:id:kazuhironagai77:20181230183924p:plain

GameplayAbilitiesとなっていますね。以下に示すようにBuild.csにGameplayAbilitiesを追加しました。

f:id:kazuhironagai77:20181230183948p:plain

ここで思い出したのですが、最初に述べたアクションRPGのゲームの作り方で出て来たGameplay Ability Systemのサイトで、

f:id:kazuhironagai77:20181230184007p:plain

となっていました。今回のレシピで習うGameplayAbilities APIの使い方とアクションRPGのゲームの作り方で出て来たGameplay Ability Systemは同じ事でした。

はぁー。

これは結構大変なレシピになりそうですね。今週は出来るところまでやりましょう。

Gameplay Ability Systemと同じとなるとGameplayAbilityだけでなく、GameplayTagsとGameplayTasksも追加しないといけないみたいですね。更にplugInも追加しないといけないみたいですね。

まず、以下に示すようにGameplayTagsとGameplayTasksをBuild.csに追加しました。

f:id:kazuhironagai77:20181230184049p:plain

Pluginを追加しました。

f:id:kazuhironagai77:20181230184106p:plain

Step.2

f:id:kazuhironagai77:20181230184130p:plain

サンプルコードを参考にしてUGameplayAbility_Attackクラスを作成します。

まず、以下に示すようにC++ウィザードからGameplayAblityクラスを選択します。

f:id:kazuhironagai77:20181230184150p:plain

GameplayAbility_Attackと名付けます。サンプルコードがPublicになっていたので、Publicを選択しました。

f:id:kazuhironagai77:20181230184209p:plain

以下に示すように出来ましたが、GameplayAbilityがエラーになっています。

f:id:kazuhironagai77:20181230184228p:plain

以下に示すように出来ましたが、GameplayAbilityがエラーになっています。

f:id:kazuhironagai77:20181230184246p:plain

以下に示すようにビルド出来ているのでそのままで行きます。

f:id:kazuhironagai77:20181230184305p:plain

Step.3

f:id:kazuhironagai77:20181230184331p:plain

サンプルコードを参考にしてオーバーライドしていきます。

まず、コンストラクターを作成します。

f:id:kazuhironagai77:20181230184350p:plain

サンプルコードのGameplayAbility_Attackクラスは全てのコードをhファイルに書いていますが、ここではヘッダーとソースファイルに分けて書いて行きます。

f:id:kazuhironagai77:20181230184413p:plain

こんな感じに書いて、ビルドしたら

f:id:kazuhironagai77:20181230184428p:plain

エラーになってしまいました。

ウーン。取りあえず、VSをリフレッシュします。

f:id:kazuhironagai77:20181230184451p:plain

まだエラーが出ているので、全部ヘッダーに戻しました。

f:id:kazuhironagai77:20181230184508p:plain

まだエラーは出ていますが、ビルドしてみます。

f:id:kazuhironagai77:20181230184535p:plain

ビルド出来ましたので次に行きます。

CanActivateAbility

まずこの関数から行きます。以下にサンプルコードの例を示します。

f:id:kazuhironagai77:20181230184605p:plain

このメンバー関数、教科書のGetting readyでも説明されていませんし、ちょっとネットでこの関数を調べて見ましょう。

見つかりませんでした。

このAPIプラグインのせいか情報が全然ネットにないですね。しょうがないのでそのままコピーします。

以下に示すようにヘッダーとソースファイルに分けてコピーしました。

f:id:kazuhironagai77:20181230184624p:plain

f:id:kazuhironagai77:20181230184633p:plain

ビルトします。

f:id:kazuhironagai77:20181230184650p:plain

成功しました。

サンプルコードはUFunctionを使用していませんのでUFunction()はつけないでおきます。

どっかにこのメンバー関数の情報がもう少しないかなと思って元のコードをGameplayAbility.hから探してきました。

f:id:kazuhironagai77:20181230184709p:plain

もし、このアビリティが今、始動出来るならTrueを返します。副作用はありません。

コメントはサンプルコードのコメントと同じでしたね。

サンプルコードの方はいつでもTrueを返す仕様になっています。ApplyCooldownメンバー関数との兼ね合いはどうなるのでしょうか?まだまだ全然分からないですね。

次にCanActivateAbilityメンバー関数のパラメーターを見てみます。

本来ならばUE4APIからそれぞれのパラメーターに使用されているクラスのremarkを読んだりするのですが、今回はそのAPIが見つからないので、直接コードを見ていきます。

<FGameplayAbilitySpecHandle

f:id:kazuhironagai77:20181230184752p:plain

そのポイントを特定の許可されたアビリティにハンドルします。

これらはグローバルかつユニークです。

まず、クラスではなく構造体(struct)でした。変数は

f:id:kazuhironagai77:20181230184821p:plain

だけでした。これは単に整数を保持しているだけの構造体(struct)のようです。

次のパラメーターも調べて見ます。

FGameplayAbilityActorInfo

f:id:kazuhironagai77:20181230184852p:plain

アビリティの一つを使用しているアクターのデータをキャッシュします。

InitFromActor内のAActor*から初期化します。

・アビリティはどのアクターに付いているのかを知るために使用します。:例)代わりに特定のアクタークラスにカップルされます。

・これらは一般的にポリモーフィズムをサポートするためにポインターとしてパスされます。

・プロジェクトは作成されたデファルトの構造体(struct)のタイプをオーバーライドするためにUAbilitySystemBlobals::AllocAbilityActorInfoをオーバーライド出来ます。

正直何の事が分かりませんがこのアビリティを使用しているアクターの情報をキャッシュする構造体(struct)のようです。

f:id:kazuhironagai77:20181230184927p:plain

Actorを保持する変数が確かにありますね。

次とその次のパラメーターはNullptrなので最後のoutのパラメーターを調べて見ましょう。

FGameplayTagContainer

f:id:kazuhironagai77:20181230184944p:plain

タグのコンテナはFGameplayTagsの集まりを保持します。FGameplayTagsに追加する事で明示的にタグは保持されます。チャイルドのタグを追加する事から暗示的にタグは保持されます。

タグが何のタグなのかが良く分かりませんが、このタグの中にいろいろな情報を詰めて必要に応じて取り出すのでしょう。

CheckCost

以下にサンプルコードのこの関数を示します。

f:id:kazuhironagai77:20181230185016p:plain

以下に示すように書き直しました。ヘッダーファイルのCheckCost関数の宣言部に実装部が見つかりませんと警告が出ていますが、無視しておきます。

f:id:kazuhironagai77:20181230185032p:plain

f:id:kazuhironagai77:20181230185041p:plain

ビルドしましたら、普通に出来ました。

f:id:kazuhironagai77:20181230185058p:plain

CheckCost関数もネット内に全く解説がないので、実際のGameplayAbility.hのコードを見てみました。

f:id:kazuhironagai77:20181230185118p:plain

コストをチェックします。もしアビリティのコストを払えるならTrueを返します。

ここのコメントもサンプルコードと同じでした。取りあえずコストをチェックしてTrueかFalseを返すメンバー関数と言う事は分かりました。

このメンバー関数もサンプルコードはTrueのみを返すように実装されていて、この関数の実際の使用方法は良く分かりません。

それではこの関数のパラメーターを調べて見ましょう。

FGameplayAbilitySpecHandle

CanActivateAbilityメンバー関数で使用したパラメーターです。

FGameplayAbilityActorInfo

これもCanActivateAbilityメンバー関数で使用したパラメーターです。

FGameplayTagContainer

これもCanActivateAbilityメンバー関数で使用したパラメーターです。

このメンバー関数はCanActivateAbilityメンバー関数と同じパラメーターをパスしていました。もしかしたらこれらのオーバーライドしたメンバー関数はどれも同じパラメーターをパスするのかもしれません。

ActivateAbility

サンプルコードの例を以下に示します。

f:id:kazuhironagai77:20181230185202p:plain

今度の例はコメントが書かれていませんでした。

元のコードを見てみます。

f:id:kazuhironagai77:20181230185222p:plain

実際にアビリティを始動します。これを直接呼ばないでください。

ここに、実際のアビリティの動作を実装するのでしょうか?これだけでは分からないですね。取りあえずパラメーターは調べておきましょう。

FGameplayAbilitySpecHandleとFGameplayAbilityActorInfoは他のメンバー関数と同じですね。FGameplayAbilityActivationInfoから調べます。

FGameplayAbilityActivationInfo

f:id:kazuhironagai77:20181230185253p:plain

データが特定のアビリティの始動をトライします。

・我々が権限(authority)であるかどうか告げます。もし我々が予測している(predicting)なら、確認します。

・現在と前の予想鍵(prediction key)を保持します。

・一般的にプロジェクト内のサブクラス化はありえないです。

・値(value)でパスします。なぜならこの構造体(struct)は小さいからです。

となっていました。予想鍵(prediction key)とは何でしょうか?分からない事が更に増えた感じです。この構造体(struct)だけ値でパスしている理由だけは良く分かりました。

パラメーターを見ていると

f:id:kazuhironagai77:20181230185435p:plain

予想鍵(prediction key)そのものがありました。 予想鍵(prediction key)があり、それをこのパラメーターが管理している事は分かりましたので次のパラメーターを調べます。

FGameplayEventData

まずこの構造体(struct)のコメントを見てみます。

f:id:kazuhironagai77:20181230185515p:plain

タグを基にしたゲームプレイのイベントのためのメタデータ。それは他のアビリティを始動したり、アビリティ特有のロジックを実行したり出来る。

とありました。何らかのイベント発生に使用するみたいですね。

<中断>

今週はリハビリを兼ねての勉強なのでここまで中断します。あんまり進展はありませんでしたが、このレシピは結構難しい内容みたいで、今のちょっと頭があんまり働いていない状態でがんばってもいい成果は出ないでしょうし。来週はもうちょっと情報を 集めてから再トライしようと思います。

<今週の成果>

分かったことと言うか今週の成果をここにまとめます。

・GameplayAbilities のAPIはネットからは見つからないが、GameplayAbilities のクラスなどを定義した実際のコード内にきちんとしたRemarkが書かれておりそれを読めばAPIと同じレベルの情報は得る事が出来るようです。

アクションRPGの解説で説明されているGameplay Ability Systemと今回のレシピで勉強している“GameplayAbilities API …”は同じ事を述べています。

・以下の推測が正しいと仮定して勉強を継続する予定です。

アビリティ―は列挙型(enum)で示します。

・UGameplayAbilityクラスからの派生クラスを作成します。この派生クラスで具体的なアビリティ―の内容を決定します。

・GameplayAbilitiesSetクラスからもオブジェクトを作成します。アビリティ―を列挙したenumとUGameplayAbilityクラスからの派生クラスを管理します。

・Game Programming Patternsに出てくるType Objectとの関係については良く分からないです。この関係についての考察は一端保留して先にこのAPIの使用方法を理解します。

GameplayAbilitiesの関数や構造体(struct)のコメントを読んでいる途中で気が付いたのですが、このAPIマルチプレイで沢山のクライアントがサーバーに接続した状態でのクライアント同士の戦闘をスムーズに行うために開発されたものかもしれません。そうだったら今の私には手に余る内容なので、ほどほどに勉強する事にします。