<前文>
で勉強しています。今回は、「第9章6節 Unrealのモーショングラフィックにバインドするデータの使用」を勉強します。
<本文>
<目的>
今回は「第9章6節 Unrealのモーショングラフィックにバインドするデータの使用」を勉強します。が、一体何を勉強するのか今一不明です。そう思っていたら、教科書の前文に解説がありました。今までの我々のUIウィジェットに対する値は全て静的でしたが、動的な値を使用出来たらゲームの最中にUIを変化したりなど色々出来ます。UnrealではAttributeシステムを使用して関数の返り値をattributeとバインドする事で動的な値をUIウィジェットに対する値やパラメーターを使用出来るようにします。今回はそのやり方を勉強します。
Unrealモーショングラフィックは一体何の事だろうと一瞬思いましたが、UMIの事でした。
<方法>
Step.0
今回も新しいプロジェクトを作成します。名前は、Chapter9part6_0710とします。使用するunreal のversionは、4.19.2です。
今回もSlate、SlateCoreのモジュールを使用するため、Build.csのSlate、SlateCoreをアンコメントしておきます。
Step.1
今回も、クラスウィザードから作成します。親クラスはGameModeでGameModeBaseではありませんので注意が必要です。
AttributeGameModeと名付け、Privateを選択します。
教科書のAtributeはスペルミスです。Attributeが正しいのでAttributeGameModeと名付けました。このモジュールはプラグインではないので他のモジュールから使用される事はありませんので、privateにしました。
Step.2
以下に示すように追加しました。SVerticalBoxがエラーを吐いていたので、インクルードにSBoxPanelのヘッダーファイルも加えました。
Step.3
以下に示すように、関数らをpublicに追加しました。
Step.4
以下に示すように、追加しました。Engine.hのインクルードを追加しています。
Step.5
以下に示すように、追加しました。
<結果>
Step.6
しました。
Step.7
以下に示すようにしました。作成したゲームモードの名前は、AtributeGameModeとなっていました。AAtributeGameModeではなかったですが、そのまま行きます。
Step.8
なりました。
プレイヤーの位置を変更すると、ボタンに表示される数字も変わりました。
<考察>
今回も、How it works…を読んでいきます。
はい。
動的な変化をボタンのtextに対して行うためにはそれの設定方法が今までの静的な設定とは違うはずなのでここが最も重要なポイントであるはずです。
3.以降でこの部分のコードに対しての解説があるはずですが、一応、自分でも調べて見ましょう。
まず、最初にボタン上の静的なテキストの作成方法ですが、最も簡単な例を以下に示します。
上記のコードは、「第9章2節 SlateWidgetをスクリーンに追加します」から引用したものです。Text( )のパラメーターが全く違います。まず、今回の場合、TAttribute<Ftext>を使用しています。APIによると、TAttributeは、
属性オブジェクト
とだけ説明されています。プログラミングにおけるAttribute(属性)そのものに対して良く分かっていないので、まず調べて見ます。ウィキペディアの属性のページでは、
と説明されていました。更に調べて見ると属性とは更なる情報をオブジェクトや、プロパティー、関数などに追加する方法のようです。もしかするとTAttributeクラスはFtextクラスに更に何かの情報を追加するクラスなのかもしれません。
Tで始まるのでクラスのタイプはtemplateですので、TAttribute<Ftext>のように使用するのは分かります。
次に、TAttributeクラスのメンバー関数であるCreate()を調べて見ましょう。
マウスをCreate関数に重ねて関数の情報を見ると以下のように表示されましたので、
APIにある4つの以下に示すCreate関数のうち
最初の関数を使用している事は分かりました。最初のCreate関数のremarkは、
静的:特定のゲッターのデリゲートに予めバインドするattributeを作成する。
とありました。動的じゃないんですね。
次は、FGetterについて調べて見ます。
属性‘ゲッター’デリゲート
オブジェクトタイプ GetValue() const
属性の値
となっていました。全く分かりませんね。正直、ポエムが何かを読んでる気分です。そう言えば、FGetterは、TAttributeクラスの関数か何かのはずです。TAttributeクラスのAPIにも何か説明があるかもしれません。
ありましたが、同じでした。
全然分からないままですが、次を見てみます、次は、CreateUObjectです。CreateUObjectのAPIを見てみると、
となっていました。これはどこかで見た事ありますね。でもTAttribute<Ftext>を使うのは初めてなのですが。でよく見ると、TBaseDelegate<Ftext>になっています。あれっと思ってもう一度見返してみるとFGetterからTAttribute<Ftext>がTBaseDelegate<Ftext>になっていました。
これで、少しは、分かってきました。まず、動的にボタン上のテキストを変更するためには、Attributeを使う必要があります。それは、TAttribute<FText>です。更に、TAttributeのメンバー関数であるCreateを使用して動的なテキストを作成します。しかし、実際のデリゲートを呼ぶ段階になったら、CreateUObjectデリゲート関数を使う必要があります。しかしCreateUObjectはTAttribute<Ftext>からでは呼べないのです。TBaseDelegate<Ftext>からしかCreateUObjectは呼べません。のでFGetterの出番です。FGetterはTAttribute<Ftext>から呼び出せて、返し値のタイプが、TBaseDelegate<Ftext>になるのです。CreateUObjectデリゲート関数のパラメ―ターは前に勉強した内容ですので、ここではスキップします。
これだけでもかなり理解出来ましたが、分からない点もまだたくさんあります。以下に整理します。
この辺が自分のみの勉強の限界でしょうか?次に行きたいと思います。
3.は大変長い文章なので、以下に箇条書きにして、内容をまとめ直します。勿論、2.で考察したコードについての解説です。以下にそのコードを示しておきます。
(a) 大変長いコードだが行っている事は単純
(b) FAttribute<FText>をText( )のプロパティーとして充てる事が出来る。
(c) TAttribute Get( )メソッドは、そのUIのTextの値が最新であると言う保証が欲しい時いつでも呼ぶ事が出来る。
まず、(b) ですがText(FText)のFTextの部分をプロバティーと呼んでいます。このFtextの代わりにTAttribute<Ftext>を使用する事が出来ますと言っているだけです。つまり、Text(TAttribute<FText>)と書けると言う事を言っています。しかし何故FAttributeでなければならないかは分かりません。それについては3.では解説されていませんでした。次に(c) ですが、TAttribute Get( )メソッドとは、FGetterを指していると考えられます。今までの我々の予測では、FGetterはTAttribute<Ftext>をTBaseDelegate<Ftext>に変換するのが主な役割と考えられていましたが、更に「そのUIのTextの値が最新であると言う保証が欲しい時いつでも呼ぶ事が出来る。」と言う機能がある事も分かりました。
ひょっとすると、Create関数の本当の機能とは、常にFGetterを呼び出す事かも知れません。
はい、違いました。Create関数の機能はTAttribute< >を作成する事でした。またこのCreate関数が静的であるとここでも解説されてます。
はい。Descriptionに対する適切な訳が分からないです。説明と訳しましたがそれでは意味がつながりません。また、違うタイプのデリゲートが呼ばれるとありますが、このレシピではCreateUObjectが呼ばれています。それ以外のデリゲート関数が呼ばれる事も出来ると言う事でしょうか?
5.で「このレシピではCreateUObjectがデリゲート関数として呼ばれています。それ以外のデリゲート関数が呼ばれる事も出来ると言う事でしょうか?」と書きましたが6.を読むとそれが正しいみたいです。6.ではこのレシピで使用されるデリゲートがUObjectクラスのメンバー関数なのでそれを呼び出すデリゲート関数はCreateUObjectデリゲート関数じゃないといけないよねと言っています。実際にこのレシピで呼び出されるデリゲートはGetButtonLabelメンバー関数でAAttributeGameModeクラスから呼び出されAAttributeGameModeはUObjectの派生クラスなので6.の解説の通りです。
ここで、さらに5.の「このレシピではCreateUObjectが呼ばれています。それ以外のデリゲート関数が呼ばれる事も出来ると言う事でしょうか?」についての詳しい解説が書かれていました。CreateUObject以外にCreateLamda、CreateStatic、そしてCreateRawが、デリゲートが生のC++上にある場合使用出来るようです。
7.では「どんなデリゲートのタイプが(そのデリゲートの)インスタンスの作成のために必要なのか?」が解説されています。
(a) 実際の変数タイプを持つTAttributeクラスをテンプレート化した。
(b) (a)より、デリゲートの返り値もTAttributeクラスと同じ変数のタイプをテンプレート化したものが必要。
はい。ただ具体的にどこまでを解説しているのかが分かりません。具体的に説明すると、サンプルコードでは最初はTAttribute<FText>だったのがTBaseDelegate<FText>に代わっています。両方とも同じタイプの変数を持つテンプレート化した変数ですが同じではありません。7.の説明を読むと変数のタイプが同じであるのでこのサンプルコードの場合でもいいと解釈出来るのでしょうか?それともこのサンプルコードの例は特別でテンプレート化する場合も同じタイプでテンプレート化しないといけないのでしょうか?
はい。FTextを返せばいいのですね!この8.の解説だとテンプレート化のタイプが同じである必要はないと解釈出来ます。
ウーン。このコード?の意味する処は良く分かりません。とりあえず次に行きます。
10.から12.はFGetterについての解説です。
10.から12.までは、3.で解説した「FGetterはTAttribute<Ftext>をTBaseDelegate<Ftext>に変換する」機能についての解説でした。
はい。
はい。
これは、プロパティーの値が変化した場合、TAttributeにそのプロパティーがリンクされると言う事なんでしょうか?それとも以下に示すようなTAttributeが呼ばれるウィジェットの事をプロパティーがリンクしていると言ってそれがフレーム毎に呼ばれるのでしょうか?
はい。
ここから、GetButtonLabel関数の解説ですが少ししか解説していません。以下にコードを示します。
まず、ファーストプレイヤーの位置ですが、普通に取ってきています。
GetButtonLabel関数のreturnについての解説です。特に不思議な処はないです。
<まとめ>
今回は、ウィジェットをゲームプレイ中に動的に変更する方法を勉強しました。サンプルはボタン上のテキストでプレイヤーの現在の位置を常に表示します。プレイヤーが動けば位置の座標も同時に変化します。その方法はText( )の中からTAttributeのテンプレートを使用します。今回のサンプルの場合では、FtextをTAttributeでテンプレート化します。
Text(TAttribute<FText>)
これだけではFtextをTAttributeでテンプレート化するには足りません。Create関数を付ける必要があります。
Text(TAttribute<FText>::Create( ))
Create関数内ではdelegateを使用出来ます。今回のサンプルではプレイヤーの位置を習得する関数、GetButtonLabel()を作成してこの関数をデリゲートとして呼び出します。
GetButtonLabel()
今までの勉強でUObjectクラスから派生したクラスの関数をデリゲートとして呼ぶ場合はCreateUObjectデリゲート関数を使用しなければならない事は知っています。GetButtonLabelメンバー関数があるAAttributeGameModeクラスは、UObjectクラスの派生クラスなので、CreateUObjectデリゲート関数を使用する必要があります。
CreateUObject(this, &AAttributeGameMode::GetButtonLabel)
このCreateUObjectデリゲート関数はTBaseDelegateテンプレートのメンバー関数であるのでこの関数を使用するためにはTAttribute<FText>をTBaseDelegate<Ftext>に変更する必要があります。これはFGetter関数が行います。
Text(TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateUObject(this, &AAttributeGameMode::GetButtonLabel)))
以上のようなコードなります。
<おまけ>
今回は時になしです。