UE4の勉強記録

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

8章14節 新しい編集用ウィンドウを作る

<前文>

f:id:kazuhironagai77:20180225113310p:plain

で勉強しています。

今回は、「8章14節 新しい編集用ウィンドウの作成」を勉強します。メニュー内に新しい編集用ウィンドウを作成する方法を勉強します。といっても、前節、前々節のレシピで、既に、編集用ウィンドウを表示していますので、全く新しい内容でもないみたいです。

***このブログは、このブログの読者が、教科書(Unreal Engine 4 Scripting with C++)を読んでいる前提で書かれています。この教科書を読まないで、このブログを読んでも理解不可能であると考えられます。ご了承ください。***

<本文1>

<プロジェクトの別名保存>

今回の勉強を始める前に、最初に、プロジェクトの別名保存方法について説明します。我流ですので、必ず出来るとは保証できません。今回のレシピのように、前回のレシピを再利用して何かを新しく作成する場合、前回のプロジェクトは取っておきたいものです。ですが、githubにあげてversion管理するほどのものでもないです。そんな場合、私は、別名で保存しておきます。

1.前回のレシピを作成したプロジェクトが入っているフォルダーを右クリックして、送るー>圧縮(zip形式)フォルダーを選択し、圧縮ファイルを作成します。

f:id:kazuhironagai77:20180401125826p:plain

2.新しいフォルダーを作成して、そこにzipファイルを移動します。そこでファイルを解凍します。(新しいフォルダーのファイル名は、なんでもいいですが、日本語は駄目です。)

f:id:kazuhironagai77:20180401125858p:plain

3.解凍したフォルダーを開きます。

f:id:kazuhironagai77:20180401125922p:plain

4..upprojectの名前を変えます。今回、別名保存するプロジェクトの新しい名前は、Fukusei_0321とします。ので、CNEM_0201.uprojectをFukusei_0321.uprojectに変えます。

f:id:kazuhironagai77:20180401134818p:plain

5.Configフォルダーを開き、DefaultEngine.iniを開きます。[URL]の下に、GameName= Fukusei_0321(GameName= プロジェクトの別名)を加えます。

f:id:kazuhironagai77:20180401125948p:plain

6.一つ前のフォルダーに戻り、解凍したフォルダー名も変更しておきます。

f:id:kazuhironagai77:20180401130013p:plain

7.Fukusei_0321フォルダーをダブルクリックしてもう一度、Fukusei_0321フォルダー内に入ります。.vs、Binaries、Intermediate、Saved、CNEM_0201.sln、CNEM_0201.VC.dbを消去します。

f:id:kazuhironagai77:20180401135455p:plain

8..uprojectをダブルクリックします。すると以下に示すメッセージが表示されますので、はいをクリックします。

f:id:kazuhironagai77:20180401130042p:plain

9.以下のイメージが表示されます。

f:id:kazuhironagai77:20180401130106p:plain

10.しばらくすると、UE4エディターが起動します。

f:id:kazuhironagai77:20180401130131p:plain

11.UE4が正常に起動しているのを確認したら、一端閉じます。

12..uprojectを右クリックしてGenerate Visual Studio project filesを選択します。

f:id:kazuhironagai77:20180401130200p:plain

13.しばらくすると、下記に記したように、新しいVSのソリューションが作成されます。

f:id:kazuhironagai77:20180401130225p:plain

14.Visual Studioのソリューションを開いて確認します。以下に示すように、Visual Studio内のプロジェクト名も、Fukusei_0321に変更されています。

f:id:kazuhironagai77:20180401130313p:plain

注意* ただし、Sourceフォルダーから下は、元の名前のままです。これ以下の名前を変更する場合は、全てのファイルを一つ一つ変更しなければならないので、大変な手間になってしまいます。全てのファイルを一つ一つ変更しなければならない場合は、素直にgithubでversion管理した方が楽だと思います。

15.最後に、visual studio側から正常に実行出来るか確認してみます。

f:id:kazuhironagai77:20180401131159p:plain

プロジェクトの別名保存が出来ました。

<本文2>

<目的>

メニュー内に新しい編集用ウィンドウを作成します。前節、前々節のレシピで、既に、編集用ウィンドウを表示していますので、全く新しい内容でもないみたいです。

<方法>

1.以下のコードを、FUE4CookbookEditorModuleクラスのMyButton_Clicked()関数内に足します。

f:id:kazuhironagai77:20180401131242p:plain

教科書には、上記のコードを全て足せ。と書かれていますが、実際には、前節のレシピで、そのほどんどのコードは書いてあるので、以下に示すコードを足すだけでよいです。

f:id:kazuhironagai77:20180401131306p:plain

2.コンパイルして、エディターを起動してください。
3.カスタムメニュー、もしくは、あなたが加えたツールバーオプションを選択するなどして、あなたが作ったコマンドを起動させてください。真ん中に何かのテキストが書かれてたウィンドウが表示されるのが見えるはずです。

<結果>

f:id:kazuhironagai77:20180401131400p:plain

普通に出来ました。

<考察>

今回、新たに増えた所は、実際にはありません。付足したコードも元々はあったのですが、前のレシピで抜いた個所です。なので、今回は、教科書のHow it works…を読んで復習するという形にします。

f:id:kazuhironagai77:20180401131424p:plain

1. 説明するまでもなく新しいエディターのウィンドウは、それ自身では(ウィンドウを)表示することは出来ません。それ故に、このレシピの最初から、新しいウィンドウを表示するためのトリガーとして使用出来る、カスタムメニューや、ツールバーボタン、もしくはカーソルコマンドを、実行しなければならない事を述べています。

2. すべてのslateウィジェットは、たいていTSharedRef<>フォームか、TSharedPtr<>フォームと交流(interact)します。

3. そのSNew()関数は、要求されたウィジェットのクラスのTSharedRefテンプレートを返します。

1の意味は分かります。ウィンドウを表示するためには、カスタムメニューを選択するとか、自作したツールボタンをクリックするといった行為か必要だと言う事です。当たり前という言葉はきらいですが、当たり前の話です。次の2から3までは、コードの最初の説明がされています。

f:id:kazuhironagai77:20180401131525p:plain

まず、2でSlateウィジェットは、TSharedRef<>(もしくは、TSharedPtr<>)を使って、宣言や初期化を行うのが普通である。と言っています。実際、サンプルコードも、SWindowクラスを、TSharedRef<>を使って、宣言、初期化しています。これは、そういうものだと覚えるしかありません。この形式で書く方法には、どんなメリットとデメリットがあり、この場合はこれが最適な選択であると、C++に真に詳しければ言えるのでしょうが、私はそんな深い話は、全然分かりませんので、そういうものだと覚えるしかありません。3では、Slateウィジェットを初期化するには、SNew関数を使用してくださいとあります。これも、2の検証と同じで、そういうものだと覚えるしかありません。

f:id:kazuhironagai77:20180401131549p:plain

4.この章のどこかで言ったように、Slateウィジェットは彼らが実行するたくさんの関数を持っています。それらすべての関数は、その関数が呼ばれるオブジェクトを返します。この事は、Method chaining(日本語訳不明)が、オブジェクトが作成される時間中に、設定(configure)するために使われることを許可します。

問題は、4です。正直何を言っているのか、全く分かりません。Method Chainingとは何なんでしょうか?とりあえず、次を読んでみます。

f:id:kazuhironagai77:20180401131656p:plain

5.これにより、Slateシンタックスが以下のようになる事を許可します。

Widget>.Property(Value).Property(Value)

5を読むことで、4も理解出来ました。つまり、いろいろなプログラム言語で、一般的なウィンドウの形状を決める方法である、<Widget>.Property(Value).Property(Value)という形式が、Slateウィジェットとも使えるよ。何故なら、4だから。と言っているのです。4で述べている個々の事象については、よく分かりませんが、全体の流れは、もう一度繰り返しますが、いろいろなプログラム言語で、一般的なウィンドウの形状を決める方法である、<Widget>.Property(Value).Property(Value)という形式が、Slateウィジェットとも使えるよ。何故なら、4だから。となっています。

f:id:kazuhironagai77:20180401131841p:plain

6.このレシピ中のウィジェットでセットされたプロパティは、ウィンドウタイトル、ウィンドウサイズ、そしてウィンドウが最大化、もしくは最小化出来るかどうか、である。

ここでは、以下に示す、2行目から5行目までのコードの説明をしています。4と5で述べたように、ウィンドウの形状は、プロバティーで決定します。

f:id:kazuhironagai77:20180401131953p:plain

説明文の通りに、ウィジット.プロパティ(バリュー).プロパティ(バリュー).プロパティ(バリュー)で、ウィンドウの形状を指定しています。

f:id:kazuhironagai77:20180401132023p:plain

7. 全てのウィジェットのリクエストが指定されたら、ブランケットオペレーター(bracket Operators)が、そのウィジェットの内側に設置されたコンテンツ(content)を特定するのに使われます。具体例を挙げるとすれば、写真とか、ボタンの内側のラベルとかです

f:id:kazuhironagai77:20180401132104p:plain

サンプルコードを見ても、ブランケット内で、コンテンツ、この場合はテキストとそのテキストを入れるスロットとその形状、を指定しています。

f:id:kazuhironagai77:20180401132130p:plain

8.チャイルドウィジェットのための一つのスロットが付いたSWindowはトップレベルのウィジェットです。ですので、我々が、それ(チャイルドウィジェット?)のために、スロットを加える必要はありません。我々が、ブランケットのペアの内側にそれを作成することで、スロット内にコンテントを設置します

ここでは、ブランケット内のコンテンツについて説明しています。Slateウィジェットにおけるスロットの意味が分からないので、最初の文から意味不明です。UE4のドキュメントによると、

f:id:kazuhironagai77:20180401132328p:plain

スロット:スロットは、ウィジェット同士を結合する見えない接着剤である。Slateでは、それらはもっと明白に、最初にスロットを作成し、そしてそのスロット内に設置するためのコントロールする何かを選択します。

とあります。ここで、スロットを説明通りにウィジェット同士を結合する接着剤と考えて、もう一度、8を訳してみると、

8.SWindowはトップレベルのウィジェットです。SWidgetには、childwidgetのためのスロットが一つ(最初から)付いています。だから、我々がSwidgetに、我々自身でslotを加える必要はありません。ブランケットの中に、コンテンツを作れば、そのコンテンツは、自然にスロット内に配置されます。

となって、すこし分かり易くなります。実際、ブランケット内を見てみると、

f:id:kazuhironagai77:20180401132445p:plain

最初の行に、Slot云々と書かれている箇所はなく、直接、SVerticalBoxが加えられています。しかしそのSVerticalBoxには、Slot()が加えられています。つまり、ブランケット内にウィジットを加える場合、最初にSlotを加えないといけないのですが、SWindowだけは例外となっていると考えられます。

f:id:kazuhironagai77:20180401132513p:plain

9. 我々が作成したコンテンツはSVerticalBoxである。SVerticalBoxは、垂直リスト内に表示するチャイルドウィジットのための任意の数のスロットを持つ事が出来るウィジットである。

ここでは、SVerticalBoxについて説明しています。サンプルコードで見た通り、SVerticalBoxはSWidgetと違いslotを加える必要がありますが、任意の数のslotを加えられるとあります。ではここで、すこしいたずらしてみます。

f:id:kazuhironagai77:20180401132555p:plain

SVerticalBoxにslotを沢山加えてみました。9で説明した通りならば、SVerticalBoxはslotを任意の数持てるはずなので、上記のようなコードも可能なはずです。ビルドして、実行すると、

f:id:kazuhironagai77:20180401132621p:plain

予測通りの結果が得られました。ので、次にいきます。

f:id:kazuhironagai77:20180401132655p:plain

10. 我々は、それぞれのウィジェットを垂直リスト内に設置したいので、スロットを作る必要がある。

あれ、教科書はまだ、SVerticalBoxのslotについて説明していました。SVerticalBoxがスロットが必要な事は、既に理解しています。

f:id:kazuhironagai77:20180401132745p:plain

11. 最も簡単に、これを行う方法は、オーバーロード+オペレーターとSVerticalBox::Slot()関数を使う事です。

f:id:kazuhironagai77:20180401132819p:plain

サンプルコードで使っている方法ですね。ここで、slotをSVerticalBoxに加えていると予測していましたが、この説明文からその事実が確認できました。

f:id:kazuhironagai77:20180401132850p:plain

12. Slot()は、他と同じように、ウィジットを返します。なので、我々は、プロパティを、我々がSwindowにしたように、それ(ウィジット)に、セット出来る。

f:id:kazuhironagai77:20180401140718p:plain

Swindowにしたように、<Widget>.Property(Value).Property(Value)が使えると12は説明しています。実際サンプルコードでも、使用しています。

f:id:kazuhironagai77:20180401132929p:plain

13. このレシピでは、HAlignとVAlignを使用して、水平軸からも垂直軸からも、スロットのコンテントを、中心にします。

https://docs.unrealengine.com/en-us/Programming/Slate/Widgetsによると、

f:id:kazuhironagai77:20180401133015p:plain

とあります。これを使用して、コードを少し以下のように変えてみると、

f:id:kazuhironagai77:20180401133041p:plain

以下に示したように変わります。

f:id:kazuhironagai77:20180401133110p:plain

HAlign_Leftと、HAlign_Fillが同じように表示されているのは、すこし不思議ですが、それ以外は、まあ予測の範囲です。VAlignはこの形式では、良く分かりません。SVerticalBoxがあるなら、SHorizontalBoxがあるかもと思って調べて見ると。UE4のAPIにありました。ので、以下のように、SVerticalBoxの代わりに、SHorizontalBoxを使用してみると、

f:id:kazuhironagai77:20180401133205p:plain

f:id:kazuhironagai77:20180401133223p:plain

以上の結果になりました。ここでも、VAlign_Fillと、VAlign_Topが同じように表示されています。すこし不思議に感じます。STextBoxに色付け出来ればもっとはっきりするかもしれませんが方法が分かりません。ここで、止めておきます。

f:id:kazuhironagai77:20180401133249p:plain

14. スロットは、一つのシングルチャイルドウィジット(single child widget)を持ちます。そしてそのシングルチャイルドウィジット(single child widget)は、SWindowと同じようにブランケットオペレーターの内側で作られます

f:id:kazuhironagai77:20180401133342p:plain

サンプルコードのここの部分の説明をしていると考えられます。ここで、一つのスロットは一つのシングルチャイルドウィジット(single child widget)しか持てないとあります。以下のようなコードは書けないと言う事でしょうか。

f:id:kazuhironagai77:20180401133434p:plain

実際書けないですね。

f:id:kazuhironagai77:20180401133500p:plain

15. スロット内に、我々は、一つのテキストブロックを作成します。そして、そのテキストブロックにテキストを加えます。

Someの意味が、よく分からなかったです。ですが、大意に問題はないはずです。

f:id:kazuhironagai77:20180401133607p:plain

16. 我々の新しいSWindowは、加えられたチャイルドウィジットを持っています。しかしそれは、まだ表示することは出来ない。なぜなら、加えられたチャイルドウィジットは、ウィンドウの階層には、まだ加えられていないからです。

え、そうなんですか?でも実際は、ウィンドが表示されているのではないですか?と思ったのですが、ここから、18までの説明文は、以下のコードの解説でした。

f:id:kazuhironagai77:20180401133651p:plain

f:id:kazuhironagai77:20180401133705p:plain

17. そのメインフレームのモジュールは、我々がトップレベルエディターウィンドウを持っているかどうか、チェックするために使われている。もしそれが存在していれば、我々の新しいウィンドウはチャイルドとして加えられる。

サンプルコードは、チャイルドウィジェットを持っているので、このチェックで、イエスとなるはずです。ブレイクポイントをセットして確認してみると、

f:id:kazuhironagai77:20180401133758p:plain

その通りでした。

f:id:kazuhironagai77:20180401133823p:plain

18. もし、チャイルドとして加えられるトップレベルのウィンドウがない場合、我々のペアレントのないウィンドウを加えるために、我々は、SlateのApplicationとしてのシングルトンを使う。

とあるので、確認のために、コードを以下のように変更して、チャイルドウィジェットを消して、デバックしてみると、

f:id:kazuhironagai77:20180401133912p:plain

f:id:kazuhironagai77:20180401133957p:plain

あれ?この場合でも、チャイルドウィジェットを持っていると判断されています。良く分からないです。

f:id:kazuhironagai77:20180401134022p:plain

19. もし、我々が作ったウィンドウの階層が見たいのなら、Window|Developer Tools |Widget Reflectorを通してアクセス出来るSlate Widget Reflectorを使えばよい。

やってみました。

f:id:kazuhironagai77:20180401134115p:plain

言われるがままに、Widget Reflectorを選択しました。

f:id:kazuhironagai77:20180401134146p:plain

20. もしあなたがPick Live Widgetを選択し、さらにカーソルを我々のカスタムしたウィンドウの上に重ねるなら、われわれのカスタムしたその階層に加えたウィジットとSWindowを見ることが出来ます。

これも、やってみます。

f:id:kazuhironagai77:20180401134300p:plain

f:id:kazuhironagai77:20180401134323p:plain

説明文の通りにすると、SWindowを見つけることが出来ましが、SWindowの中にあるWidgetがSVerticalBox以外もあるのが不思議です。

<まとめ>

今回は、編集用ウィンドウの作成方法を勉強しました。その中で以下の事をしました。

  • プロジェクトの別名保存
  • 編集用ウィンドウの作成方法のコードのレヴュー

プロジェクトの別名保存は我流です。もっと正しい方法があるかもしれません。編集用ウィンドウの作成方法のコードのレヴューは、実際は、前のレシピで勉強していたので、教科書のhow it works…を読みながらの復習となりました。ただし、復習といえども、知らない内容がたくさんあり、以下に示す内容を新たに学びました。

  • Slotについて:Slotは接着剤のようなもので、ウィジェット内に別のウィジェットを作成する場合必ず必要になる。Swindowだけは自前のslotを持っているので、こちらで作る必要はない。Slotの作り方は、オーバーロード+オペレーターで作成する。
  • ウィンドウの形状について:ウィンドウの形状は、<Widget>.Property(Value).Property(Value)で指定する。
  • ウィンドウの階層について:ウィジェットの階層は、Window->Developer Tools ->Widget Reflectorで確認できる。