UE4の勉強記録

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

12章2節 FMessageLog からメッセージをメッセージログに書く

<前文>

f:id:kazuhironagai77:20180902113251p:plain

FMessageLogはメッセージログとアウトプットログに同時にメッセージを発信する事が出来るオブジェクトだそうです。今回はこのオブジェクトの使用方法を勉強するみたいです。

<本文>

<目的>

12章2節「FMessageLog からメッセージをメッセージログに書く」を勉強します。これのどこが前回と違うのか今一、分からなかったので、ちょっと調べて見ました。このサイトによると

f:id:kazuhironagai77:20180930085231p:plain

最も大切なログのメッセージ、エラーや警告、は普通のメッセージログのウィンドウに記録されます。

とありました。前回はoutput logへの表示方法を勉強したので、今回、メッセージログへの表示方法を勉強するようです。

<方法>

Step.0

前回作成したプロジェクトをそのまま使用します。プロジェクト名はChapter12Part1です。

f:id:kazuhironagai77:20180930085402p:plain

教科書は色々言っていますが、UE4のエディター上でメッセージログを表示させればいいだけと解釈しました。

Step.1

f:id:kazuhironagai77:20180930085443p:plain

f:id:kazuhironagai77:20180930085509p:plain

色々書いていますが、取りあえずChapter12Part1.hファイルに上記のコードを追加します。

f:id:kazuhironagai77:20180930085543p:plain

取りあえず#define でマクロを作成した事は流石に私でも分かります。これからどこかのコードにLOCTEXT_NAMESPACEを入れるたびに、コンパイルする前にそのLOCTEXT_NAMESPACEは”Chapter12Namespace”に置き換わると言う事です。

気になる事は、PacketPublish社のサンプルコードの中に、log.hファイルがありそれが、どうもprojectname.hの代わりに入っているみたいなのですがそこには、

f:id:kazuhironagai77:20180930085650p:plain

“Chapter12”と書かれていました。この部分の名前は何でもいいんでしょうか?それともプロジェクト名と同じにしないといけないのでしょうか?

良く分からないですがこのまま行きます。

Step.2

f:id:kazuhironagai77:20180930085750p:plain

f:id:kazuhironagai77:20180930085801p:plain

以下のようになりました。

f:id:kazuhironagai77:20180930085819p:plain

FMessageLogはエラー表示になったので#include “MessageLog.h”を追加しました。2.を読むとextern FName LoggerName;は要らないようにも思えますが、一応入れておきます。

f:id:kazuhironagai77:20180930085900p:plain

f:id:kazuhironagai77:20180930085951p:plain

まず、#define FTEXT(x) LOCTEXT(x,x)ですが、FTEXT(x)はLOCTEXT(x,x)として扱うという意味と考えられますがLOCTEXT(x,x)がネットで検索しても出て来ません。LOCTEXT(x,x)もどこかで定義するのでしょうか?

次の行は2. で宣言したextern FName LoggerName;に”Chapter12Log”と定義しています。extern FName LoggerName; 余計かもと思いつつ残しておいて良かったです。(しかし考え直してみるとやっぱりいらない?)

そして関数CreateLog(FName name)の定義です。この関数の宣言はヘッダーファイルにはなかったです。後、最後の方でこの関数を使っているような個所があるのですがそこではCreateLogger()と成っています。PacketPublish社のサンプルコードのlog.cppファイルでは

f:id:kazuhironagai77:20180930090100p:plain

CreateLogger()となっていました。更にlog.hファイルでは、以下に示すように

f:id:kazuhironagai77:20180930090129p:plain

CreateLogger()関数の宣言がされていました。

多分CreateLogger()が正しいのでしょう。

他にも参考になる箇所がないかとサンプルコードをみていると、log.hに

f:id:kazuhironagai77:20180930090200p:plain

#define FTEXTS(x) LOCTEXT(x, x)がコメントされていて、その下に#define FTEXTS(x) FText::FromString(x)となっていました。LOCTEXT(x, x)は何処を探しても定義が見つからなかったのでひょっとすると廃棄されたマクロではと思っているのですがその可能性はあるかと。注意が必要な部分ですが取り敢えずはこのままでいきます。

CreateLog()もしくはCreateLogger()関数の定義を見ていきます。

FMessageLogModule& MessageLogModule = FModuleManager::LoadModuleChecked<FMessageLogModule>("MessageLog");

最初の行は教科書もサンプルコードも全く同じでした。FMessageLogModuleクラスのレファレンスオブジェクト、MessageLogModuleの宣言と定義です。まずFMessageLogModuleクラスやFModuleManagerクラスが何のクラスか全く分かりません。APIで調べてみました。

FMessageLogModuleクラス

f:id:kazuhironagai77:20180930090528p:plain

Remarkがありませんでした。これではこのクラスが何なのかよく分かりません。特に今の時点では。

FModuleManagerクラス

f:id:kazuhironagai77:20180930090600p:plain

モジュールマネジャーを実装する。

そのモジュールマネジャーはモジュールをロードしたりアンロードしたりするのに使用される。

またそのモジュールマネジャーは現在ロードされている全てのモジュールの記録を保つためにも使用される。

このシングルトンにはFModuleMagager::Get()を使用する事でアクセスできる。

こっちは沢山説明があり、非常に良く分かりました。

では本題であるFModuleManagerクラスのメンバー関数、LoadModuleChecked()を見てみると、二つありました。

f:id:kazuhironagai77:20180930090707p:plain

どっちか分からん。更に最初のやつにはオレンジ色のSマークがついています。こんなの気にした事ありませんでした。(スタテックの意味でした。)

f:id:kazuhironagai77:20180930090736p:plain

f:id:kazuhironagai77:20180930090746p:plain

両方のシンタックスを見てみると最初の関数はテンプレートを使用しています。もう一方の関数は使用していません。LoadModuleChecked<FMessageLogModule>ですから最初の関数が正解ですね。

最初の関数のRemarkには、

f:id:kazuhironagai77:20180930090804p:plain

特定のモジュールをロードします。

それが存在するかチェックします。

ロードされたモジュールかもしロード操作が失敗した場合はnullptr(返り値の事?)

となっていました。

更に、この行の最期の("MessageLog")の部分はInModuleNameですね。大体分かりました。

次の行は、FMessageLogInitializationOptions InitOptions;となっています。FMessageLogInitializationOptionsクラスは、

f:id:kazuhironagai77:20180930090938p:plain

メッセージログUIのセットのためのオプション

とありました。メッセージログにメッセージを表示するために書いているのですからここをTrueにすればいいのかと思ったら実際は更に複雑でした。

f:id:kazuhironagai77:20180930091057p:plain

f:id:kazuhironagai77:20180930091107p:plain

まず、上に示したように教科書とサンプルコードは同じでした。FMessageLogInitializationOptionsクラスの変数をまず見てみましょう。

f:id:kazuhironagai77:20180930091127p:plain

ブーリアンbAllowClear:このログに対してユーザーがclearを使用出来るかどうか

ブーリアンbDiscardDuplicates:2重のメッセージかどうかチェックしてそれを捨てるかどうか

ブーリアン:bshowFilters:フィルターメニューを見せるかどうか

ブーリアン:bShowLogWindow:メインのログウィンドウ内にこのログを見せるかどうか

ブーリアン:bShowpages:最初にページウィジェットを見せるかどうか

Uint32:MaxPageCount:このログが持てる最大のページ数。ページはファーストインラストアウトで管理

とありました。この変数からbShowpages、「最初にページウィジェットを見せるかどうか」とbshowFilters、「フィルターメニューを見せるかどうか」の設定をオンにしたと言う事のようです。

正直、ページウィジェットやフィルターメニューが何なのか分からない私はこれをオンにしてもピンと来ません。次に行きましょう。

f:id:kazuhironagai77:20180930091232p:plain

f:id:kazuhironagai77:20180930091241p:plain

遂にマクロの関数FTEXTを使用しているコードに出会ってしまいました。果たして、#define FTEXT(x) LOCTEXT(x,x)は実行されるのでしょうか?サンプルコードの#define FTEXTS(x) FText::FromString(x)が正しいのではないでしょうか?

後、今気が付いたのですが、サンプルコードの方はFTEXTでなくFTEXTSだったんですね。まあそれによって何かが変わるわけではないですが…。

CreateLog()もしくはCreateLogger()関数の最後の行、

f:id:kazuhironagai77:20180930091330p:plain

f:id:kazuhironagai77:20180930091341p:plain

を見てみましょう。

まずRegisterLogListing()メンバー関数についてです。APIによると、

f:id:kazuhironagai77:20180930091400p:plain

IModuleInterfaceのエンドインターフェイスは、メッセージログウィジェットにリスト化されたログを

登録します。このログはデファルトでグローバルメッセージログのウィンドウに表示されます。

しかしこれは初期の設定で無効にも出来ます。

AddMesseageなどを通してログをアウトプットする前にこの関数を

呼ぶ事は必要ではありません。

この呼び出しは単純にUIがログデータから見えるようにするために登録するだけです。

この関数がメッセージログにログを表示させる働きをすると解釈しました。次にこの関数のパラメーターを見てみます。

f:id:kazuhironagai77:20180930091453p:plain

LogName:登録するためのログの名前

LogLabel:ログのために表示するラベル

InitializationOptions:このメッセージログのための初期化のオプション

それぞれのパラメーターのタイプも分かっておきたいのでシンタックスも載せておきます。

f:id:kazuhironagai77:20180930091532p:plain

まず最初のパラメーターであるLogName(タイプはFName)ですが、サンプルコードと教科書が微妙に違っています。サンプルコードは、

f:id:kazuhironagai77:20180930091603p:plain

logName、この関数のパラメーターをパスしています。

教科書の方は、パスしていません。

f:id:kazuhironagai77:20180930091626p:plain

サンプルコードの場合は、この関数(CreateLooger()の事)を使用する個所で、LoggerNameをアギュメントとしてパスすればいいようにみえます。問題は教科書のコードです。まずCreateLog関数内にパラメーターであるnameを使用している箇所がありません。??です。

RegisterLogListing()メンバー関数のパラメーターに戻りましょう。二番目のパラメーターは、

LogLabel:ログのために表示するラベル

タイプは、FTextとなっています。

まず、教科書の方ですが、パスされているアギュメントはLogListingName. タイプはFTextです。ただしこの変数の初期化のために、

f:id:kazuhironagai77:20180930091734p:plain

FTEXTマクロが使用されています。

f:id:kazuhironagai77:20180930091754p:plain

このLOCTEXT(x、x)を指定しないで動くのかが大変不安な所です。

PacketPublish社の提供しているサンプルコードの方は、

f:id:kazuhironagai77:20180930091830p:plain

FTEXTSマクロが使用されていて、projectname.hファイルの方で、

f:id:kazuhironagai77:20180930091849p:plain

FTEXTSマクロの定義がしっかり指定されています。

RegisterLogListing()メンバー関数の最後のパラメーターは、

InitializationOptions:このメッセージログのための初期化のオプション

で、タイプは、FMessageLogInitializationOptionsです。教科書でもサンプルコードでもinitOptionsをアギュメントとしてパスしています。

ここで、CreateLog()もしくはCreateLogger()関数はお終いです。あれ、FMessageLogクラスのオブジェクトを返していませんね。サンプルコードの方はvoidになってますね。

この後には、gameModeクラスのコンストラクターにでも書いて下さいと以下のコードが紹介されていました。

f:id:kazuhironagai77:20180930091935p:plain

CreateLog()ではなくCreateLogger()になっていますね。

サンプルコードのgameModeクラスのコンストラクターにも同じコードがありました。

f:id:kazuhironagai77:20180930091950p:plain

では、実際にやってみましょう。

まずは、教科書通りに、#define FTEXT(x) LOCTEXT( x, x )を追加してみました。この関数が働かない可能性があるので、//#define FTEXTS(x) FText::FromString( x )も入れておきました。

f:id:kazuhironagai77:20180930092026p:plain

次にCreateLogもしくはCreateLogger関数ですが、名前はCreateLoggerにします。教科書ですらgameModeクラスのコンストラクターで使用する際にCreateLoggerと書いているからです。返り値はvoidにします。

最初の行を書いてみました。

f:id:kazuhironagai77:20180930092200p:plain

思いっきりエラーが出ています。FMessageLogModuleのAPIに以下のように出ているので、

f:id:kazuhironagai77:20180930092233p:plain

そのまま足しました。

f:id:kazuhironagai77:20180930092255p:plain

見事にエラーが消えました。

更に、ModuleがMessageLogになっているので、build.csファイルにMessageLogを足します。

Publicかprivateか分からないですが取り敢えずpublicの方に足しておきます。

念のために、サンプルコードのbuild.csを見てみると、

f:id:kazuhironagai77:20180930092316p:plain

f:id:kazuhironagai77:20180930092335p:plain

MessageLogモジュールはありませんね。何か嫌な感じです。ですがとりあえず、ビルドしてみましょう。

f:id:kazuhironagai77:20180930092353p:plain

ビルドは通りましたね。実行してみます。

f:id:kazuhironagai77:20180930092409p:plain

これも問題ないです。がCreateLogger関数を実際に呼んだ時に問題になるかもしれません。取りあえず様子見で行きます。

f:id:kazuhironagai77:20180930092430p:plain

FTEXTで取りあえずは行きます。ここまではエラーもなく問題なく進んでいます。

最終的には以下のようになりました。

f:id:kazuhironagai77:20180930092455p:plain

更に、LoggerNameを忘れていたので、追加しました。

f:id:kazuhironagai77:20180930092533p:plain

なるべく教科書の例に近づけましたが、この場合なら(FName name)は要らないですね。取ってしまいます。

f:id:kazuhironagai77:20180930092553p:plain

たったこれだけのコードなの?ちょっと驚きです。

残りのコードはゲームモードクラスにでも入れて置けと書かれているので専用のゲームモードクラスでやります。(勿論、私が作成したのはGameModeBaseクラスからの派生クラスです。)

f:id:kazuhironagai77:20180930092613p:plain

f:id:kazuhironagai77:20180930092624p:plain

こんな感じになりました。まずCreateLogger()を書いたら速攻でエラーを食らいました。サンプルコードに書かれているようにChapter12part1.hに

f:id:kazuhironagai77:20180930092643p:plain

を足しました。

更に、FTEXTを認識しないので、#define FTEXT(x) LOCTEXT( x, x )をもう一度足しました。

Tipには

f:id:kazuhironagai77:20180930092702p:plain

f:id:kazuhironagai77:20180930092709p:plain

と書かれていましたがこの事なんでしょうか?

<結果>

f:id:kazuhironagai77:20180930092759p:plain

普通に表示されました。

#define FTEXTS(x) FText::FromString( x )の方も試してみましたが、

f:id:kazuhironagai77:20180930092839p:plain

どちらでも出来ました。

Step.4

f:id:kazuhironagai77:20180930093007p:plain

f:id:kazuhironagai77:20180930093015p:plain

以下に示すようにコードを足しました。

f:id:kazuhironagai77:20180930093032p:plain

結果は、

f:id:kazuhironagai77:20180930093050p:plain

完璧ですね。

最期に、

f:id:kazuhironagai77:20180930093110p:plain

のMessageLogが本当に必要だったのか、取り除いて試してみます。

f:id:kazuhironagai77:20180930093126p:plain

プロジェクトをcleanしてからビルドすると、

f:id:kazuhironagai77:20180930093143p:plain

普通に出ていますね。何故でしょうか?

<考察>

12章のレシピの説明はアバウトだったので、考察しながら進めざるえず、今回はここに書くべき内容は全て<方法>と<結果>に書いてしまいました。

その中で2つだけ分からない事があります。

1. #define FTEXT(x) LOCTEXT( x, x )のLOCTEXT()はどこに定義されているの?

2. MessageLogモジュールをBuild.csに加えなくてもFMessageLogModuleが使えるのは何故?

この二つに関しては良く分かりません。

2.についてですが、ここで少し考察してみます。正しいかどうかは分かりません。

まず、FMessageLogModuleですがAPIには、

f:id:kazuhironagai77:20180930093244p:plain

モジュールはMessageLogと書かれていますがそこにはIModuleInterfaceが親クラスであるとも書かれており

f:id:kazuhironagai77:20180930093308p:plain

そのIModuleInterfaceクラスのモジュールはAPIによると

f:id:kazuhironagai77:20180930093324p:plain

Coreモジュールになっています。Coreモジュールは勿論、build.csで

f:id:kazuhironagai77:20180930093425p:plain

元々追加されていますから、ひょっとするとこのせいかもしれません。

<まとめ>

今回は、FMessageLog からメッセージをメッセージログに書く方法を勉強しました。この方法の最も重要なポイントは、FMessageLogModuleクラスのメンバー関数RegisterLogListing()を使う事です。このメンバー関数の目的は、APIによると以下のようになっています。

f:id:kazuhironagai77:20180930093537p:plain

これを一言で言ってしまえばメッセージログにログのリストを登録する事です。今回のレシピで使用したその他の総てのコードはこのメンバー関数を正しく呼ぶためのものです。例えば、CreateLogger()関数はその全てがRegisterLogListing()のアギュメントを正しく設定しそれをパスする事を行っているだけです。

f:id:kazuhironagai77:20180930093555p:plain

FMessageLog からメッセージをメッセージログに書く方法で大切な事は「RegisterLogListing()を使う。」これだけです。

今回のレシピにおいて、2つ良く分からない箇所がありました。一つは、#define FTEXT(x) LOCTEXT( x, x )のLOCTEXT()はどこに定義されているのか。もう一つは、MessageLogモジュールをBuild.csに加えなくてもFMessageLogModuleが使える理由です。

<おまけ>

最近、C++の基礎を非常に深く考察しながらも大変分かり易く解説したサイトを見つけてC++の復習にはまっています。作者の方はEAでゲームエンジンを作成されているそうです。最先端のC++の使い方を考慮しつつ基本の大切さを具体的な例を使って説明するので非常に納得出来ます。C++はほとんど独学(学校ではJavaで基礎を教えていたのでC/C++は研究室に入るまでほとんど使わなかった。)なので、C++でコードは書けますが、正しいコードが書けているのか、いつも不安に思っていましたが、このサイトで勉強してC++のコードを書くときのモヤモヤが完全に吹き飛びました。例えばMember Initialization listなんで自分で使用した事はなかったですがこれからは毎回使用する事にしました。