<前文>
で勉強しています。今回も前回の続きです。
前々回は、GameplayAbilities APIについて全く分からないまま取りあえずレシピを途中まで勉強しました。これはGameplayAbilities APIについての情報が全くネットから手に入らなかったからです。その時勉強して分かった事の一つが「Gameplay Ability Systemと今回のレシピで勉強している“GameplayAbilities API …”は同じ事。」です。
そこで、前回はGameplay Ability Systemについてネットで調べてまとめました。Gameplay Ability Systemの情報はそこそこですがネット上にありましたので、GameplayAbilities APIの全体像と目的が大分見えて来ました。
以下に前回まとめた「GameplayAbilities APIの目的と全体像についての推測」を示します。
1. GameplayAbilities APIの目的はアクションRPGやマルチプレイ式のゲームにおいてプレイヤーとその敵とのお互いの干渉(例えば攻撃とそのダメージ)とそれぞれのキャラクターの持つパラメーターを管理するためのAPIであると考えられます。
2. このAPIを使用する最重要な点はマルチプレイ式のゲームにおいて複製と予測を行う事で、サーバーの許可がない状態でもクライアントの扱うキャラクターは攻撃出来き、もし後でその攻撃が無効だった場合、その攻撃がなかった事かつそれ以外はあった事にゲーム内の現実を変化する事が出来る事です。これによってクライアントはサーバーの返事を待たずに攻撃などの行動を行う事が出来き、ゲーム内のプレイヤーの反応が非常にスムーズになります。
3. GameplayAbilities APIは、主にAbilitySystemComponent, AttributeSets, GameplayTags, GameplayEffects, GameplayCues, GameplayAbilities, GameplayTasksとGameplayEventsで構成されるとみられます。
今回は、前回出来なかったGameplayAbilities and You (Unreal Wiki)と日本語のサイトの勉強をします。
<本文1>
<前回の「上記のサイトらを読んで」の続き>
前回、GameplayAbilities APIの目的と全体像に対する大体のイメージが掴めたのでそれ以上の情報やそれに反する内容に注目して読んでいきます。
GameplayAbilities and You (Unreal Wiki)
GameplayAbilities and Youを読んでいきます。このサイトは他のGameplayAbilities APIのサイトの作者からも素晴らしいサイトであると紹介されていたのであえて最後に残しておきました。このサイトの作者の名前がKAZとあったので前回述べたように日本人なのかなと思っていたのですが、このサイトにはKJZに書かれたとあり、KAZじゃないじゃんと思っていたらフォーラムの方でKAZと呼ばれるけどKZJと書いてねとありました。(それでもKJZとは違うのね。)
最初にGameplayAbilityについて解説がありました。
Dotaとかそれに類するゲーム内のアビリティのようなもの。
ファイヤーボールを投げてプレイヤーに当てたり(ダメージの量をセットする事で)爆発させたり、爆炎の半径内の全ての人を固定(時間によるダメージ)します。
一方で、ファイヤーボールを投げたプレイヤーはいくらかのマナを失いクールダウンに入ります。
やっぱりアビリティは炎を投げたり、水を浴びせたりする事も含まれるみたいですね。基本的には、Gameplay Abilities in Action RPGと同じ事を言っていますね。マルチプレイにおけるGameplayAbilities APIの役割についてはあえて省いているのでしょうか。
やっぱり、みんなこれが疑問になるのですね。
この後、具体的なチュートリアルに入るのですが、このチュートリアルが教科書とやっている事が違うので、先に教科書のレシピを終わらせてから読む事にします。
日本語のサイトも教科書のレシピに戻る前に読んでおきます。
猫でも分かる UE4の新しいサンプル「Action RPG」について(p77~)
ここで、処理を非同期に実行可能と述べているのは、「サーバーが許可する前にプレイヤーの魔法が取りあえず発動して…」の事を指していると思われます。
更にその後、処理の流れのイメージとして回復薬をゲットした例が載っているますが非常に分かり易い。今まで読んだ中で、一番単刀直入でズバズバと解説してます。
作者の名前が和也(敬称略)となっていたのですが、この方がGameplayAbilities and Youの作者なのでしょうか?
これを見るとやっぱしアビリティは炎を投げたり、水を浴びせたりするアニメーションも管理しているみたいですね。更にGameplayAbilities はBPでも管理可能と書かれていました。これがGameplayAbilities and YouではGameplay AbilityのC++のコードが無い理由なのかもしれません。
GameplayAbilitiesの使い方(セットアップ編)
最後になってしまいましたが、おかわりはくまい氏による解説を見てみます。このサイトは「猫でも分かる UE4の新しいサンプル「Action RPG」について(p77~)」でも紹介されてたので最後にしました。
サラッと読んだ限りでは、GameplayAbilities and Youと「猫でも分かる UE4の新しいサンプル「Action RPG」について(p77~)」の両方を兼ねて、更に実際にコードを実装した場合の注意点が書かれているようです。実際にコードを書いて実行する段階になったら何回もお世話になる気がします。
このブログの最後に書かれていたのが、以下の文章です。
やっぱりあえてマルチプレイ式のゲームにおけるGameplayAbilities APIについては抜かしているのですね。学習者が理解しやすいように削れるだけ削った基本のみを教えているのようです。
<取りあえずここまでのまとめ>
前回まとめた「GameplayAbilities APIの目的と全体像についての推測」に反する内容はなかったです。
更に、追加で以下に示す内容が分かりました。
<本文2>
今週はこれで終わりにするには流石に短すぎるので前々回のPart1の続きを勉強します。
<方法>
Step.3
前回は、ActivateAbilityメンバー関数まで調べたので、今回はInputPressedメンバー関数から調べていきます。
<InputPressed>
サンプルコードを以下に示します。
スタブとバインドするインプット
コメントがそれしか書かれていませんでしたので、UE4の元のコードを見てみます。
同じでした。もう少しこの関数の解説が見つからないか色々探したのですが見つかりませんでした。
次にこの関数のパラメーターについて調べて見ます。
<FGameplayAbilitySpecHandle>
前々回、CanActivateAbilityメンバー関数に使用されるパラメーターとして調査済み。
<FGameplayAbilityActorInfo>
このパラメーターもCanActivateAbilityメンバー関数に使用されるパラメーターとして調査済み。
<FGameplayAbilityActivationInfo>
前々回、ActivateAbilityメンバー関数に使用されるパラメーターとして調査済み。
前々回のプロジェクトChapter12part8にこの関数を追加してビルドしたら
エラーになってしまいました。この関数を抜いてもう一度ビルドしても同じエラーが出るようになってしまいました。
ウーン良く分からないですが、2週間前に何かしてしまったのかも。そういえばスクリーンショットの撮影をするために元のコードを何行にも分割した時に、このコードは読み専用だけと変えていいんですか?みたいなダイアログが表示されたような。
面倒なので、最初から作り直します。今度のプロジェクト名はChapter12Part9とします。
GameplayAbility_Attackを派生してビルドするとやっぱりエラーになってしまいます。しかたないのでversion4.21.1を再インストールしました。
今度は、ビルドしました。
前々回はここから、コンストラクターを追加したのですが、無くてもいいような気がしますので、CanActivateAbility、CheckCost、ActivateAbilityそしてInputPressedメンバー関数のみを追加します。
もうめんどくさいのでサンプルコード通りに全部ヘッダーファイルに追加してしまいました。
ビルドも出来ました。
Step.4
具体的なやり方が書いていないのですが、以下の方法で派生しました。
Step.5
選択しました。
名前が分からない。適当でいいはずですがDataフォルダーに入っているこれらのファイルの中で多分これを表しているWarriorGameplayAbilitySetとします。
GameplayAbilitySetを調べて見ます。
これはアビリティのセットを定義するために使用されるDataAssetの一例です。それはAbilitySystemComponentに与えるためのものでインプットコマンドにバインドします。
しかしながらそれが望んでもあなたのプロジェクトがこれを実装する事は自由です。
とありました。
と紹介されていたのでそれも調べてみると
確かに唯一のメンバー関数としてGiveAbilities()があります。
Step.6
名前が分からないので勝手にWarriorGameplayAbilitySetとしましたが合ってました。
Step.7
WarriorAbilitySetをダブルクリックしたら以下に示すイメージが現れました。
どこにもTArrayオブジェクトの+などないじゃないか。
確認のためにサンプルコードの方のWarriorAbilitySetを強引にChapter12Part9プロジェクトのコンテントフォルダー内に移して表示させてみました。
全然違う。
もう一度作り直して見ました。
今度はきちんと出来ました。ので説明にあるようにBP_GameplayAbility_Attackを選択しました。
Step.8
まず、Warriorクラスって何ですか?いつそんなの作ったの?と。こんな時はまずサンプルコードに当たります。
ありました。
取りあえず、Characterクラスの派生クラスとして作成していますね。このまま作成します。
こんな感じに出来ました。
インターフェイスがありませんがそのまま行ってみます。
UGameabilitySet…を追加します。
ビルドしてみます。
成功しました。
次の手順ですが、
どうやって選択するのか分かりません。
UPROPERTY(EditAnywhere, BlueprintReadWrite…とある事からエディター上かBP内で選択するのでしょうが具体的な方法は書かれていません。
ここは無視していきます。
Step.9
やっぱりインターフェイスからも派生していないといけないのですね。
と追加しました。ビルドをすると
エラーになってしまいました。
E0077をクリックしたら、C++ object of abstract class type "AWarrior" is not allowed: pure virtual function "IAbilitySystemInterface::GetAbilitySystemComponent" has no overriderとありました。
なので、GetAbilitySystemComponentメンバー関数を追加します。
サンプルコードのGetAbilitySystemComponentメンバー関数:
とそれを実装するのに必要なコードを追加しました。
ビルドします。
今度は成功しました。
Step.10
GameplayAbilitySetクラスのGiveAbilities()メンバー関数は使用すべきではないとテップで述べていますがここでは使用するのでしょうか?
先を読んでみます。
Step.11
これは何を言っているのか分かります。まず、サンプルコードのSetupPlayerInputComponent関数を見てみます。
同じコードをWarrior.h ファイルに追加します。と思ったら元からこのコードがありました。
サンプルコードのWarrior.cppファイルには、SetupPlayerInputComponentメンバー関数の実装が書かれていますが、今ここでそれをしないといけないのでしょうか?
残りはティプだけでした。
やります。
まず、サンプルコードから見てみます。
This, &AWarrior::Forward)はAWarrior::Forward関数を指しているはずです。しかしForward関数らはないので作る必要があるはずです。
サンプルコードをみると
ありました。まず、これから作っていきます。
以下のコードをWarrior.hファイルに追加しました。
次にWarrior.cppファイルに以下のコードを追加します。
まずlastInputを初期化します。
実装します。コードはサンプルコードと全く同じです。
サンプルコードではTick関数でlastInputをアップデートしていたので、そのコードをそのままコピーしました。
準備が出来たので、SetupPlayerInputComponent関数に追加します。
次にサンプルコードではAbilitySystemCompnenetをアクターのインプットコンポーネント(input component)に連結しています。のでそのままコピーしました。
Forループがありました。教科書には、
GameplayAbilitySetのAbilityグループ内でリスト化したそれぞれのGameplayAbilityを反復(iterate)します。
とありましたが、これをこのForループで行っているのでしょうか?見ていきましょう。
GameplayAbilitiySet内にあるそれぞれのBindInfoを調べます。
それぞれのAbilitySystemComponentを始動し試してみます。
Warriorクラスのメンバー変数でUGameplayAbilitySetクラスから作成したオブジェクト、GameplayAbilitySetはエディター上かBP内で、私がUGameplayAbilitySetクラスから作成したBPであるWarriorAbilitySetを選択するはずなので、Abilityはその中にあるBP_GameplayAbility_Attackのみが選ばれるはずです。
ちなみに、GameplayAbilitySetクラスのメンバー変数のAbilityは以下に示すようにFGameplayAbilityBindInfoクラスのTArrayです。
UとFからUGameplayAbility_AttackはFGameplayAbilityBindInfoの派生ではないと考えられるので、FGameplayAbilityBindInfoクラスのメンバー変数であるAbilityがUGameplayAbilityクラスからの派生クラスUGameplayAbility_Attackから作成したBP_GameplayAbility_Attackを保持出来る理由が分かりません。
FGameplayAbilityBindInfoがどんなクラスか元のコードを当たって見ました。
はい、良く分かりました。これならFGameplayAbilityBindInfo構造体は、UGameplayAbilityクラスからのオブジェクトを何個も保持できますね。
このまんまの構造体ですね。
次のコードを見ていきます。
BindInfoは2つのメンバーを持ちます。
GameplayAbilityClass (UClass of a UGameplayAbility)
そのままの説明がサンプルコードにありました。
このif文はGameplayAbilityクラスが本当に存在しているのかチェックしています。GameplayAbilityクラスの指定はエディター上で行われるのでこのチェックは絶対必要ですね。
FGameplayAbilitySpecについて調べます。
アビリティシステムコンポーネント(ability system component)にホストする、活動できるアビリティのスペック。
これはアビリティが何であるか(クラス、レベル、インプットバインドなど)と
アビリティの外側でインスタンス化/始動を保持しなければならないランタイムの状態の維持の両方を定義します。
アビリティシステムコンポーネント(ability system component)が何なのか不明ですね。
取りあえず、以下にFGameplayAbilitySpec関数のパラメーターから調べていきます。
まず、最初のパラメーターInAbilityClassはUGameplayAbilityクラスです。これは解説文にある「アビリティが何であるか(クラス、レベル、インプットバインドなど)の定義」におけるクラスの部分を担当していると考えられます。更に次のパラメーターであるInLevelがレベル、その次のパラメーターであるInInputIDがインプットバインドを担当しているのでしょう。そこだけは分かりました。インプットバインドにBindInfo.Commandをパスするのは何となく分かりますが、レベルが1とは実際は何を指しているのか全く分からないですね。
もうちょっとFGameplayAbilitySpecクラスのコードを調べて見ると、このレベルはFGameplayAbilitySpecクラスメンバー変数であるLevelを定義するのに使用しているようです。
これはひょっとするとキャラクターのレベルを表しているのかもしれませんね。キャラクターのレベルによって攻撃力は変化しますし。
以下に示したようにそのままコピーしました。
次の行に行きます。
後にアビリティの呼び出しをするためのアビリティのハンドルを保持する。
まず、FGameplayAbilitySpecHandleクラスを調べます。
特定の許可されたアビリティへのポイントを扱う。
全体で唯一である。
とありました。後クラスでなく構造体(struct)でした。次の
AbilitySystemComponentですがGetAbilitySystemComponentメンバー関数を追加した時に作成したのですが、そのクラスが何をするクラスなのか調べる事は忘れていました。今、調べます。
う!
何だかスゴイクラスですね。よく見たら、GameplayAbilities APIを構成すると思われる8大コンセプトのAbilitySystemComponent, AttributeSets, GameplayTags, GameplayEffects, GameplayCues, GameplayAbilities, GameplayTasksとGameplayEventsの内の一つですね。これはしっかり調べておいた方がいいみたいですね。
来週やります。
今週はここで終了します。
<中断>
取りあえずここまでビルド出来る事は確認しました。続きは来週やります。