<前文>
で勉強しています。
今回は、「8章19節 カスタムディテールパネルによるタイプの検査」を勉強します。8章における最後の節です。ブログを書く前は、1章を一週間で終わらせていましたが、ブログにしてまとめ始めたら一つの節に対して一週間もしくはそれ以上かかってしまいとんでもない事を初めてしまったと後悔しました。しかし、ブログにまとめる事で、今まで気が付かなかった間違いなどに気づく事も出来ました。
<本文>
<目的>
「8章19節 カスタムディテールパネルによるタイプの検査」を行います。といっても、一体それが何を意味するのが、全く分かりません。この節の最後まで、読んでみると、以下に示すように、
mycustomassetのグラフ(UEエディターのコンテントブラウザー内にあるmycustomassetをダブルクリックすると表示される。)を自分が望むようにカスタム化する事のようです。
<方法>
Step 0.
前回のプロジェクトをそのまま使用します。別名保存はしません。そのまま使用します。
Step 1.
前節で、既にMyCustomAssetDetailsCustomized.hを作成しているので、それを使用します。
Step 2.
以下に示すように前節で作成したMyCustomAssetDetailsCustomized.hが2.で示したすべてのインクルードを既に保持しているのでこのままでいきます。
Step 3.
以下に示すように、前節で作成したMyCustomAssetDetailsCustomized.hに、全く同じコードが既にかかれていますので、そのまま使用します。
Step 4.
以下に示すように、前節で作成したMyCustomAssetDetailsCustomized.cppに、ほぼ同じコードが書かれていますので、そのまま使用します。(DetailBuilder.GetDetailsView()->GetSelectedObjects();が違います。)
Step 5.
これって定義じゃなくて実装ですよね。前節で作成したMyCustomAssetDetailsCustomized.cppに既に実装されてました。
Step 6.
全部、以下に示すように、インクルードされていました。
Step 7.
PucketPublish社のサンプルコードに、まったく同じコードがあるので、それをコピーしました。
Step 8.
以下に示すように、PinFactory.Reset()の後に、付け加えました。
<結果>
Step 9.
以下に示すように、ビルドは一発で通りました。
Step 10.
以下に示すように、完璧に出来ました。
<考察>
いつもと同じように、How it works…を読んでいきます。
では、1.の解説の考察から行っていきます。1.の解説では以下の事が述べられています。
(イ) ディテールのカスタム化はIDetailCustomizationのインターフェイスを通じて行う。
(ロ) IDetailCustomizationのインターフェイスは、クラスを定義した時に継承できる。
(ハ) IDetailCustomizationのインターフェイスを継承したクラスは、別のアセットとなるクラスのディテールの表示のされ方のカスタム化を決定できる。
まず(イ)では、ディテールのカスタム化についての話です。ディテールとはアセットをダブルクリックした時に表示されるグラフの事だと思います。以下に示すように。セットをダブルクリックした時に表示されるグラフには、Detailsと表示されています。
(ロ)の解説におけるクラスとは、MyCustomAssetDetailsCustomizationの事です。以下に示すように、MyCustomAssetDetailsCustomization.hファイル内で、FMyCustomDetailsCustomizationクラスを作成する時に、IDetailCustomizationを継承しています。
このクラスは、mycustomassetクラスのアセットがUE4エディター上でダブルクリックされた場合、どのように表示されるかを指定するクラスのはずなので(ハ)の解説と一致しています。
2.の解説では以下の事が述べられています。
(イ) このプロセスを起こすためにIDetailCustomizationが使用する主な関数を以下に示します。
まず、このプロセスとは何でしょうか?1.の解説で説明した(ハ)の部分のクラスのディテールがどのように表示されるかを指定する事のはずです。そして表示されるディテールはmycustomassetクラスのはずです。なので、
(イ) mycustomassetクラスのディテールがどのように表示されるかを決定するためにIDetailCustomizationが使用する主な関数を以下に示します。
となり、その関数は、
となります。
どのようにこの関数を実装すれば、mycustomassetクラスのディテールが決定されるのかはこれからこのHow it works…内で解説されると考えられます。
3.の解説では以下の事が述べられています。
(イ) この関数の実装部で、パラメーターとしてパスされたDetailBuilderが持つメソッドを使用します。
(ロ) このメソッドは全ての選択されたオブジェクトの配列を獲得します。
(ハ) ループする事で全ての選択されたオブジェクトをスキャンします。
(ニ) 少なくとも選択されたオブジェクトの一つは正しいタイプであるという事を保証します。
まず最初にFMyCustomAssetDetailsCustomizationクラスの実装部を以下に示します。なぜなら3.はこの関数の実装に関しての解説だからです。
(イ)が解説するように、この関数はIDetailLayoutBuilderクラスのインスタンスであるDetailedBuilderをパラメーターとしてパスしています。この解説文では、このDetailedBuilderが持つメンバー関数をメソッドと言っていますが、c++なので、メソッドはおかしいです。関数もしくは、メンバー関数と言うべきでしょう。DetailedBuilderの使用される関数は以下に示すようにGetDetailsViewです。
このパスされたDetailBuilderは、配列として全ての選択されたオブジェクトをパスします。以下に示す部分のコードがそれを担当していると考えられます。
このコードは、前節でエラーを吐き続けたいわくつきの箇所です。ので少し丁寧にみていきましょう。
まず、IDetailLayoutBuilderクラスのAPIをみてみます。以下にremarkを示します。
ディテールのカスタム化のレイアウトのためのビルダーです。
更に、GetDetailsView関数のAPIも以下に示します。
Remark:このレイアウトビルダーのペアレントのディテールビュー
Returns: このレイアウトビルダーのペアレントのディテールビュー
このreturnは少し曖昧ですがIDetailsViewクラスを返すとGetDetailsView関数のAPIではっきりと示しています。ので、IDetailsViewクラスのGetSelectedObjectsのAPIを見てみます。
我々が検査するすべての選択されたオブジェクトのリスト
となり、APIの説明と(ロ)の解説がマッチしました。
この一連のAPIの説明文で考察しなければならない部分がもう一つありました。ディテール(detail)とディテールビュー(detail view)が完全に分けられている事です。今まで、ディテール(detail)とディテールビュー(detail view)は同じ事だと思っていましたが違うようです。ただ調べてたのですが良く分かりません。一応、以下のように解釈しました。
ディテールは以下に示す図で、detailと書かれている部分です。日本語では詳細と訳されていました。
ディテールビュー(detail view)は、以下に示す図の黄色の枠で囲った部分で、ディテールのボックスの事みたいです。
ただ、これを明快に説明した文章は見つかりませんでしたが、一応これが正しいと仮定して今後の考察を進めていきます。
(ハ)が解説する「ループする事で全ての選択されたオブジェクトをスキャンします。」の部分は、以下に示すForループの事を解説しています。
ちなみに、MyAssetはMyCustomAssetDetailsCustomization.hファイル内で以下のように定義されています。
更に(ニ)に「少なくとも選択されたオブジェクトの一つは正しいタイプであるという事を保証します。」と解説されていますが、正しいタイプがどうかをチェックできる箇所はこのコード内では、以下に示す部分のコードのIsValid()関数だけです。
UE4のAPIには、IsValid()は以下のように解説されています。
このポインターが生きたUObjectかどうかテストします。
これは、最適化されたバージョンです。bEvenIfPendingKill=false, bThreadsafe=falseを暗示しています。
もし、Get()が適切なヌルでないポインターを返したらTrueになります。
やはり、IsValid()関数が(ニ)に示されている「…保証する内容は少なくとも選択されたオブジェクトの一つは正しいタイプ…」をチェックしてるようです。ただし、コードを見る限り、少なくとも一つの部分が不明です、もし一つもIsvalid関数がTrueを返さない場合でも、MyAssetの配列の要素がゼロになるだけのような気がします。
4.の解説では以下の事が述べられています。
(イ) クラスのディテールのカスタマイズをDetailsBuilderオブジェクトのメソッドを呼ぶ事で行います
(ロ) EditCategory関数を使用する事でディテールのための新しいカテゴリーを作成します。
(イ)については、3.の(イ)で説明した事をもう一度説明しているだけです。ここでもメソッドと読んでいますね。
(ロ)の説明にあるカテゴリーとは何でしょうか?以下に示したイメージの赤いサークルで囲った部分がカテゴリーです。EditCategory関数は、このカテゴリーを作成する関数のようです。
まず、EditCategoryの実際のコードをみていきましょう。以下にEditCategory関数の実際のコードを示します。
(ロ)が述べるようにEditCategoryを使用して我々のディテールの新しいカテゴリーを作成しています。
UE4のAPIのEditCategoryもチェックしておきましょう。
元々あるカテゴリーを編集するか、新しいカテゴリーを作成します。
Remarkでも新しいカテゴリーを作成するとありました。
EditCategory関数についての更なる解説は、5.から始まるので4.の考察はここで終了します。
5.の解説では以下の事が述べられています。
(イ) EditCategory関数の最初のパラメーターは、我々が操作しようとしているカテゴリーの名前です。
UE4のAPIのEditCategoryでは、一番目のパラメーターは以下のように紹介されています。
CategoryName:カテゴリーの名前
5.の説明と同じです。以下に示すように、カテゴリー上の名前は、Custom Categoryとなっています。
ここで、以下に示すように、EditCategoryの”CustomCategory”を”CustomCategory2”に変えてみました。
その結果、
Detailの表示もCustom Category 2 に変化しました。この結果から推測するとカテゴリーの名前とはディテール内に表示されるCustomAssetの部分のようです。
6.の解説では以下の事が述べられています。
(イ) 2番目のパラメーターはカテゴリーのための潜在的なローカライズされた表示用の名前です。
UE4のAPIのEditCategoryでは、2番目のパラメーターは以下のように紹介されています。
NewLocalizedDisplayName: 新しいカテゴリーの名前を表示します(オプション)。
APIの説明では、不十分ですが、英語以外の言語でカテゴリー名を表現する場合に使用するパラメーターと考えられます。早速、日本語の名称を試してみたいのですが、サンプルコードでは、
FText::GetEmpty()となっていて実際に指定したい時の例が載っていません。この教科書全体に言える事ですが、他の言語の表示方法は全て省かれています。
7.の解説では以下の事が述べられています。
(イ) 3番目のパラメーターはカテゴリーの優先順位を示します。高い優先順位はリストの上位に表示されます。
リストに順位があるのでしょうか?ECategoryPriorityのUE4のAPIを調べて見ると、
などの優先順位リストがありました。
ImportantからUncommonに変えてみると、
Custom Categoryの順位が、Custom Assetの下になりました。ここから推測するとカテゴリーの優先順位とはそれぞれのカテゴリーがどのような順番でディテール内で表示されるかのようです。
8.の解説では以下の事が述べられています。
(イ) EditCategoryはリファレンスをカテゴリーそのものにカテゴリービルダーとして返します。
(ロ) カテゴリービルダーは我々にエディットカテゴリーを呼ぶ事で、追加のメソッドコールをチェーン化する事を許可します。
(イ)については、UE4のAPIからEditCategory関数を調べると
IdetailCategoryBuilderを返すとあります。それは(イ)で述べたカテゴリービルダーであります。
(ロ)についてはEditCategory内では、チェーン化出来るとだけ理解しておきます。
9.の解説では以下の事が述べられています。
(イ) 我々はCategoryBuilderのAddCustomRow()を呼びます。
(ロ) CategoryBuilderは、カテゴリー内に表示される新しいキーバリューペアを追加します。
以下に示すサンプルコードでは、(イ)で述べられた通りAddCustomRow()を読んでいます。GetEmpty()を追加しているので、
(ロ)に述べられているキーバリューペアについては何も追加していないと考えられます。
10.の解説では以下の事が述べられています。
(イ) Slateシンタックスを使用してシングルセンターかつ垂直なボックスのスロットを指定します。
10.は以下に示す部分のコードを解説していると思われます。
この部分を見れば(イ)の説明がそのままこの部分のコードの説明である事が分かります。
11.の解説では以下の事が述べられています。
(イ) そのスロットの内で、我々はカラーピッカーを作成します。
(ロ) OnColorCommittedデリゲートはイベンドハンドラー(ColorPicked関数)にバインドします。
以下に11.で解説する部分のコードを示しています。(イ)、(ロ)とも標準的なデリゲートの使用で特別な考察は必要ないと思います。
12.の解説では以下の事が述べられています。
(イ) ColourPickedを定義する事が必要である。
(ロ) ColourPickedを 実装する事が必要である。
(ハ)そのためのシグネチャは以下に示したものである。
最初に、実際のサンプルコードからの実装部を以下に示します。
(イ)、(ロ)については、デリゲートにパスする関数は、どこかに実際に作成する必要があると当たり前の事を述べており、それが今回はColorPicked関数となっているだけです。(ハ)についても、その具体的なコードの一部を紹介しただけで一般的なデリゲートの方法と何ら変わりありません。
13.の解説では以下の事が述べられています。
(イ) ColorPickedの実装内で選択したアセットの一つが正しいタイプであるかどうかをチェックします。
(ロ) もし少なくとも一つの選択したアセットが正しければ、MyAssetに妥当な値を入力します。
(イ)についてはif(MyAsset.IsValid())を示していると考えられます。そして(ロ)で述べているように、もしそれが望みのタイプのオブジェクトだった場合、MyAssetに妥当な値が入力されると考えられです。
14.の解説では以下の事が述べられています。
(イ) ColorNameのプロパティをユーザーが選択した色に一致する16進法のストリング値にセットします。
今回のケースでは、妥当な値とは、16進法で示したユーザーが選択した色の値です。以下に示したコードがこれを担当しています。
<まとめ>
MyCustomAssetDetailsCustomizationクラスが今回のレシピの全てです。
このクラスは前回全く紹介されないまま使用されていたのでまったく役割が分からなかったクラスですが、今回の解説で大変重要な役割を持つクラスである事が分かりました。このクラスはIDetailCustomizationを継承する事で、他のクラスから作られたアセットのディテール内の表示する内容を決定する事が出来ます。
ディテールとは、以下に示した図の事です。特に黄色で囲った部分を特にディテールビューと呼びます。このディテール内に表示されているCustom CategoryやCustomAssetをカテゴリーと呼びます。
ディテールの表示する内容を決定するための最も重要な2つの要素は、CustomizeDetails関数とCustomizeDetails関数にパラメーターとしてパスされるIDetailLayoutBuilderクラスのインスタンスであるDetailBuilderです。DetailBuilderはCustomizeDetails関数内で、EditCategory関数を使用する事で、このディテール内のカテゴリーに表示する要素の名前、機能、順番などを決定します。また、そのためにSlateシンタックスが使用出来るようになるので、カテゴリーの機能を決定するためにデリゲートの使用が可能になります。
<おまけ>
ディテールビューがディテールとどのように違うのか調べたのですが、きちんとしたレファレンスは見つかりませんでした。ただそれに関した文章を読んでいると以下に示した図の黄色で囲った部分を指していると思われます。