UE4の勉強記録

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

9章7節 スタイルを使用してウィジェットの外観をコントロールする

<前文>

f:id:kazuhironagai77:20180729084456p:plain

で勉強しています。今回は、「9章7節 スタイルを使用してウィジェットの外観をコントロールする」を勉強します。

<本文>

<目的>

今回はボタンのデザインのカスタム化の方法を習います。ただ、実際にゲーム内でオリジナルなデザインのボタンを作成する場合はHUDで作成すべきだと思います。勿論、プログラマーC++側からでもボタンのデザインのカスタム化が出来ると言う事を知っておくべきですが。

<方法>

Step.0

今回からは、version 20.0を使用します。Projectの名前は、Chapter9part7_0724とします。私はVisual Studio 2017をかなり昔にダウンロードして放置しておいたのですが、今回Project作成時にエラーが出ました。Visual studio 2017をupdateした後Projectを作成し直したら今度は普通に作れました。

今回もslateとSlateCoreモジュールを使用するはずなので、build.csの以下に示す箇所を

f:id:kazuhironagai77:20180729084946p:plain

アンコメントしました。

Step.1

f:id:kazuhironagai77:20180729085011p:plain

今回は、生のC++なので、VS側から作成しようと思ったのですが、クラスウィザードに

f:id:kazuhironagai77:20180729085100p:plain

Noneという選択肢があり、使って見たくなりましたので、これで作成してみます。

f:id:kazuhironagai77:20180729085130p:plain

ファイルの場所は、VSで作成した場合と違い変更する必要はないみたいです。他のモジュールからアクセスされる予定はないのでprivateに設定します。

この時VSを開いたままCreateClassボタンを押してしまったと焦ったのですが、何の問題もなくCookBookStyleクラスが作成出来ました。Ver20はVSを開いたままクラスウィザードからクラスを作成しても大丈夫みたいです。

f:id:kazuhironagai77:20180729085412p:plain

f:id:kazuhironagai77:20180729085432p:plain

Step.2

f:id:kazuhironagai77:20180729085506p:plain

f:id:kazuhironagai77:20180729085521p:plain

タイプするのが面倒なので、Packet Publish社が提供するサンプルコードをコピーしました。

f:id:kazuhironagai77:20180729085548p:plain

Step.3

f:id:kazuhironagai77:20180729085615p:plain

f:id:kazuhironagai77:20180729085634p:plain

f:id:kazuhironagai77:20180729085651p:plain

f:id:kazuhironagai77:20180729085723p:plain

これも面倒なので、サンプルコードをそのままコピーしました。

ただし、#include "Chapter9.h"は#include "Engine.h"に変更しました。以下のようになりました。

f:id:kazuhironagai77:20180729085805p:plain

全体が大きいので、NotePad++で開いて撮影しました。

まず、BOX_BRUSHがエラーを吐いています。

f:id:kazuhironagai77:20180729085837p:plain

更に、教科書に書いてある”include ”UE4Cookbook.h”がサンプルコードにはありません。

f:id:kazuhironagai77:20180729085901p:plain

f:id:kazuhironagai77:20180729085913p:plain

とりあえず、ビルトしてみます。

f:id:kazuhironagai77:20180729085937p:plain

ビルド出来ちゃいましたので、このまま行きます。

Step.4

f:id:kazuhironagai77:20180729090113p:plain

f:id:kazuhironagai77:20180729090131p:plain

はい。まずクラスウィザードからGameModeを選択します。名前をStyledHUGGameModeとして新しいGameModeクラスを作成します。

f:id:kazuhironagai77:20180729090223p:plain

勿論、Privateを選択します。

f:id:kazuhironagai77:20180729090248p:plain

思いっきりエラーが表示されていますが、

f:id:kazuhironagai77:20180729090311p:plain

以下に示すように、ビルドは成功しているのでエラー表示は無視します。

f:id:kazuhironagai77:20180729090338p:plain

コードを一々写すのは面倒なので、Packet Publish社のsample codeからコピーします。

f:id:kazuhironagai77:20180729090426p:plain

Step.5

f:id:kazuhironagai77:20180729090457p:plain

f:id:kazuhironagai77:20180729090518p:plain

こちらも一々写すのは、面倒なので、Packet Publish社のsample codeからコピーします。

f:id:kazuhironagai77:20180729090557p:plain

BeginPlay関数がエラーになっていますが、とりあえず、ビルドしてみます。

f:id:kazuhironagai77:20180729090829p:plain

思った通り普通にビルト出来ました。

Step.6

f:id:kazuhironagai77:20180729090906p:plain

f:id:kazuhironagai77:20180729090955p:plain

Contentフォルダー内にSlateフォルダーを作成し自作したButton.pngを保存しました。

f:id:kazuhironagai77:20180729091102p:plain

この方法では、Button.uassetは作成されません。と思っていたら

f:id:kazuhironagai77:20180729091123p:plain

エディターの方で、勝手に作成してくれました。

Step.7

f:id:kazuhironagai77:20180729091159p:plain

f:id:kazuhironagai77:20180729091217p:plain

今一、何処を変更すればいいのか不明ですが、以下に示すクラスが、FDefaultGameModelImplを使用しているので、このクラスを上記に示されているように変更します。

f:id:kazuhironagai77:20180729091243p:plain

とりあえず、以下のようにコードを足してみました。

f:id:kazuhironagai77:20180729091518p:plain

サンプルコードにこの部分はなかったので全部手打ちでコピーしました。ケアレスミスがない事を祈りましょう。

<結果>

Step.8

f:id:kazuhironagai77:20180729091628p:plain

ビルドをしてみます。

f:id:kazuhironagai77:20180729091651p:plain

ビルドは成功しました。GameModeをStyledHUGGameModeに変更しました。

f:id:kazuhironagai77:20180729091715p:plain

途端にエラーでエディターが停止してしまいました。

ウーン。色々、調べて見ると、ヒストリアさんのブログに答えが載っていました。

f:id:kazuhironagai77:20180729091749p:plain

Step.7と同じコードですが、最後の一行が加えられています。

もう一度、ビルトしてエディターを起動し、GameModeをStyledHUGGameModeでオーバーライドしてゲームをプレイしてみます。

f:id:kazuhironagai77:20180729091818p:plain

今度は、しっかり出来ました。

Step.9

f:id:kazuhironagai77:20180729091855p:plain

f:id:kazuhironagai77:20180729091904p:plain

Step.8に示すように出来ています。

<考察>

今回もHow it works…を読んでいきます。

f:id:kazuhironagai77:20180729091957p:plain

コードのどこを説明しているのかまだ不明です。多分まだ今回のレシピ全体の説明をしているのでしょう。1.では以下の事が述べられています。

 (ア) 複数のSlateウィジェットを通して共有できるスタイルを作成したい。

 (イ) そのためには、そのスタイルを保持するオブジェクトを作成する必要がある。

 (ウ) 更に、そのオブジェクトをスコープの範囲内に留める必要がある。

まず、スタイル(style)について考察しましょう。プログラミングとは関係ない一般的なstyleの訳は「思想の表現方法」だそうです。ここではウィジットの形状を示しているのでしょうか。全然関係ない話ですが大学の授業でscientific visualizationについて学んだとき教授が「科学実験の可視化のためのデザインは必要最低限の要素しか入れてはいけない。絶対に如何なる思想を入れてもいけない。」と言っていたのを思い出しました。その時、私はデザインは思想そのもので、科学もある種の思想なので、そんなのは無理じゃないかと思ったのを思い出しました。

兎に角、ウィジェットのスタイルを自由に作成する方法がUnrealC++にはあるみたいですね。しかしそれを使用するためには、(イ)と(ウ)の条件を満たさないといけないそうです。(イ)は単純にあるクラスを自作してその中で、ウィジェットのスタイルを指定すればいいと考えられますが(勿論具体的な方法は分かりませんが…)、(ウ)については?です。オブジェクトのスコープなんで勝手に適切にやってくれるじゃないの?と思ってしまいます。まあ、次を読んでいくうちに判明するでしょう。

f:id:kazuhironagai77:20180729092542p:plain

FSlateStyleSetクラスについて解説しています。これはFCookBookStyleクラスの解説が始まるのでしょうか?いずれにせよFSlateStyleSetクラスが1.で述べている要件を満たす機能を持つクラスと言う事が分かりました。FSlateStyleSetのAPIを見てみると、

f:id:kazuhironagai77:20180729092813p:plain

名付けられたプロパティの収集を含むSlateスタイルの塊。

名付けられたプロパティはSlateの外観を案内する。

その瞬間は基本的にFEditorStyle.

とありました。2.とAPIのRemarkからFSlateStyleSetクラスが沢山のスタイルを保持しているのは間違いないみたいです。それらのスタイルがウィジェットの外観を決定しているのでしょう。それをウィジェットのプロパティ一つ一つに当てはめるのでしょうか?

ここに、スタイルとプロパティについての解説がありました。

f:id:kazuhironagai77:20180729092952p:plain

ここでの解説は、UMGにおけるウィジェットとプロパティの関係をエディターを通して解説してましたが、FSlateStyleSetクラスにおいても同じような気がします。

実際のソースコードも探してみました。

f:id:kazuhironagai77:20180729093128p:plain

ウーン。よくは分からないですが、FSlateImageBrushクラスがウィジェットの外観を決定しているみたいです。ただFSlateStyleSetクラスは「名付けられたプロパティの収集を含むSlateスタイルの塊」とあったので、上記のような設定を沢山保持している、もしくは保持できると思っていました。

2.で「ウィジェットのスキンのために…」とありましたが、スキンとは具体的には外観を指す単語とここでは考えています。

f:id:kazuhironagai77:20180729093300p:plain

はい。StyleSetのオブジェクトは、本当は一個あればいいのですね。

f:id:kazuhironagai77:20180729093448p:plain

3.の条件を満たすためにはStyleSetクラスはシングルトンが良かったのですがStyleSetはシングルトンではないので、あるクラスを作成しそのクラスに3.の条件を満たせるようにします。そのクラスは、

 (ア) StyleSetのオブジェクトを管理

 (イ) たった一つのインスタンスしか持てない

と言う条件を満たしています。このクラスがFCookbookStyleクラスなのでしょう。

f:id:kazuhironagai77:20180729093533p:plain

まさに、その通りでしたね!

f:id:kazuhironagai77:20180729093609p:plain

6.では以下の事が述べられています。

 (ア) FCookbookStyleクラスにはInitialize()関数がある。

 (イ) Initialize()関数はモジュールのStartupのコードから呼ばれます。

実際のコードを見て確認してみましょう。まずFCookbookStyleクラスのヘッダーファイルの一部を以下に示します。

f:id:kazuhironagai77:20180729093654p:plain

確かに、Initialize()関数はあります。ありますが、モジュールのStartupのコード云々についてはここからは不明ですね。次にソースファイルを見てみましょう。

f:id:kazuhironagai77:20180729093732p:plain

CookbookStyleInstanceが既に作成されている場合は、Initialize関数は何もせず、CookbookStyleInstanceが作成されていない場合のみCreate関数を使用して、CookbookStyleInstanceを新たに作成してRegisterSlateStyle関数を使用して新たに作成したCookbookStyleInstanceを登録するようです。この方法なら常に一個のオブジェクトしか持てないと言う4.の条件を満たせるはずです。

ただここから、6 .で 述べられているStartupのモジュール云々については不明です。Startupのモジュール云々は、ひょっとすると、FSlateStyleRegistryクラスの事について述べているのかもしれません。調べて見ましょう。

FSlateStyleRegistryクラスのAPIによると

f:id:kazuhironagai77:20180729093831p:plain

セントラルレポジトリ

Slateスタイルのデータの塊の追跡や管理に使用される

とありました。FSlateStyleRegistryクラスが属しているモジュールはSlateCoreモジュールでした。

f:id:kazuhironagai77:20180729093907p:plain

当たり前と言えば当たり前ですね。「…モジュールのStartupのコードから呼ばれます。」についてはちょっと不明です。

f:id:kazuhironagai77:20180729093938p:plain

はい。以下の部分のコードの話ですね。

f:id:kazuhironagai77:20180729094119p:plain

f:id:kazuhironagai77:20180729094204p:plain

はい。以下に該当する部分のコードを示します。

f:id:kazuhironagai77:20180729094248p:plain

f:id:kazuhironagai77:20180729094302p:plain

はい。以下に該当する部分のコードを示します。

f:id:kazuhironagai77:20180729094332p:plain

7.、8.、と9.についてはInitialize関数の実装部についての解説ですが、6.で考察した以上の解説はなかったですね。とりあえず、こう書くものだと覚える以外ないようですね。

f:id:kazuhironagai77:20180729094456p:plain

ここからは、Create関数についての解説でしょうか?以下にCreate関数のコードを示しておきます。

f:id:kazuhironagai77:20180729094532p:plain

ウーン。このコードの解説が次から始まるのでしょうか?ちょっと12.を見て確認します。どうやら、Create関数のために必要なマクロの定義についての解説から始まるみたいです。

f:id:kazuhironagai77:20180729094620p:plain

まず、以下にCreate関数に使用されているマクロを示します。

f:id:kazuhironagai77:20180729094702p:plain

はい。マクロについて復習が必要です。以下にここから引用したマクロの関数の例を示します。

f:id:kazuhironagai77:20180729094746p:plain

まず、マクロの名前とパラメーターが来て、その後で実際の実装が来ると言う事ですか。ここを見ると、正しく使用するのは簡単ではないようですね。Create関数に使用されているマクロでは、パラメーターの…と_VA_ARG_が良く分からないです。調べて見ましょう。

_VA_ARG_から調べて見ます。このサイトによると

f:id:kazuhironagai77:20180729094819p:plain

関数と同じだけの数のアギュメントを受け入れるために宣言されたマクロ。

このマクロを定義するためのシンタックスは関数のそれと似ている。

となっていました。このサイトはGNU.orgのコンパイラーについての解説ですが、多分他も同じでしょう。ついでに、…の謎も解けてしまいました。…と_VA_ARG_は、アギュメントを増やせるだけ増やせると言う意味だったのですね。となると、FSlateImageBrushなどのパラメーターの宣言が気になります。それも調べて見ます。FSlateImageBrushのAPI によると、

f:id:kazuhironagai77:20180729094932p:plain

となっていました。分かりました。どのコンストラクターが来ても、…と_VA_ARG_のコンビで定義したマクロなら、正しいアギュメントをパス出来るのですね。

ウーン。それぞれのマクロが何を意味しているのか分かりましたが新たな疑問も出て来ました。これってあえてマクロを書かないといけないのでしょうか?ここで言われているようにマクロを正しく使用するのは簡単ではないですし、絶対マクロを使用した方がいい理由がないのにマクロを使用するのはあまり乗り気ではありません。

f:id:kazuhironagai77:20180729095113p:plain

はい。

f:id:kazuhironagai77:20180729095204p:plain

Create関数の前後でマクロの#defineと#undefが行われています。

f:id:kazuhironagai77:20180729095228p:plain

14.では以下の事が述べられてます。

 (ア) “ FPaths::GameContentDir() / "Slate"/ RelativePath + TEXT(".png")”をマクロで使用したので、Create関数内のコードがシンプルになった。

これがマクロを使用する最大の理由だったのですね。12.における疑問も片付きました。ウーン…これが唯一の理由だったらあえてマクロを使用しない選択肢もあったのではないでしょうか?

はい。試してみます。

f:id:kazuhironagai77:20180729095353p:plain

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

f:id:kazuhironagai77:20180729095417p:plain

ウーン…。出来るとは思うんですが、どこかにケアレスミスがあるのかもしれません。ただ出来ない以上はマクロを使って書くしかありません。

f:id:kazuhironagai77:20180729095439p:plain

以下に実際のコードを示します。

f:id:kazuhironagai77:20180729095513p:plain

はい。15.で説明してる通りですね。FSlateGameResourceAPIを見てみると、Remarkがありませんでした。しかしFSlateGameResources::NewAPIのRemarkには以下の解説がありました。

f:id:kazuhironagai77:20180729095606p:plain

かなり長い文なので以下にまとめました。

f:id:kazuhironagai77:20180729095631p:plain

一見分かり易いですが、良く考えると分からない解説です。まず、ScopeToDirectoryが何なのか分かりません。次にInBashPathとAssetPathも何を指しているのかが不明です。例1と例2はファイルの探索が厳密にScopeToDirectory内しか行わないと言う事を具体的に示していて非常に分かりやすいですが、例文がResources.Initialize() Resources.GetBrush()となっていてFSlateGameResources::New()関数ではありません。ここでResources.Initialize()がFSlateGameResources::New()と同じなのかどうかも良く分かりません。うーんとなっていたら、syntaxを見逃していました。

f:id:kazuhironagai77:20180729095718p:plain

syntaxを見たらScopeToDirectoryとInBashPathが何なのかすぐに分かりました。と同時にResources.Initialize()、Resources.GetBrush()がそれぞれ何を意味しているのかも分かりました。Resources.Initialize(ScopeToDirectory, InBasePath)Resources.GetBrush(AssetPath)と言う意味でした。この例では、

 ScopeToDirectory = "/Game/Widgets/SFortChest"

 InBasePath= "/Game/Widgets"

 AssetPath= "SFortChest/SomeBrush"(例1)もしくは、"SSomeOtherWidget/SomeBrush"(例2)

となっています。例1の場合は、実際のパスはInBashPath+”/”+AssetPathのルールにより、/Game/Widgets/ SFortChest/SomeBrushとなります。これはScopeToDirectoryの範囲である/Game/Widgets/SFortChest内にあるフォルダーです。よってここで指定されたパス内のフォルダーは探索されます。例2の場合は実際のパスは/Game/Widgets/SSomeOtherWidget/SomeBrushとなり、これはScopeToDirectoryの範囲である/Game/Widgets/SFortChest外にあるフォルダーです。よって例2の場合は失敗します。

大変良く分かりましたので、実際のサンプルコードの場合を見てみましょう。

f:id:kazuhironagai77:20180729095857p:plain

 ScopeToDirectory = "/Game/Slate"

 InBasePath= "/Game/Slate"

となっていました。両方同じならスコープの範囲外を探索するような命令は出せないですね。ちなみに、サンプルコードでは、AssetPathはマクロで設定されていますが、

 AssetPath=“FPaths::GameContentDir() / "Slate"/ RelativePath”

となっていました。私の場合ですが、実際のパスが"D:\2018\UE4_2018\UE4C++Tutorial_ForBlog\Chapter9\Chapter9part7_0724\Content\Slate\button.png"なのでFPaths::GameContentDir()はD:\2018\UE4_2018\UE4C++Tutorial_ForBlog\Chapter9\Chapter9part7_0724\Contentに変換されるはずです。

実際に以下のようなテストをしてパスを見てみると、

f:id:kazuhironagai77:20180729100029p:plain

../../../../../2018/UE4_2018/UE4C++Tutorial_ForBlog/Chapter9/Chapter9part7_0724/Content/

となっていました。

../は一つ上のファイルを意味するはずなので、Chapter9part7_0724->Chapter9->UE4C++Tutorial_ForBlog ->UE4_2018->2018->D->2018->UE4_2018->UE4C++Tutorial_ForBlog->Chapter9->Chapter9part7_0724->Contentと言う事を実際はしているのでしょうか?

f:id:kazuhironagai77:20180729100208p:plain

はい。15.で既に考察した通りです。

f:id:kazuhironagai77:20180729100244p:plain

17.は15,16,で設定した方法が以下に述べる性質を持っている事を述べています。

 (ア)  違うフォルダー内にある同じ名前を持つイメージを指す複数のスタイルセットを宣言する事を可能にします。

 (イ)  単純に他のフォルダーのスタイルセットに変える事で、UI全体をスキンする事、もしくは再度スタイルする事を可能にします。

ここで、大体は同意なのですが、同じ名前のイメージである必要なないと思います。イメージの名前は、以下に示すマクロの

f:id:kazuhironagai77:20180729100352p:plain

RelativePathによって定義されているので、同じ名前である必要はないと思いますがどうでしょうか?

f:id:kazuhironagai77:20180729100414p:plain

はい。以下に実際のコードを示します。

f:id:kazuhironagai77:20180729100436p:plain

f:id:kazuhironagai77:20180729100451p:plain

はい。

f:id:kazuhironagai77:20180729100557p:plain

はい。以下にその部分のコードを示します。

f:id:kazuhironagai77:20180729100622p:plain

Setは2回使われています。

f:id:kazuhironagai77:20180729100848p:plain

f:id:kazuhironagai77:20180729100913p:plain

2回のセットの使用におけるパラメーターの種類と数が一致しているので、同じオーバーロードを使用していると考えられます。さらにsetのAPIを調べて見ると、

f:id:kazuhironagai77:20180729100939p:plain

コードはInclineではだめ

となっています。マクロよりInclineを使用すべきと述べるC++の教科書は多いですが、このSet関数内ではinclineは使用出来なかったのですね。しかし12.でやった実験のそのままコードを書いてもエラーになる理由はまだ分かりません。

f:id:kazuhironagai77:20180729101023p:plain

はい。一番目のパラメーターは、FName PropertyNameなのでスタイルの名前なのでしょう。2番目のパラメーターは、APIのsyntaxによるとDefinitionType & InStyleDefintionとなっていてこれがスタイルオブジェクトなのでしょうか。実際のサンプルコードを見てみると、FButtonStyle、FTextBlockStyleとなっています。これらはスタイルオブジェクトなのでしょうか?

FButtonStyleのAPIFTextBlockStyleのAPIをみてみると両方とも、FSlateWidgetStyleのサブクラスでした。このFSlateWidgetStyleのサブクラスから作成されるオブジェクトをスタイルオブジェクトと呼んでいるみたいです。なので以下に示すFSlateWidgetStyleのサブクラスならばどれでもいいみたいです。

f:id:kazuhironagai77:20180729101136p:plain

f:id:kazuhironagai77:20180729101215p:plain

はい。Slateのチェーン化と同じですね。以下に実際のコードを示します。

f:id:kazuhironagai77:20180729101232p:plain

FButtonStyle()は、SetNormal()を使用しています。FButtonStyle()のAPIを見てみるとSetNormal()以外にも以下のメンバー関数がありました。

f:id:kazuhironagai77:20180729101309p:plain

何か簡単なメンバー関数を試してみたいと思っていたら、SetHovered()がありました。パラメーターがFSlateBrushでSetNormalと同じです。コードを以下に示すように改造してテストしてみました。

f:id:kazuhironagai77:20180729101400p:plain

とりあえず、ビルドは出来ました。

f:id:kazuhironagai77:20180729101430p:plain

実行して、エディターを開き、ゲームモードをStyledHUGGameModeにオーバーライドして、ゲームをプレイすると、以下に示したような白色のボタンが現れました。

f:id:kazuhironagai77:20180729101457p:plain

更に、マウスをボタン上に重ねると、以下に示したように、カスタム化したボタンのイメージが現れました。

f:id:kazuhironagai77:20180729101518p:plain

FTextBlockStyle関数の方も色々試したかったですが、学習内容的にはFButtonStyle関数と同じ事の繰り返しになるのでここでは省略します。

f:id:kazuhironagai77:20180729101640p:plain

はい。23.は21.の解説の追加です。21.で述べた「一番目のパラメーターは、FName PropertyName…」の考察が正しいかったと言う事ですね。

f:id:kazuhironagai77:20180729101748p:plain

f:id:kazuhironagai77:20180729101806p:plain

はい。

f:id:kazuhironagai77:20180729101830p:plain

はい。これも、22.で考察した結果が正しかった証明ですね。22.で「Slateのチェーン化と同じですね。」と述べていますが、まさしくその事をここで述べています。

f:id:kazuhironagai77:20180729101919p:plain

26.では、SetNormal()関数についての解説が述べられています。

f:id:kazuhironagai77:20180729102026p:plain

はい。SetNormal()関数については散々やったのでここではスキップします。

はい。今まで散々考察して来たマクロを使用する話です。そこは問題なく理解出来るのですが、次の9スライススケール(nine-Slice scaling) 云々が良く分かりません。この辺をここでは掘り下げてみましょう。

ウィキペディアの9スライススケール(nine-Slice scaling)によると、

f:id:kazuhironagai77:20180729102135p:plain

9スライススケールは、一つのイメージを9つの部分に分解してそれぞれのサイズに比例してサイズを調節する事で2Dのイメージをリサイズするテクニックです。

とありました。どうやらアドビ―で開発されたテクニックみたいですね。この9つに分割されたイメージの中で角にあたる4つの部分のイメージの最低のピクセル数を14にしたいと28.では言っているみたいですね。やっと意味が分かりました。

以下にもう一度、SetNormal()関数を示しました。

f:id:kazuhironagai77:20180729102233p:plain

このFMargin内の14.0fが角のサイズの最低ピクセルを指定しているみたいですね。ただし、このマクロであるBOX_BRRUSHに使用されている関数であるFSlateBoxBrushのオーバーロードされたパラメーターのどのタイプともこのパターンは当てはまらないみたいです。なので、この方法は次のversionのUE4では動かない可能性もあるみたいですね。ここではこの問題については深入りしないで次にいきます。

f:id:kazuhironagai77:20180729102323p:plain

はい。と言いましたが、SlateBoxBrush.hに9スライススケールのビジュアル的な説明があるとは思えません。APIの事を言っているのかなと思って、SlateBoxBrushのページを見ましたが、全くそれらしいものはありませんでした。一応、SlateBoxBrush.hを開いて見たら、以下に示すようにSlateBoxBrush.hに9スライススケールのビジュアル的な説明がありました。スゴイ!!

f:id:kazuhironagai77:20180729102353p:plain

f:id:kazuhironagai77:20180729102410p:plain

29.はFTextBlockStyle についての解説です。

f:id:kazuhironagai77:20180729102447p:plain

29.によればこの関数は、スタイルの全体をデフォルトのままで、一つのプロパティ―のみを変える事が出来るそうです。まず、FTextBlockStyleのAPI を見てみましょう。

f:id:kazuhironagai77:20180729102525p:plain

f:id:kazuhironagai77:20180729102540p:plain

ウーン。APIにはあまり役立つ情報はなかったです。兎に角、サンプルコードのように書けば、スタイルの全体をデフォルトのままで、一つのプロパティ―のみを変える事が出来るのは証明されているので、これはこのまま覚える事にしましょう。

f:id:kazuhironagai77:20180729102805p:plain

おお、本当にHow it works…の解説は素晴らしいです。自分で調べて分からない所が、スパッと説明されています。

f:id:kazuhironagai77:20180729103011p:plain

この部分のコードはデファルトのクローンをコピーコンストラクターを使用して作成していたのですね。コピーコンストラクターなんて、すっかり忘れていました。

f:id:kazuhironagai77:20180729103042p:plain

以下のコードについての解説です。

f:id:kazuhironagai77:20180729103113p:plain

特に解説は要らないと思います。リニアカラーとは、RGBAの事を指すのでしょうか。いつも訳す時にへんな日本語になってしますので、そこだけ調べて見ましょう。FLinearColorのAPIを見てみました。

f:id:kazuhironagai77:20180729103205p:plain

線形の、32bitsのフロート型によるRGBAカラー

とありました。その通りでしたね。後、リニアは線形と訳すと変な日本語にならなくていいみたいです。

f:id:kazuhironagai77:20180729103247p:plain

それって何なんだと、訳だけみると分け分からなくなりますが、コードをみれば一目瞭然です。

f:id:kazuhironagai77:20180729103316p:plain

はい。Initialize関数に返しているそれとは、StyleRefです。まあ、32.はこのCreate関数の総括的な解説ですね。

f:id:kazuhironagai77:20180729103341p:plain

今度は、Initialize関数についての解説です。以下にInitialize関数のコードを示します。

f:id:kazuhironagai77:20180729103443p:plain

コードの意味する処と、33.の解説に特別な矛盾はないです。CookbookStyleInstanceは、

f:id:kazuhironagai77:20180729103527p:plain

f:id:kazuhironagai77:20180729103540p:plain

で宣言初期化されていますので、StyleRef と同じTSharedPtr< FSlateStyleSet >タイプになるので、特に問題もないですし。

次のFSlateStyleRegistryクラスとそのメンバー関数であるRegisterSlateStyleについてAPIを調べて見ました。

f:id:kazuhironagai77:20180729103628p:plain

セントラルレポジトリ

Slateスタイルのデータの塊の追跡や管理に使用される

あれ、何か前に訳したような。と思ったらすでに6.で訳していましたね。

f:id:kazuhironagai77:20180729103709p:plain

レポジトリにslateのスタイルを追加

スタテックな関数

この二つのRemarkから明らかなようにEU4のエディターにはslateのスタイルなどを管理する巨大なレポジトリが存在しています。そこに、Createで作成したSlateのスタイルを登録すれば、いつでもその登録したSlateのスタイルが使用出来ると言う事みたいですね。

f:id:kazuhironagai77:20180729103819p:plain

以下に示したコードの解説ですね。

f:id:kazuhironagai77:20180729103840p:plain

「Get関数は実際のSlate内で使用されるStyleSetを回収するために使われます。」とありますが、どのように使用されるのかまだ不明です。それ以外は普通のget関数と考えていいと思います。

f:id:kazuhironagai77:20180729103929p:plain

以下にモジュールスタートアップを含むゲームモジュールのコードを示します。

f:id:kazuhironagai77:20180729104023p:plain

35.で述べられているように、StartupModuleでInitialize関数が呼ばれています。ので35.の解説のように既に、StyleSetのインスタンスは作成されていますので、Get関数はそれを単に返すだけです。

そう言えば、6.で「…モジュールのStartupのコード云々についてはここからは不明ですね。…」と言ってかなり見当違いの推測をしていましたが、6.の解説はこのクラスの事を述べています。今となっては明白ですね。

f:id:kazuhironagai77:20180729104139p:plain

36.は「ゲームモジュールでSlateのスタイルを作成し登録するInitalzie()関数とshutdown()関数を呼ぶので、いつでも我々は、我々がカスタム化したSlateのスタイルへのレファレンスが持てます。」と述べています。その通りであるし、この方法は大変便利だと思います。

f:id:kazuhironagai77:20180729104251p:plain

37.は以下に示すの部分の操作についての説明で、前のレシピで行った方法と全く同じなので、特に考察する必要はありません。

f:id:kazuhironagai77:20180729104319p:plain

f:id:kazuhironagai77:20180729104334p:plain

38.以降はAStyledHUGGameModeクラスのBeginPlay関数の実装に関する解説です。

f:id:kazuhironagai77:20180729104358p:plain

NormalButtonBrushやNormalButtonTextが普通に呼び出されています。とても不思議な気持ちです。

f:id:kazuhironagai77:20180729104448p:plain

f:id:kazuhironagai77:20180729104502p:plain

やはり、教科書でもそう思ったんでしょうか?NormalButtonBrushやNormalButtonTextについての解説が来ましたね。

f:id:kazuhironagai77:20180729104523p:plain

勿論そうです。

f:id:kazuhironagai77:20180729104543p:plain

これは、ちょっと何を指しているのか始めは分からなかったです。これは

f:id:kazuhironagai77:20180729104611p:plain

を解説してます。<Class>Style()メソッドは、SNew(SButton).ButtonStyle(FCookbookStyle::Get(), "NormalButtonBrush")とSNew(STextBlock).TextStyle(FCookbookStyle::Get(), "NormalButtonText")をそれぞれ指します。更にこの二つの関数はそれぞれ二つのパラメーターをバスします。

f:id:kazuhironagai77:20180729104638p:plain

35.でどのように使用されるか分からないと考察したFCookBookStyle クラスのGet関数がここで使われていました。

Get関数はTSharedPtr< FSlateStyleSet >タイプを返します。

f:id:kazuhironagai77:20180729104745p:plain

最初のパラメーターのタイプは、ISlateStyle となっています。ISlateStyleのAPIを見たら、

f:id:kazuhironagai77:20180729104807p:plain

となっていましたので、何も問題なく使用出来るはずです。

f:id:kazuhironagai77:20180729104829p:plain

43.はうまくまとまりませんでしたが、言いたい事はこのレシピの総括です。「我々が作成したカスタム化したスタイルを使用したウィジェットがプレイヤーがゲームを開始した時に表示されます。」と言い出いのだと思います。

<まとめ>

今回のレシピは、最初の予測とは逆に大変濃い内容でした。今までは、新しいデザインのイメージをUE4に追加するためには、UE4のエンジンのソースコードGithubからdownloadして自分でコンパイルする必要がありました。今回習った方法は、そんな面倒な事は必要ないです。今回のレシピにおけるカスタム化したデザインを持つボタンの作成とゲームプレイ中における表示には、3つのクラスが必要です。以下にその3つのクラスをまとめました。

f:id:kazuhironagai77:20180729104931p:plain

f:id:kazuhironagai77:20180729104938p:plain

f:id:kazuhironagai77:20180729105040p:plain

となります。その3つのクラスの関係は、

f:id:kazuhironagai77:20180729105205p:plain

となっています。FCookbookStyleで新しいデザインのウィジェットを作成し、それをFDefaultGameModelImplクラスのサブクラスで登録します。FDefaultGameModelImplクラスのサブクラスで登録したFCookbookStyleクラスで登録した新しいデザインのウィジェットはAStyleHUGGameMoideで使用許可をもらい実際のコードにAStyleHUGGameMOdeで実装されます。

<おまけ>

今週はなし。