UE4の勉強記録

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

第9章 1節 キャンバスを利用して描く

<前文>

f:id:kazuhironagai77:20180527131504p:plain

で勉強しています。今回は、「9章1節 キャンバスを利用して描く」を勉強します。9章における最初の節です。

前の日曜日に、私のパソコンにある2つのHDDの内の一つが突然死んでしまいました。ブログの原稿は既に書き終わっていたのですがそのブログを保存したHDDが死んでしまったのでもちろん更新はできませんでした。

色々調べてみると、HDD内にデータは残っているのですがHDD内にどんなデータがどこにあるかを示したテーブルが壊れてしまったらしいのでした。

途方に暮れていたのですが、シャットダウンした時に、実際のPCの電源が10分ぐらい落ちないので、もう壊れてもいいやと覚悟を決めてPC本体の電源ボタンを長押ししてPCの電源を切って、もう一度起動したら、死んだHDD内のデータを開く事は出来ないのですが、エクスプローラからデータにアクセスは出来るようになりました。そこで、外付けHDDを繋げて死んでいるHDD内のデータを全て外付けHDDにコピーしました。

一日と一晩かかりましたが、そのコピーしたデータの方を開いて見たら、ブログの原稿も見る事が出来ました。それでこのブログも無事に復活しました。

<本文>

<目的>

キャンバスを利用して何かを描きます。キャンバスとはUE3で作られた簡単なHUDの実装だそうです。今はほとんど使われていないそうです。今回習うキャンバスに利用方法は歴史的な価値はありますが実用性はなさそうです。

<方法>

Step 0.

途中までやったのですがGameModeクラスを作成するところでGameModeBaseクラスを作ってしまいそれが原因でエラーを吐いていたので新しいプロジェクトを作成してやり直しました。バージョンは4.19.2を選択しました。プロジェクト名は、Chapter9_0528としました。

Step 1.

f:id:kazuhironagai77:20180605132543p:plain

f:id:kazuhironagai77:20180605132635p:plain

Sourceファイル内のChapter9_0528にBuild.csファイルはありました。

f:id:kazuhironagai77:20180605132753p:plain

Build.csを開いて見ると

f:id:kazuhironagai77:20180605132819p:plain

コメントになっていたので、アンコメントしました。

f:id:kazuhironagai77:20180605132847p:plain

Step 2.

f:id:kazuhironagai77:20180605132923p:plain

エディターからクラスウィザードを開きます。

f:id:kazuhironagai77:20180605132951p:plain

GameModeを選びます。昨日途中までやった時ここでGameModeBaseを選んでしまい間違えました。

f:id:kazuhironagai77:20180605133026p:plain

名前はCustomHUDGameModeとしました。Pathはそのままです。C++でつくるUnreal Engineアプリ開発を読んでいたら通常のクラスはPrivateにするとあったので、privateを選択しました。

f:id:kazuhironagai77:20180605133057p:plain

Step 3.

f:id:kazuhironagai77:20180605133157p:plain

f:id:kazuhironagai77:20180605133211p:plain

加えました。

f:id:kazuhironagai77:20180605133455p:plain

Step 4.

f:id:kazuhironagai77:20180605133539p:plain

f:id:kazuhironagai77:20180605133553p:plain

加えました。

f:id:kazuhironagai77:20180605133636p:plain

今度は、AGameModeの処にエラー表示はありません。ACustomHUDのエラー表示は、次に作成するクラスを加えると消えるはずなのでこのままでいきます。

Step 5.

f:id:kazuhironagai77:20180605134636p:plain

加えました。

f:id:kazuhironagai77:20180605134701p:plain

f:id:kazuhironagai77:20180605134718p:plain

エラーが出ました。

f:id:kazuhironagai77:20180605134744p:plain

エラーを消すためにCustomHUDGameMode .cppファイルにCustomHUD.hをインクルードしました。

f:id:kazuhironagai77:20180605134822p:plain

もう一度ビルドしてみます。

f:id:kazuhironagai77:20180605134846p:plain

今度は成功しました。

Step 6.

f:id:kazuhironagai77:20180605134928p:plain

f:id:kazuhironagai77:20180605134934p:plain

以下に示したように加えました。

f:id:kazuhironagai77:20180605135005p:plain

Step 7.

f:id:kazuhironagai77:20180605135039p:plain

f:id:kazuhironagai77:20180605135057p:plain

加えましたが

f:id:kazuhironagai77:20180605135131p:plain

思い切りエラーを吐いています。Canvas のUE4のAPIによるとCanvasのヘッダーはEngine.hなのでEngine.hをインクルードしてみます。

f:id:kazuhironagai77:20180605135159p:plain

綺麗にエラー表示が消えました。

<結果>

Step 8.

f:id:kazuhironagai77:20180605140010p:plain

ビルドしました。

f:id:kazuhironagai77:20180605140206p:plain

エディターも順調に起動しました。

f:id:kazuhironagai77:20180605140305p:plain

Step 9.

f:id:kazuhironagai77:20180605140336p:plain

f:id:kazuhironagai77:20180605140351p:plain

しました。

Step 10.

f:id:kazuhironagai77:20180605140432p:plain

f:id:kazuhironagai77:20180605140446p:plain

選択しました。

f:id:kazuhironagai77:20180605140521p:plain

Step 11.

f:id:kazuhironagai77:20180605140616p:plain

f:id:kazuhironagai77:20180605141038p:plain

現れました。

f:id:kazuhironagai77:20180605141104p:plain

<考察>

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

f:id:kazuhironagai77:20180605141138p:plain

では、1.の解説の考察から行っていきます。1.の解説では以下の事が述べられています。

     (a) UIは描画のためにSlateモジュールを使用します。
     (b) 我々のモジュールとSlateモジュールの間に依存関係を加える必要があります。
     (c) そうすれば我々はそのモジュール内の宣言されたクラスにアクセス出来ます。

とあります。(a)は「Slateモジュールを使用してUIのための描画をします。」ということです。そのために、Build.csファイル内でSlateモジュールが使用出来るようにアンコメントしました。

f:id:kazuhironagai77:20180605141657p:plain

ここで(b)と(c)についてですが良く分かりません。Slateモジュールに関しては依存関係を指定しましたので、slateモジュール内のクラスがこのプロジェクト内から使用可能になりましたが、(b)と(c)は「我々のモジュール内のクラスとSlateモジュールの依存関係を指定すると我々のクラスに我々がアクセス出来るようになる。」と言っています。しかし我々のモジュールが何なのかが不明です。更にそのモジュールとSlateモジュールの依存関係を指定するとありますが、Build.csファイルで指定したのはSlateモジュールの依存関係だけです。かなり不明です。とりあえず無視して次にいきます。

f:id:kazuhironagai77:20180605141957p:plain

2.の解説の考察を行います。2.の解説では以下の事が述べられています。

     (a) AHUDのサブクラスからcanvasを呼び出します。

AHUDのサブクラスであるCustomHUDクラスのメソッドにDrawHUD()があります。そしてその実装部を以下に示します。

f:id:kazuhironagai77:20180605142202p:plain

確かにキャンパスを呼び出しています。

C++でつくるUnreal Engineアプリ開発を読んでいたら、P.44にUE4C++で作成するクラス内のメンバー関数はメソッドと呼ぶと書かれていました。それでこの教科書でも関数と読んだりメソッドと読んだりしていたのですね。勉強になりました。

f:id:kazuhironagai77:20180605142305p:plain

では、3.の解説の考察を行います。3.の解説では以下の事が述べられています。

     (a) 新しいGameModeを作成する事が必要です。
     (b) 我々のカスタム化したクラスのタイプの特定が必要です。
     (c) 我々のカスタム化したサブクラスを使用するとエンジンに告げるためには、(a),と(b)が必要です。

まず、「(a) 新しいGameModeを作成する事が必要です。」はCustomHUDGameModeクラスの作成の事です。CustomHUDGameModeクラスはGameModeクラスのサブクラスでその作成は新しいGameModeの作成に当たります。以下にCustomHUDGameMode.hの一部を示します。

f:id:kazuhironagai77:20180605142511p:plain

CustomHUDGameModeクラスの実装部を以下に示します。

f:id:kazuhironagai77:20180605143154p:plain

HUDClassがCustomHUDクラスであると特定しています。これが「(b) 我々のカスタム化したクラスのタイプの特定が必要です。」なのでしょうか?HUDClassは何なんでしょうか?HUDClassはCustomHUDGameModeクラスで宣言した変数ではありません。GameModeクラスが元々もっている変数かと思いGameModeクラスのUE4のAPIを見てみるとHUDClassという変数はありません。一つ上の継承ヒラレルキーであるGameModeBaseクラスのUE4のAPIを見てみると、HUDClassの変数がありました。そこでは、HUDClassは、

f:id:kazuhironagai77:20180605143438p:plain

このゲームで使用するHUDクラス

と説明されていました。ここはむしろ「(c) 我々のカスタム化したサブクラスを使用するとエンジンに告げる。」の部分でした。「(b) 我々のカスタム化したクラスのタイプの特定が必要です。」はそれならばStaticClass()メソッドしかないですね。StaticClass()のUE4のAPIを見てみると

f:id:kazuhironagai77:20180605143540p:plain

実行中のこのクラスを表現するUClassのオブジェクトを返す

とあります。オブジェクトのタイプを特定してはいませんでした。となるとHUDClassがCustomHUDクラスであると特定し、かつ「エンジンに我々のカスタム化した(この)サブクラスを使用するとエンジンに告げる。」の(b)と(c)の部分の両方を担当していると考えられます。しかしタイプを特定するという言い回しにはHUDClassクラス以外のタイプも特定出来るというニュアンスが含まれていると考えられます。GameModeBaseクラスのUE4のAPIを見てみると以下に示すようにHUDClass以外のタイプも特定出来るみたいです。

f:id:kazuhironagai77:20180605143708p:plain

f:id:kazuhironagai77:20180605143730p:plain

では、4.の解説の考察を行います。4.の解説では以下の事が述べられています。

     (a) UClassをHUDClassの変数に任命する。

これだけです。ただしこの(a)についての解説は沢山しています。まずこの(a)の部分ですが3.で散々考察した以下に示す部分のコードについての解説です。

f:id:kazuhironagai77:20180605143825p:plain

4.では更に、この部分のコードは。

      (ア) このUClassはそれぞれのプレイヤーコントローラーが生成された時にそれぞれのプレイヤーコントローラーにパスされる。

      (イ) そしてそれぞれの(プレイヤー)コントローラーが生成されたAHUDのインスタンスを担当する。

と解説しています。何の事か分かりませんね。マルチプレイの場合の例を述べているのでしょうか?にしても唐突です。このHUDは体力ゲージのようなものを表示するのでマルチプレイならばプレイヤー一人一人の値が違ってくるはずです。それが可能です。と言いたいのでしょうか。にしても話が唐突です。

f:id:kazuhironagai77:20180605144019p:plain

では、5.の解説の考察を行います。5.の解説では以下の事が述べられています。

     (a) 実際にカスタム化したHUDクラスの作成が必要です。

これだけです。CustomHUDクラスの作成が実際にカスタム化したHUDクラスの作成に当たります。

f:id:kazuhironagai77:20180605144102p:plain

では、6.の解説の考察を行います。6.の解説では以下の事が述べられています。

     (a) DrawHUD()というバーチャル関数をAHUDが定義している。
     (b) DrawHUD()はフレーム毎に要素をスクリーンに描く事が出来る。

まずDrawHUD()を関数と呼んでいるじゃないですか!UE4C++で作成するクラス内のメンバー関数はいつもメソッドといつも呼ぶ訳ではないようですね。大学の教授が言っていたセリフを思い出しました。メソッドとか関数の使い方は人それぞれで偉い先生ほど独自な使い方をする。あんまりこだわらないほうがいい。と。とりあえず、DrawHUDメンバー関数を持つAHUDのUE4のAPIをみてみましょう。

f:id:kazuhironagai77:20180605144153p:plain

HUDのためのメインの描画ループ

サブクラスされるべき

「HUDのサブクラスでDrawHUDメンバー関数をオーバーライドして描画する。」との解釈でいいみたいです。ところでこのAPIでもDrawHUD()を関数と呼んでいますね。

f:id:kazuhironagai77:20180605144251p:plain

7.で述べられている内容はすでに6.で考察されていますね。ここはスキップします。

f:id:kazuhironagai77:20180605144325p:plain

f:id:kazuhironagai77:20180605144341p:plain

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

f:id:kazuhironagai77:20180605144408p:plain

しかも今度はメソッドと書いています。DrawText()はメソッドなんでしょうか?DrawText()にカーソルを重ねてUE4API を表示させると以下の表示となりました。

f:id:kazuhironagai77:20180605144439p:plain

8.で表示されているメソッドと全く同じですね。UCanvasのUE4のAPIも見てみましょう。

f:id:kazuhironagai77:20180605144506p:plain

二つ全く同じパラメーターのDrawText()があってどっちか分からないので両方載せました。よく見ると二番目のパラメーターが最初のはFStringで次のはFTextになっていました。一番目のDrawText()を使用しているみたいです。

更に、DrawText()はUCanvasの関数の処で説明されていました。UCanvasのメソッドとは書いてませんでした。やはりこの教科書のメソッド、メンバー関数の使い分けは適当みたいですね。

f:id:kazuhironagai77:20180605144536p:plain

9.では結構いろいろな事が述べられています。ので9.の解説の考察を行います。9.の解説では以下の事が述べられています。

     (a)  DrawTextは書くためのフォントを必要とします。

     (b) Statとエンジンコード内の他のHUD描画用のコマンドに使用されているデフォルトのフォントは、実際はGEngineクラスに保存されています。

     (c) そしてGEngineクラスは、GetSmallFont関数を使用する事でアクセスされる事が可能です。

     (d) GetSmallFont関数はUFontのインスタンスポインターとして返します。

これらは全てDrawText()関数の最初のパラメータ―GEngien->GetSamllFont()についての解説です。

f:id:kazuhironagai77:20180605144621p:plain

「DrawText()メンバー関数はフォントの指定が必要なのでGEngineクラスに保存されているデフォルトのフォントを使用します。GEngineクラスのデフォルトのフォントへのアクセスはGetSmallFont関数で行えます。」と言う事ですね。これ以上無いくらい簡潔かつ丁寧にDrawItem()メンバー関数の最初のパラメーターについて解説されています。何も考察しなくても意味が完璧に理解出来ました。

f:id:kazuhironagai77:20180605144647p:plain

10.はDrawText()関数の2番目のパラメーターの解説のようですね。では10.の解説の考察を行います。10.の解説では以下の事が述べられています。

     (a) DrawText()メンバー関数の次のアギュメントは実際のテキストです。

     (b) そのテキストはそのテキストが持っているオフセット込みでピクセル化されて描かれます。

f:id:kazuhironagai77:20180605144755p:plain

アギュメントは実際のデータでパラメーターは変数名です。よってDrawText()メンバー関数の2番目のアギュメントはTEXT("Test string to be printed to screen")です。ので(a)で述べているように実際のテキストです。(b)の部分は10.をかなり意訳したと言うか意味を推測して書きました。多分(b)の解釈であっているでしょう。

f:id:kazuhironagai77:20180605144820p:plain

11.に関しては英訳が分からない部分がありました。Allows you to directly pass in the dataの部分ですが、in the dataなので「あなたがデータ内を直接横切る事(パスする事)を許可する」が正しい訳だと思います。しかし前後関係から考えると「データを直接パスする」になるはずなんですね。Allow you to directly  pass the dataなら「あなたがデータを直接パスする事を許可する」になるんですが…。

11.はスキップします。

f:id:kazuhironagai77:20180605144907p:plain

12.からはDrawItem()メンバー関数についての解説です。以下にDrawItem()メンバー関数とそのパラメーターであるProgressBarを以下に示しています。

f:id:kazuhironagai77:20180605144932p:plain

では12.の解説の考察を行います。12.の解説では以下の事が述べられています。

     (a) 一般的なDrawItem関数はVisitorの実装です。
     (b) Visitorの実装はあなたがある情報をカプセル化したオブジェクトを作成する事を許可します。
     (c) ある情報とは、そのオブジェクトは描画されるオブジェクトと、何回も描画呼び出しをするそのオブジェクトを再利用する事についてです。

(a)のVistorの実装とは何でしょうか?それを知るためには、DrawItem()メンバー関数のUE4のAPIを見てみましょう。

f:id:kazuhironagai77:20180605145036p:plain

与えられた座標にキャンバスアイテムを描画します

特にvisitorについての解説は載っていませんでした。更に調べましたがvisitorについての解説は見つかりませんでした。(b)と(c)もvisitorについての解説です。ウーン…。Visitorが分からない以上12.の解説の考察は一端中止にしますか。

f:id:kazuhironagai77:20180605145122p:plain

13.の解説の考察を行います。13.の解説では以下の事が述べられています。

     (a) まずプログレスバーを描画するのに使用される要素を作成します。

     (b) 我々はFCanvasBoxItem内でボックスの幅と高さに関する情報をセットします。

     (c) FCanvasBoxItemのインスタンスProgressBarをDrawItem関数にパスします。

(a)から(c)についての解説は以下に示したコードの説明です。

f:id:kazuhironagai77:20180605145224p:plain

FVector2Dが長方形の2つの対角な頂点を指しているはずです。大体の2D画面の座標は左上が(0,0)ですので、上記のボックスの場合、高さと幅は以下のようになると考えられます。

f:id:kazuhironagai77:20180605145250p:plain

では試してみましょう。ボックスの高さと幅を以下の様に変えます。

f:id:kazuhironagai77:20180605145318p:plain

非常に縦長なボックスになるのでしょうか?

f:id:kazuhironagai77:20180605145337p:plain

全然違う形になりました。この結果から推測するに、最初のVectorがボックスの左上の座標、次のvectorが幅と高さを決定してるみたいです。

f:id:kazuhironagai77:20180605145404p:plain

ので以下に示すように変えてみました。

f:id:kazuhironagai77:20180605145427p:plain

今度こそ、非常に縦長なボックスになってくれるのでしょうか?

f:id:kazuhironagai77:20180605145453p:plain

なりました。もう一回テストしてみます。

f:id:kazuhironagai77:20180605145526p:plain

f:id:kazuhironagai77:20180605145541p:plain

最初のVectorがボックスの左上の座標、次のvectorが幅と高さで間違いないですね。

f:id:kazuhironagai77:20180605145609p:plain

14.は以下のコードについて解説しています。

f:id:kazuhironagai77:20180605145639p:plain

14.の解説の考察を行います。14.の解説では以下の事が述べられています。

     (a) 我々が描画する三番目のアイテムは中の詰まった長方形です。

     (b) この関数はキャンバスそのものではなくHUDクラス内で定義される便利なメソッドを使用します。

     (c) その中の詰まった長方形は我々のFCanvasBoxItemと同じ場所に配置されます。

     (d) それで詰まった長方形はプログレスバー内側の現在の値を表現出来ます。

考察の必要のない完璧な解説ですね。しかし、テストだけは必要です。色と位置とサイズを変更してみましょう。以下の条件に変えています。

f:id:kazuhironagai77:20180605145743p:plain

f:id:kazuhironagai77:20180605145755p:plain

これも、最初のVectorがボックスの左上の座標、次のvectorが幅と高さで間違いないですね。

<まとめ>

今回のレシピは、キャンバスを使用して簡単な2Dイメージをゲーム画面上に表示する方法を習いました。以下の簡単なやり方を示します。

  • 2つの新しいクラス(一つはgame modeを継承し、もう一つは、AHUDクラスを継承)を作成します。
  • ゲームモードクラスを継承したクラスにAHUDクラスを継承したクラスをHUDクラスとして使用する事を告げます。
  • AHUDクラスを継承したクラス内にDrawHUD()メンバー関数をオーバーライドして、その中でキャンバスを使用する事で簡単な2Dのイメージを表示します。

この方法が、歴史的な価値以上があるのかどうか、現在も使用する必要があるのかは不明だが使い方は理解出来ました。

<おまけ>

GameModeのクラスを作成する際、GameModeBaseのクラスを間違えて作成してしまいエラーが消せず最初からやり直した。以下にその時の失敗した手順を残しておきます。

――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

<失敗例>

<方法>

Step 0.

新しいプロジェクトを作成しました。バージョンは4.19.2を選択しました。プロジェクト名は、Chapter9_0526としました。

Step 1.

f:id:kazuhironagai77:20180605145925p:plain

f:id:kazuhironagai77:20180605145938p:plain

Sourceフォルダー内にあるChapter9_0526フォルダーにあるChapter9_0526.Build.csを開きます。

f:id:kazuhironagai77:20180605150001p:plain

以下にBuild.csを示します。PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });がコメントアウトされているので、アンコメントします。

f:id:kazuhironagai77:20180605150024p:plain

以下に結果を示します。

f:id:kazuhironagai77:20180605150050p:plain

Step 2.

f:id:kazuhironagai77:20180605150135p:plain

エディタークラスウィザードって何でしたっけ?4章に書いてあるそうなので見てみましょう。…見つからなかったです。しかし、GoogleするとC++ Class Wizardに説明されていました。エディターにあるクラスウィザードを使用してと言う意味でした。

f:id:kazuhironagai77:20180605150211p:plain

以下に示すAddC++Classがクラスウィザードらしいです。

f:id:kazuhironagai77:20180605150243p:plain

f:id:kazuhironagai77:20180605150257p:plain

保存するフォルダーをどこにすべきかが分かりません。PuckePublish社のサンプルコードを先に見てみましょう。サンプルコードでは、CustomHUDGameModeはSourceフォルダー内のChapter9フォルダー内に保存されていました。よってPathはこのままでいきます。

f:id:kazuhironagai77:20180605150323p:plain

出来ました。

Step 3.

f:id:kazuhironagai77:20180605150413p:plain

f:id:kazuhironagai77:20180605150432p:plain

加えました。

f:id:kazuhironagai77:20180605150457p:plain

Step 4.

f:id:kazuhironagai77:20180605150524p:plain

f:id:kazuhironagai77:20180605150537p:plain

以下に示すようにcppファイルに加えましたが、エラーになりました。

f:id:kazuhironagai77:20180605150601p:plain

AGameModeはEngine.hをインクルードすればいいらしいので、

f:id:kazuhironagai77:20180605150624p:plain

としたら

f:id:kazuhironagai77:20180605150644p:plain

エラー表示は消えませんがAGameModeの色が青に変わったのでとりあえずこれでやって見ます。