<前文>
前回のGameplayAbilities and You (Unreal Wiki)の実装が動かなかったのを直すのはおまけでやります。
<本文>
<目的>
今回は、GameplayEffectによるバフ(buff)の実装を行います。
あれ。前回のレシピの勉強の最後で今回のレシピと繋がって完成するはず。と結論づけたのに何か繋がっていないですね。繋がっているのでしょうか?兎に角、やって行きます。
<方法とそれに対する考察>
Step.0
Chapter12Part10を使用します。前回作成したやつです。前回は考察と方法を分けて書こうとしてコードを写している時に思った事を忘れてしまい考察が薄くなってしまいました。今回は疑問に思った事はその場でどんどん調べていくようにします。また今回も以下のサイトを参考にして勉強します。
*******************************************************************************************
公式サイトからの情報です。
・Gameplay Abilities in Action RPG
・GameplayAbilities and You (Unreal Wiki)
・Action RPG | Project Spotlight | Unreal Engine Livestream
(日本語)
・猫でも分かる UE4の新しいサンプル「Action RPG」について(p77~)
公式サイト以外でGameplay Ability Systemについて述べたサイト。
Tom Looman氏による解説
・Tutorial: Creating a RAGE Potion in Epic’s Action RPG
・Gameplay Ability System Tutorial with C++ & Blueprint [Unreal Engine 4 RPG Game]
SabreDartStudiosによる解説
・Intro to Gameplay Abilities in Unreal Engine 4
Unreal Sydneyによる解説
・Intro to the Unreal Engine Gameplay Abilities Module - Unreal Sydney Meetup
(日本語)
おかわりはくまい氏による解説
・GameplayAbilitiesの使い方(セットアップ編)
*******************************************************************************************
まず、GameplayEffectについてです。Unreal SydneyのGameplay Abilities System の8大コンセプト(AbilitySystemComponent, AttributeSets, GameplayTags, GameplayEffects, GameplayCues, GameplayAbilities, GameplayTasks, GameplayEvents)の一つがGameplayEffectsです。ので、きっちりとGameplayEffectについて復習します。
何も見ないで、GameplayEffectについて私が覚えている事は、GameplayEffectの目的はAttributeSetで設定したキャラクターの定量的な特徴(例えばMP)の値をコントロールすると言う事です。まずここから確認していきます。
Unreal Sydneyによる解説では、以下のような説明がされていました。
うぁ。GameEffectが担当する機能は沢山ありますね。ただCan modify attributesとあるので「AttributeSetで設定したキャラクターの定量的な特徴(例えばMP)の値をコントロールする。」と言うのは間違いではないでしょう。
教科書にもっと端的に紹介されていました。
GameplayEffectはアクターのAbilitySystemComponentに付随しているUAttributeSet内の個々のゲームプレイの属性に作用します。
はい。私が考えていたのと全く同じ内容です。それではやって行きます。
Step.1
これは、生のC++のコードから作れと言う事なのでしょうか?サンプルコードの方も見てみましょう。
UGameplayEffectから派生したファイルなないみたいですね。うーん。どれかのクラス内に作成するのでしょか?
Warriorクラス、GameUnitAttributeSetクラスを見てみましたがCONSTRUCT_CLASSを使用している箇所はありませんでした。
分からん。まず、CONSTRUCT_CLASS が何なのか分かりません。Googleします。
出て来ません。
因みに、GameplayEffectはUE4C++のクラスではないの?と思いC++ウィザードで探して見たら、普通にありました。
こっちから作成したのでは駄目なんでしょうか?
分からないので、教科書を読み直していたら、
の部分を読み飛ばしていました。こっちから調べて見ます。
まず、VeryVerboseですがこのサイトに説明されていました。
VeryVerboseレベルでは、ログはログファイルにプリントされます。しかしインゲームのコンソールにはプリントされません。これは非常に細かいログの記録(logging)のために使用されます。さもないと出力のスパムになります。
logの報告の細かさを最大にセットする事をVeryVerboseと言うのでしょうね。ただそのセットの方法ですがoutputLogにあるCmdに単にタイプすればいいんでしょうか?
コマンドの`なのですがこれってゲーム中にコマンドを開くための日本語キーボートの場合の@の事ではないのでしょうか?調べて見ましょう。
分からなかったです。
そのままタイプしました。
エラーは出ませんが、何も起きません。コマンドを探してもそんなコマンドは見つかりません。
Log LogAbilitySystemAllをタイプするのかもしれません。
うーん。分からない。
Logのみタイプしてみました。
今度は何か説明か現れました。読んでみると、
そのカテゴリー(cat)のverbosityのレベルをセットする。
というコマンドがありました。これをそのまま使用すればいいんじゃない。となると、Log LogAbilitySystemAllだと[level]が足りないです。Log LogAbilitySystem Allなら正しいんでしょうか。
更によく読んでみると、[cat]と[level]についての解説がありました。
[cat] 全てのカテゴリーのための”global”もしくは、操作するためのコマンドのためのカテゴリー
[level] Verbosityのレベル、以下のうちのひとつ:none, error, warning, display, log, verbose, all, default
とありました。[level]の中にallがしっかりあります。後は[cat]の中にLogAbilitySystemがあればいいはずです。
Log listと打つと全てのlogのカテゴリーを表示するらしいので、LogAbilitySystemがあるか確認してみます。
ありました。ので、Log LogAbilitySystem Allと打ちます。
今度は出来ました。
CONSTRUCT_CLASSに戻ります。12章の全てのサンプルコードを検索したのですが、CONSTRUCT_CLASSを使用しているコードは見つかりませんでした。
だたし、Warrior.h ファイルに
かなり今回のレシピに関係ありそうな関数が見つかりました。これらの関数の実装部を読もうと、Warrior.cppファイルを見ていたら、以下の関数がありました。
うーん。何だか見えて来ました。
教科書の言うCONSTRUCT_CLASSマクロはUE4C++のどこかに定義されている変数の初期化が出来るマクロなはずです。
探したら確かにありました。
サンプルコードでは「マクロを使用して実行時間を短くする方法はCのやり方でC++ではinclineを使うべき。」みたいなC++十字軍のような考えの人たちがCONSTRUCT_CLASS滅すべしみたいな感じでinclineを使ってあえて書き換えたのでしょう。うーん。ここは教科書的には、CONSTRUCT_CLASSマクロそのものを教えると言う目的もあったはずです。サンプルコードだけ勝手に変えていいんでしょうか?
サンプルコードと同じようにしました。理由はそっちの方が簡単そうだからです。更にサンプルコードではこの関数はBeginPlay関数の後に入れられているので、それも同じにしました。
前回と違いコードの解説もここでやって行きます。
NewObject関数はCreateDefaultSubobject関数と並ぶ、UE4C++の2大基礎関数ですので知っていて当然ですが、一応ここでも調べておきます。
うーん。あんまりいいサイトが見つかりませんでした。この教科書の2章の6節の説明が一番分かり易いです。以上です。
次に調べる必要があるのがGetTransientPackage()関数です。
UE4C++のドキュメントに説明がありました。
寄せ集めの(Misc)
スタティックな過度(transient)のパッケージを返す。
と説明されていました。正直この関数何を返すのか良く分かりません。
こっちの説明のほうが分かり易いですね。
そのouterはそのオブジェクトが発生する所のコンテナでしょう。つまりそれは、貴方が探している親クラスかもしれないし違うかもしれません。
GetTransientPackage()は、オブジェクトが発生した現在の場所もしくは環境を返します。
つまり、オブジェクトを作成した環境を返すと言う事ですね。
因みにこの教科書の2章の6節ではNewObject関数は常にGetTransientPackage()関数を呼ばなければならないから、ConstructObject関数のほうが使いやすいかも。と述べています。最初にこの教科書を読んだときは、ConstructObject関数なんて聞いた事ない関数を使っててこの教科書は大丈夫なのか?と思っていましたが、今読み直してみると教科書のこの部分の内容は深いです。
Step.2
サンプルコードから見てみます。
WarriorクラスのTestGameplayEffect()関数の実装部内で使用されていました。
AddModifier()関数そのものはWarrior.cppファイル内で定義されていました。
AddModifier()関数のパラメーターについて調べていきます。
AddModifier()関数の最初のパラメーター、UGameplayEffectはそのままなので次を調べます。
2番目のパラメーター、UPropertyクラスはUE4C++のAPIによると、
UnrealScriptの変数
と説明されています。UE4C++で変数の宣言をする時に必ずUPROPERTYと書きますがこれが着いたら全部UPropertyクラスなのでしょうか?
サンプルコードでは、UPropertyのオブジェクトであるhpPropertyは、以下に示したように定義されています。
コンパイル時間(compile-time)はUGameUnitAttributeSetクラス(UGameUnitAttributeSet.hのリスト) からHpUPROPERTY()の取得をチェックします。
うん。これだけみると、UPropertyクラスはそういうクラスがあると考えた方が無難みたいですね。Remarkの内容を考えるとこのUPropertyクラスとUE4C++における変数の宣言に使用するUPROPERTYは関係がありそうですが、これに関してはここでは掘り下げません。
FindFieldChecked関数も一応調べて見ます。UE4C++のAPIによると、
特定されたスコープ内で名付けられた領域(filed)を探します。
如何なるアウタークラスも含みます。
Failureを断言します。
とありました。パラメーターには、
Scope:探している領域が入っているスコープ
FiledName:探している領域の名前
と定義されていました。
となると、UGameUnitAttributeSet::StaticClass()が探している領域が入っているスコープ、GET_MEMBER_NAME_CHECKED(UGameUnitAttributeSet, Hp)が探している領域の名前になります。
GET_MEMBER_NAME_CHECKEDはマクロなので、定義を探してみると、
そのメンバーがクラスの名前に存在しているか静的(static)に確認してFName(TEXT(“MemberName”))を返します。
となっていました。UGameUnitAttributeSetクラスの変数、Hpを返すはずです。(以下にUGameUnitAttributeSet.hの一部を示します。)
AddModifier()関数の3番目のパラメーター、EGameplayModOp::Typeは、
Additive, Multiplicitive, Division, OverrideそしてMaxがあるようです。サンプルコードではAdditiveを選択していますので、値を足す事になるのでしょう。
AddModifier()関数の最後のパラメーター、FGameplayEffectModifierMagnitudeを調べます。
Gameplay Effect modifierの大きさを再現する構造体(struct)。潜在的に沢山の違う方法で計算される。
とあります。実際のアギュメントはFScalableFloat( 50.f )がパスされていますのでFScalableFloatも調べて見ます。
Value*Curve[Level]という型の一般的な数的な値
とありました。ただの数字をパスしていると考えていいのでしょうか。
ここまでで、一応AddModifier()関数について理解出来たので、サンプルコードのAddModifier()関数をコピーしていきます。
以下に示すようにまず、AddModifier()関数そのものをCunstructGameplayEffect()関数の下にコピーします。
ビルドしても成功しました。
サンプルコードではAddModifier()関数はWarriorクラスのTestGameplayEffect()メンバー関数内で使用されているので、まず、TestGameplayEffect()メンバー関数を作成します。
更に、AddModifier()関数にパラメーターとしてパスする2つのアギュメントをTestGameplayEffect()メンバー関数内で作成します。
更に、AddModifier()関数を加えます。
試しにビルドしてみると成功しました。
Step.3
サンプルコードを見ると、全く同じコードが、WarriorクラスのTestGameplayEffect()メンバー関数内のAddModifier()関数の後にあるので、それをそのままコピーします。
試しにビルドしてみると成功しました。
ので、各行を見ていこうと思います。
調べなくても関数名とコメントからその関数の機能はほとんど想像できます。DurationPolicyはHpを回復するのに時間がかかるのかどうか?DurationMagnitudeは時間がかかる場合どのくらいかかるのか。ChanceToApplyToTargetは成功する確率、Periodは次にHp回復が出来るまでの時間、つまりクールダウンの時間でしょう。
EGameplayEffectDurationTypeのタイプには、以下のものがありました。
これだけ理解出来れば、コードを読む分には十分と思われるので次に行きます。
Step.4
サンプルコードのままにコピーしました。
一応確認のためにビルドすると成功しました。
使用している関数、ApplyGameplayEffectToTargetの機能は書いてある通り、効果をUAttributesetに影響される事でしょう。一応これで完成です。
<結果>
この教科書のGameplayAbilities APIのレシピにはどれもテストの方法が書かれていないので、GameplayAbilities and You (Unreal Wiki)の実装を基にして実際に動いている事を確認します。ただ先週やったGameplayAbilities and You (Unreal Wiki)の実装はどこかが間違っていてまだ動かないので、それをまず直します。
<考察>
すでにやってしまったのでなしです。
<まとめ>
これも来週か再来週に行います。
<おまけ>
前回のGameplayAbilities and You (Unreal Wiki)の実装が動かなかったのを直していきます。
まず、以下に示すif-stateがfailureを返している事が確認できました。
HasAuthority()とAbilityのどちらがfailureを返しているのか調べて見ました。
実行すると以下のLogが表示されたのでAbilityが初期化されていないようです。
ここで、Abilityの宣言をおこなっているのですが、初期化を行っているコードがないです。
UE4のエディター上で選択しないといけないみたいです。
Use_Spell_1を選択しました。
今度は出来ました。
来週はこのコードを利用して、教科書のGameplayAbilities APIの3つのレシピが本当に動くのか確認します。