<前文>
Fox Business Newsを見てたら、カルロス・ゴーンについてやっていたのですが内容が酷すぎます。カルロス・ゴーンの不当な勾留について日本人は外人を差別するからと人種差別丸出しのコメントを述べ、日本人も同じように勾留される件については全く触れません。後は日本人悪い、日本人を叩け!の大合唱でした。トランプ政権の優先順位(国境に壁建設、中国との貿易戦争、ロシアのスパイ問題など)からこの問題がFox Business Newsで最重要課題になる事はないと思いますが、現地の日本人や日系人がヘイトの標的として、差別、嫌がらせ、そして暴行を受けるかもしれないと思うと、血が沸騰するかのような怒りを感じます。日本にいると全く感じないこの怒りの感覚を久しぶりに思い出しました。
日本人は有色人種です。世界では差別される側なんです。差別される側は差別をする奴と戦わないといけないんです。戦わなければ人として当然与えられるべき権利も自由も得る事は出来ないんです。この件だってアメリカのある州か白人が大多数を占める国で同様の事件が起きた場合なら「その国(州)では全ての人が同様に扱われているが…」とコメントされその行為について問題視される事はあってもその国(州)の住民にヘイトが向けられる事はないはずです。
そこで思い出したのですが、この「差別される側は差別をする奴と戦わないといけない。」は昔の日本の漫画やアニメそしてRPGの隠れたテーマだったような気がします。ドラえもんが未来に帰る日にのび太がジャイアンと喧嘩する所なんか「差別される側は差別をする奴と戦わないといけない。」を非常に上手く表現していると思います。FF7でも差別をする側は巨大企業で主人公はそれと戦うセクトに雇われた傭兵であり「「差別される側は差別をする奴と戦わないといけない。」のか?」を最初からプレイヤーに問いかけています。
今の漫画やJRPG、そしてアニメは何故つまんなくなってしまったのか?の答えの一つはこれだと思うんです。兎に角、戦わない。特に主人公が。そして敵も曖昧です。敵が主人公に差別をする奴であるかどうか分かりにくいです。で大変つまらなく感じます。
話がそれ過ぎました。今週の勉強をやっていきましょう。
<本文>
Step.11(続き)
前回、AbilitySystemComponentを調べる途中で、AbilitySystemComponentがGameplayAbilities APIを構成すると思われる8大コンセプトの一つであると気が付きました。それでAbilitySystemComponentは本格的に調べる必要があると考え、今週やる事にしました。
AbilitySystemComponentについて
まず、UE4C++ののコメントを見てみます。
UAbilitySystemCompoment
コンポーネントは、AbilitySystemの3つの様相と簡単に干渉(interface)します。
GameplayAbilities:
・(例えばプレイヤーやAIによって)使用されるアビリティを与える/任命する方法を提供します。
・インスタンス化したアビリティ(それら付加されなければならない何か)の管理を提供します。
・複製機能の提供
・アビリティの状態はUGameplayAbilityそのものに対して常に複製されなければならないです。しかしUAbilitySysemComponentは実際のアビリティの始動のためにRPCの複製を提供します。
GameplayEffects:
・実行中のGameplayEffectsを保持するためにFActiveGameplayEffectsContainerを提供します。
・ターゲットかそれ自身にGameplayEffectsを適応するための手段を提供します。
・FActiveGameplayEffectsConatainers(持続期間(duration)、大きさ(magnitude)など)内の情報をクエリ(querying)するためにラッパーを提供します。
・GameplayEffectsをクリア(clearing)/取り除く(remove)するために手段を提供します。
GameplayAttributes:
・Attribute setsを初期化したり、配分(allocate)したりするための手段を提供します。
・AttributeSetsを得るための手段を提供します。
UE4のAbilitySystemComponent.hには以上のコメントが書かれていました。このコメントによれば、AbilitySystemComponentの役割は、3つのクラス、GameplayAbilities、GameplayEffects、GameplayAttributesと干渉する事です。今回のレシピでは、AbilitySystemComponentはGameplayAbilitiesと干渉しますが、他の2つGameplayEffects、GameplayAttributesとも干渉出来るみたいです。
前に調べた他のサイトでもAbilitySystemComponentについては沢山のコメントが述べられていました。
Gameplay Abilities in Action RPG
ここでも、3つの要素であるGameplayAbility、GameplayEffects、そしてGameplayAttributesを実際に使用するためにAbilitySystemComponentが必要であると述べられています。
大切そうな所だけを抜き出して行きます。
Ability System Componentは貴方のゲームのキャラクターがGame Ability Systemを通してアクセスする主なインターフェイス(interface)です。
このコンポーネントはGameplay Attributeを管理し、Gameplay Eventsを実行し、Gameplay Abilitiesを保持します。
そしてのプレイヤーインプットからGameplay Ability の始動、確認、そしてコマンドのキャンセルへのバインドでさえ扱います。
UAbilitySystemComponentはクラスでインターフェイスではないですが、最初の一行は何をいっているのでしょうか?
その他の部分は他のサイトと全く同じ事を述べていますね。特に最後の行で述べている「そしてのプレイヤーインプットからGameplay Ability の始動、確認、そしてコマンドのキャンセルへのバインドでさえ扱います。」はまさに今レシピでやろうとしている事ですね。
Attribute Setを作成したらそれをAbility System Componentに登録しなければなりません。
それをするためにはAttribute SetをAbility System Componentを保持するアクターのサブオブジェクトとして追加するか、
それをAbility System ComponentのGetOrCreateAttributeSubobject関数にパスするかします。
今回のレシピではまだAttribute Setは作成していないので、スキップします。
Ability System Componentと干渉するGameplay Effectsはデータのみのブループリント(基底クラスであるUGameplayEffect)として実行されます。
もし適切であれば、それが活動している間そこで保持されます。
Gameplaye Effectもまだ作成していないので、スキップします。
Game Ability SystemにおけるAbility System Componentについての一般的な説明は、上記の二つと同じですね。しかし今回のレシピで使用するGameplayAbilityとAbility System Componetの干渉についての具体的な説明は全くされていなかったです。
GameplayAbilities and You (Unreal Wiki)
まず、Ability System Componentの初期化の実装部が紹介されていました。
そういえば、WarriorクラスのAbilitySystemComponent変数の初期化を実装していなかったです。
サンプルコードのAbility System Componentの初期化を以下に示します。
PCIPは、FObjectInitializerクラスのオブジェクトです。CreateDefaultSubobject関数を使用するにあたって無くても良くなったみたいな記事を何処かで読んだような気がしたのですがその記事が見つかりません。
実際、PCIPを省いて初期化したらエラーになってしまいました。
と書きました。
この後、Ability System Componentのバインドについての解説が続きますが、Ability System Componentそのものについての解説ではないので、省略します。
Intro to the Unreal Engine Gameplay Abilities Module - Unreal Sydney Meetup
と書かれていました。Gameplay Ability Systemやその他のサイトで、「Ability System Componentは貴方のゲームのキャラクターがGame Ability Systemを通してアクセスする主なインターフェイス(interface)です。」のような文にたびたび出会い、何で、UAbilitySystemComponentはクラスでインターフェイスではないのにInterfaceと言うのか不思議だったのですが、これで意味が分かりました。IAbilitySystemInterfaceを通して全てのアクターはAbility System Componentを使用出来るそうです。
ちなみに前回の勉強で、IAbilitySystemInterfaceを追加したstepでは以下のような解説がされていました。
AbilitySystemInterface.hには、以下の説明がありました。
Ability System Componentにアクセスするアクターのためのインターフェイス
次に初期化について述べています。
これは、教科書のRecipeとほぼ同じで、GameplayAbilities and You (Unreal Wiki)の初期化の方法とは完全に同じですね。
その次に以下の説明がありました。
うーん。この部分からの説明についてはまだ分かりませんね。
<AbilitySystemComponentについてのまとめ>
Ability System ComponentはGameplay Ability systemにおける8大コンセプトの一つですが、その目的は GameplayAbilitiesを保持、GameplayEffectsを実行し、そしてGameplayAttributesを管理と干渉する事です。更にアクターを通してGameplayAbilitiesを保持する事で、プレイヤーインプットからGameplay Ability の始動、確認、そしてコマンドのキャンセルへのバインドを扱う事が出来ます。今回のレシピは、この機能を利用する事してプレイヤーのインプットからアクターを操作します。
大雑把ですがAbilitySystemComponentについて分かりました。
では、本題に戻ります。
前回、サンプルコードにある以下の部分が何をしているのか今一、分からなかったので、AbilitySystemComponentについて調べ始めたのでした。
まず。GiveAbilityメンバー関数ですが、AbilitySystemComponent.hには以下のような説明がありました。
Abilityを許可します。TryActivateAbilityなどで使用出来るハンドルを返します。
とありました。そのままですね。
サンプルコードの説明でも、
後でアビリティを呼ぶためのアビリティのハンドルを保持します。
とありました。
次の行は
アビリティを呼ぶ整数のID(enumのリスト内での?番目の値)
とありました。そのまま追加します。次の行に行きます。
取りあえず、同じコードを書いてみました。
FGameplayAbiliyInputBindsがエラーになります。それで、FGameplayAbiliyInputBindsのコードを探したんですが見つかりません。
ひょっとしてと思い、FGameplayAbilityInputBindsを探したらありました。
GameplayAbilities and You (Unreal Wiki)には以下のように解説されていましたが、
スペルが直ったのでしょうか?GameplayAbilitiesの使い方(セットアップ編)をみれば、それが分かるかもしれないと見てみました。
載ってなかったです。GameplayAbilitiesの使い方(セットアップ編)はGameplayAbilities and You (Unreal Wiki)を分かり易く解説して実行したサイトと思ったのですが全然違ってました。
FGameplayAbiliyInputBindsはFGameplayAbilityInputBindsに直されたと仮定してFGameplayAbilityInputBindsを見ていきます。
AbilitySystemComponentに何とInputComponent(BindAbilityActivationToInputComponent参照)とバインドするか告げる構造体(structure)。
とありました。
先程、コピーしたサンプルコードのコメントには
インプットバインド(inputBinds)のオブジェクトを構築(construct)します。
インプットイベント(input event)とGameplayAbilityのinputPressed()/InputReleased()イベントを連結する事を許可します。
とありました。これらのコメントから類推するに、FGameplayAbilityInputBinds構造体は、2つの何かをバインドするようにAbilitySystemComponentに告げるための物みたいです。
次にFGameplayAbilityInputBinds構造体のパラメーターを見てみましょう。
これらのパラメーターは以下に示すように、FGameplayAbilityInputBinds構造体のメンバー変数とそれぞれ対応していますので、メンバー変数のコメントをそれぞれ見てみます。
コマンドストリング(command string)を定義します。それはConfirm Targetingとバインドします。
以下に示すサンプルコードの一部分は、
Confirm Targetingとバインドするコマンドストリングだそうです。
このFSはサンプルコード独自の定義で、
となっています。となると、GetName()関数はActorの名前、BindInfo.GameAbiityClass->GetName()関数は、BP_GameplayAbility_Attackの名前が選ばれるようですね。
サンプルコードのコメントには、
これらは、アクターのインスタンスのためにそのアビリティが何を開始するのかを定義するたった一つのストリングであるはずです。
”ConfirmTargetting_Player0_AbilityClass”のストリングのフォーマットを使用します。
とありました。ここから推測するに、ターゲットを確認するコマンドは、”ConfirmTargetting_Warrior0_ BP_GameplayAbility_Attack”みたくなるのでしょうか。
2番目のパラメーターは、
コマンドストリング(command string)を定義します。それはCancel Targetingとバインドします。
サンプルコードの以下の部分は、
となっていて、Cancel Targetingとバインドするコマンドストリングです。
GetName()関数はActorの名前、BindInfo.GameAbiityClass->GetName()関数は、BP_GameplayAbility_Attackの名前が選ばれるので、最初のパラメーターと同じですね。
3番目のパラメーターは、
となっていて、Cancel Targetingとバインドするコマンドストリングです。
GetName()関数はActorの名前、BindInfo.GameAbiityClass->GetName()関数は、BP_GameplayAbility_Attackの名前が選ばれるので、最初のパラメーターと同じですね。
3番目のパラメーターは、
アビリティのバインド(ability bind)に使用するためのEnumを返します。
”Ability1”から”Ability9”のインプットコマンドはAbilitySystemComponent内のアビリティの始動(ability activation)にバインドします。
まず、最初の行ですが、やっぱりアビリティはEnumで保持されているのですね。Part1のまとめで
と述べてから、他のサイトなどから全くこの話が出てこないので、何か間違えて理解したのかと心配していましたが、「アビリティ―は列挙型(enum)で示す。」は正しいようです。
”Ability1”から”Ability9”のインプットコマンドが何を指しているのか不明でしたが、以下に示すWarriorAbilitySet内のAbility1からAbility9を指していると考えられます。
サンプルコードの以下の部分は、
アビリティのリストを持つEnumの名前です。
となっていて、これも「アビリティは列挙型(enum)で示す。」を明示しています。
ここで、このアビリティのリストを持つEnumの名前をEGameplayAbilityInputBindsと定義していますが、この名前は何でもいいんでしょうか?それとも決まった名前があるのでしょうか?
GameplayAbilities and You (Unreal Wiki)では、
このアビリティのリストを持つEnumの名前は、”AbilityInput”となっていたので、何でもいいみたいです。
と思ったら、GameplayAbilities and You (Unreal Wiki)でAbilityInputと言う名前のEnumをしっかり作成していました。3番目のパラメーターで定義した名前のEnumを作成してそれを、Actorクラスの派生クラスのヘッダーファイルに実装しています。
サンプルコードの方のWarrior.hファイルにはEGameplayAbilityInputBindsと言う名前のEnumはないので、何処かで定義しないといけないのですが、サンプルコードのどこにも見つかりません。
思い余ってUE4のコードで検索したらGameplayAbilitySet.hにありました。
どうやら、サンプルコードはこのEnumを呼び出していたみたいです。このEnumを使用出来るのなら独自のアビリティのリストを持つEnumを作成する必要はない気がします。と言うかGameplayAbilitySetクラスから派生したWarriorAbilitySetのアビリティのコマンドはEGameplayAbilityInputBindsなので、他のEnumを選べるのでしょうか?
EGameplayAbilityInputBindsのコメントを以下に示します。
これはGameplayAbilitiesのためのインプットバインド(input binding)用のEnumの一つの例です。あなたのプロジェクトでそれ自体を作成してもいいです。
このメタデータデファルトバインド鍵(LMB、RMB、Q、Eなど)は、デザイナーがアビリティセット、
もしくは貴方が持っているデファルト鍵のバインドによってアビリティを与えるどんな他のデータでもセットするのに便利です。
例:”Ability1”はAbillitySystemComponent::ActivateAbillity(1)にバインドするコマンドストリングです。
このメタデータは単にあなたのプロジェクトのDefaultInput.ini内で、”Ability1”をLMBにバインドするように“提案”するだけです。
とありました。自分で作ってもいいみたいですね。
「アビリティは列挙型(enum)で示す。」はこのレシピの勉強を始めて、暗中模索の中もがいている時、最初に導き出した推論です。他のサイトなどから全くこの話が出てこないので間違えたのかと心配していましたので、この部分が正しい事が証明されたのは大変うれしいです。だれも言っていない事を述べるのは勇気が必要です。しかもほとんどの人はその内容は覚えていなくて「俺に逆らった!」と憎しみと怒りの感情だけ覚えています。ので後から正しいと証明されてもほとんどの場合、称賛を他人から受ける事はありません。しかし丁寧で正確な分析と推測から導き出した結論が他人の結論と違っていたからとして、変える訳にもいかないのもまた事実です。この正解はGameplayAbilities APIの勉強の方向性の正しさについての自信も与えてくれました。
ですが、アビリティのリストの中身はかなり想像と違っていました。まず、私が考えていたアビリティとは攻撃のみならず、単純な前後左右の動き、アイテムの使用も含んでいました。しかしこの教科書の例を見るに、アビリティのリストの中身に単純な前後左右の動きは含まれないみたいです。次に、私がイメージしていたアビリティのリストは「通常攻撃、炎攻撃、氷攻撃、回復…」みたいに具体的な内容が記されたリストだったのですが、これも教科書の例では「攻撃1、攻撃2、…」となっているだけでした。しかも、このアビリティのリストの一つ一つは、アビリティセット内で、UGameplayAbilityクラスから派生したUGameplayAbility_Attackクラスから作成したBPとバインドしています。つまり攻撃1の内容はこのBPが決定していると予測出来ます。しかしこのBPのdetailを見る限りでは、攻撃のクールダウン時間と攻撃のコストぐらいしか設定出来ないみたいです。私の想像では、炎攻撃なら炎のアニメーションの設定もここで設定すると思っていたので思っていたのとはまだ完全に一致はしません。
ところで、GameplayAbilities and You (Unreal Wiki)の例では、何でパラメーターが3つしかないんでしょうか。
FGameplayAbilityInputBindsの元のコードを見てみたら、以下に示す様に4番目と5番目のパラメーターはオプションでした。
では4番目のパラメーターを見てみます。
もし>=0ならば、確認(confirm)はenum内での入口とバインドしました。
とありました。サンプルコードの方では、
となっていて特に解説はありませんでした。このAbilityIDは、上の方で簡単に調べて終わりにしてしまいました。もしenum内にアビリティのリストがある場合は0以上の整数を保持しているはずです。UE4のAPIによるとINDEX_NONEは-1だそうです。その場合はenum内にアビリティのリストがあるか確認出来ないと言う事でしょうか?それでもバインドは出来るのでしょうか?
最後のパラメーターは
もし>=0ならば、enum内での入口とバインドした事をキャンセルします。
あり?どういう事でしょうか?ひょっとするとConfirmTargetInputIDとCancelTargetInputIDの値は後で変化するのかもしれませんね。サンプルコードの例にしろ、GameplayAbilities and You (Unreal Wiki)の例にしろ同じ値をパスしているのですから。
一応これで、FGameplayAbilityInputBinds構造体の中身がどうなっているのか確認は取れました。
サンプルコードの次の行を見てみましょう。
早速、前の行で作成したFGameplayAbilityInputBinds構造体のオブジェクトをパラメーターとしてパスしていますね。もう一つのパラメーターは、UInputComponentクラスから作成したオブジェクトです。何故かこのUInputComponentクラスをまだ調べていなかったので、今調べます。
UE4C++のAPIによると、
インプットバインドのためにアクターコンポーネント(actor component)を実装します。
インプットコンポーネント(input component)はアクターが色々な形状のインプットイベントとデリゲート関数をバインドする事が可能な過度コンポーネント(transient component)です。
インプットコンポーネントはPlayerControllerに管理されたスタックからとPlayerInputによって処理されます。
それぞれのバインド(binding)はそのインプットのイベントを消費出来ます。
そのインプットのイベントはインプットストックの他のコンポーネントのインプットの処理を防いでいます。
Remarkがありました。Remarkがあるだけでこんなに安心出来るものなのですね。ただこのremarkで述べている事は大体しか理解出来ませんが。このクラスは元からUE4C++のクラスなのでGameplayAbilities API以外で使用されるはずですので大体分かればいいとも思います。
次にBindAbilityActivationToInputComponentメンバー関数について調べます。
まず、サンプルコードのコメントには、
それぞれのアビリティとインプットコンポーネントはバインドしなければなりません。
さもないとそのアビリティはインプットイベントを聞く事が出来ません。
もしあなたが選択したならば、これはInputPressed()/InputReleased()イベントを引き起こす事が可能です。
実際にはInputPressed()/InputReleased()イベントはTryActivateAbiity()を呼ぶために使用します。
とありました。この関数はUInputComponentクラスから作成したInputと、FGameplayAbilityInputBinds構造体から作成したInputBind内のアビリティをバインドするための関数のようですね。一々コメントを読むより、関数の名前そのもの(BindAbilityActivationToInputComponent)がその関数の目的を表していますね。
そのままコピーします。
遂にサンプルコードのSetupPlayerInputComponent関数の実装部の最後の行に来ました。
サンプルコードのTryActivateAbiity()関数のコメントには、
活動状態のアビリティをテストします。
あなたは手動でWarriorクラスのキープレスとの繋がりを試せます。
もしアビリティが呼ばれた場合、これは自身の内部ルールに基づいて自動チェックします。
実際のコードの方には、コメントはありませんでした。そのままコピーします。
一応これで、SetupPlayerInputComponent関数の実装部のコードの総てを調べました。
ただし、まだ全体を把握出来ていないので、来週もう一度この関数のまとめを今週と先週の調査を基に行います。
今週はここで終了します。