UE4の勉強記録

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

「Unreal Engine4.xを使用してRPGを作成する」3.2を勉強する

<前文>

f:id:kazuhironagai77:20190512203528p:plain

の3章2節キャラクターと敵の決定を勉強します。

f:id:kazuhironagai77:20190519220542p:plain

今回もパラパラ見た感じでは特に難しい所はない様です。やって行きましょう。

<本文>

<目的>

3章2節キャラクターと敵の決定を勉強します。

<方法と結果>

<Step.0>

当然ですが前回と同じプロジェクトを使用します。

確認のためにビルドを行ったところエラーになってしまいました。

f:id:kazuhironagai77:20190519220642p:plain

ウェ。何で?とエラーの表示を読むとRPGGameMode.hは最初にインクルードして下さいと書かれています。RPGGameMode.cppのインクルードの順番を以下に示すように直しました。

f:id:kazuhironagai77:20190519220713p:plain

そしてビルドをし直してみると

f:id:kazuhironagai77:20190519220746p:plain

今度は成功しました。

Playを実行してみると

f:id:kazuhironagai77:20190519220812p:plain

前回の終わりと同じようにプレーヤーポーンを操作出来ます。

うーん。何故前回の終わりに今回のエラーは表示されなかったのでしょうか?分からないですがそれではやって行きましょう。

<Step.1>

FCharacterClassInfoクラスを作成します。

教科書にはUObjectを親にして作成しますと書かれていますが本当なのでしょうか?サンプルコードを見て確認してみます。

f:id:kazuhironagai77:20190519220918p:plain

FTableRowBase構造体から作られています。思いっきり違うじゃねーか!確認のためにもう一度教科書を見てみるとUObjectから作成して以下のコードを追加して下さいと書かれていました。

f:id:kazuhironagai77:20190519221008p:plain

うーん。一番最初にこのデータテーブルのためのクラスを作成した時はどうしたんでしょうか。

教科書ではアクタークラスから作成してくださいと書かれていました。私のブログも確認して見ましたが特別にTableRowBase構造体から作成したとは書かれてはいませんでした。

f:id:kazuhironagai77:20190519221052p:plain

C++ウィザードをみてもTableRowBaseはありませんでした。うーん。UObjectクラスから作成しますか。

更に教科書ではSource/RPG/Dataフォルダー内に作成するようにと書かれていました。RPGは教科書のプロジェクト名なので、私の場合はSource/Ch2/Dataに作成すればいいのかと見てみると、

f:id:kazuhironagai77:20190519221124p:plain

そんなフォルダーはなかった。更に私のフォルダーはPrivateとPublicに分かれているのにサンプルコードの方はPrivateとPublicのフォルダーがありませんでした。

f:id:kazuhironagai77:20190519221159p:plain

またまた更にサンプルコードはVSではSource/RPG/Dataフォルダーと表示されていますが、UE4エディター上ではContent/Blueprints/Dataと表示されています。

f:id:kazuhironagai77:20190519221231p:plain

訳が分かりませんが、所詮はフォルダーなので適当に対応する事にします。

UE4エディター上でContentフォルダー内にDataフォルダーを作成しました。

f:id:kazuhironagai77:20190519221258p:plain

VSでは作成しませんでした。4.22では必ずPrivateかPublicを選択しなければならないみたいなのでDataフォルダーを作成すると更にその中にPrivateフォルダーとPublicフォルダーが生成されて返って複雑さを増してしますと思ったからです。

作成しました。以下にヘッダー部分を示します。

f:id:kazuhironagai77:20190519221339p:plain

これを直していきます。

f:id:kazuhironagai77:20190519221407p:plain

f:id:kazuhironagai77:20190519221415p:plain

まず、ここだけ変えました。

ビルドすると

f:id:kazuhironagai77:20190519221439p:plain

エラーに成ってしまいました。前に作成したTestCustomData構造体のヘッダーファイルを見てみると、GENERATED_BODY()がGENERATED_USTRUCT_BODY()に成っていました。更に構造体なのにclassに成っていました。

f:id:kazuhironagai77:20190519221506p:plain

もう一度ビルドしてみます。今度は成功しました。

f:id:kazuhironagai77:20190519221530p:plain

やった!と思ったらUE4エディターがクラッシュしてしまいました。すぐに再起動させると問題なく起動しましたが何か嫌な感じです。

一応UE4エディターからVSをリフレッシュはしました。

f:id:kazuhironagai77:20190519221605p:plain

そのまま、サンプルコードからコピーして残りを作成しました。

ビルドすると、

f:id:kazuhironagai77:20190519221629p:plain

普通に成功しました。

その後、教科書ではTArrayについての解説が書かれていますが、TArrayについては考察で復習します。考察では更に宣言部分のUSTRUCT()とstrcutとGENERATED_USTRUCT_BODY()についても調べようと思います。

<Step.2>

データテーブルを作成します。教科書には「このデータテーブルを保持するためにDataと言う名のフォルダーをcontent内に作成してください。」とありました。これでstep.1で意味不明だったサンプルコードのフォルダー構成が理解出来ました。

教科書の手順通りにMiscellaneousからData Tableを選択します。

f:id:kazuhironagai77:20190519222149p:plain

Character Class Infoを選択します。

f:id:kazuhironagai77:20190519222211p:plain

名前をCharacterClassesに変更します。

f:id:kazuhironagai77:20190519222234p:plain

しました。

ダブルクリックしてDataTableを開くと以下の様にデータテーブルが出来ていました。

f:id:kazuhironagai77:20190519222257p:plain

教科書のデータを挿入すると以下の様になりました。

f:id:kazuhironagai77:20190519222319p:plain

<Step.3>

FCharacterInfo構造体を作成します。

やり方はFCharacterClassInfo構造体と一緒ですので結果だけ以下に示します。

f:id:kazuhironagai77:20190519222405p:plain

これでビルトすると成功したのですがまたUE4エディターがクラッシュしました。

UE4エディターを再起動して先に進みます。

サンプルコードから残りをコピーしました。

f:id:kazuhironagai77:20190519222436p:plain

ビルドすると普通に成功しました。

<Step.4>

データテーブル、CharactersをFCharacterInfo構造体から作成します。作成方法はstep.2の方法と全く同じなので結果だけ以下に示します。

f:id:kazuhironagai77:20190519222509p:plain

<Step.5>

FEnermyInfo構造体を作成します。作成方法はこれまでと全く同じなので結果だけ示します。

f:id:kazuhironagai77:20190519222547p:plain

出来ました。ビルドも成功しました。

<Step.6>

データテーブル、EnamiesをFEnermyInfo構造体から作成します。

これも作成方法はこれまでと全く同じなので結果だけ示します。

f:id:kazuhironagai77:20190519222620p:plain

<考察>

<Step.1>

TArrayについて

最近C++の総復習をしていて先週丁度Arrayを勉強した所です。なのでTArrayについて非常に興味があります。更にC++11のArrayとの比較も出来たらいいなと思っています。

TArrayのUE4APIを見てみましょう。

f:id:kazuhironagai77:20190519222747p:plain

Remarks

テンプレート化された動的な配列。

タイプ化された要素の配列でサイズを動的に変更できる。

その要素が移転出来ると仮定している:例)それらはユーザーに気付かれない且つコピーコンストラクターを使用しないで新しいメモリーに移動出来きる。

これはTArray内の要素へのポインターは配列内の他の要素の追加や削除によって無効化されている事を主に暗示している。

要素の削除はO(N)でありその後の要素のインデックスを無効化する。

注意:以下に記したように幾つかのメソッドはコンストラクターを必要とするタイプの要素に対して安全ではない。

とありました。まず、動的にサイズを変える事が出来る配列であると述べています。C++の生の配列は初期化する時に配列のサイズを指定しそれを途中で帰る事は出来ないのでこれだけでも便利です。

因みに厳密に言えばC++の生の配列はそのサイズを別な変数で保持しなければその配列のサイズすら不明になってしまいます。以下に示すようにTArrayには配列のサイズを返すメソッドがありそれも便利です。

f:id:kazuhironagai77:20190519222912p:plain

「その要素が移転出来ると仮定している」とはかなり難しい表現です。これは配列のメモリー上での保存場所を例えば要素を足したり削除したりした時に別に移す時にそれが可能であると仮定していると言っていると思われます。可能じゃない場合があるのでしょうか?次の文章が具体的な例を紹介していますがそれは「それらはユーザーに気付かれない且つコピーコンストラクターを使用しないで新しいメモリーに移動出来きる。」と指定しています。ユーザーに気付かれない(transparently)は具体的に何を指しているのか不明ですがその次のコピーコンストラクターを使用しないで新しいメモリーに移動出来きるは何となく分かります。

しかしもっと分かり易い表現はないのかネット上を探していると見つかりました。TArray: Arrays in Unreal Engine

f:id:kazuhironagai77:20190519223009p:plain

TArrayは値を直接保存するタイプです。それの意味はTArrayint32floatなどのビルトインタイプと同じように処理すべきと言う事です。

継承したりTArrayをヒープ領域で作成したり削除するようにはデザインされていません。

つまりTArrayは要素を追加したり消去したりで動的にサイズを変えた場合、配列内のすべての要素をメモリー上の別の場所に移動するかもしれない。その移動した場合は値渡ししかしないよ。と言っていたんです。だから配列内の要素をメモリー上の別な場所に移動する時にコピーコンストラクターが必要な複雑な場合(つまりTArrayをヒープ領域で作成するなどの場合)は移動出来ないよ。と言いたかったと思われます。

Remarkの次の文章「これはTArray内の要素へのポインターは配列内の他の要素の追加や削除によって無効化されている事を主に暗示している。」はTArrayをヒープ領域で作成したり削除したりするなよと言っている事と同義だと思われます。大分分かって来ました。

「要素の削除はO(N)でありその後の要素のインデックスを無効化する。」配列から一つの要素を見つけてそれを消す場合どんなアルゴリズムがあってそれのBigOが何であるかなんてもうすっかり忘れてしまいました。今更勉強する気も起きません。O(N)だったら別に気にする必要もないと思います。インデックスが要素を削除した後で変わるのも当たり前と言えば当たり前です。

ここまで勉強して思ったんですがTArrayを真に理解するためのC++の勉強が足りてないです。私は自分で独自のArrayをC++で作った事がないんです。だからArrayを作成した場合にどこに問題があってそれの解決にはどうすればいいかというような事が全く分からないです。それでTArrayの解説からの考察も底が浅くなってしまっています。ので今回はここで一端停止します。C++11のArrayも比較する予定だったのですがやっぱり今回は止めておきます。

USTRUCT()StructGENERATED_USTRUCT_BODY()について

USTRUCT()についての解説をネット上で探していたら、UE4C++の教科書であるUnreal Engine 4 Scripting with C++ CookbookにUStructについてのレシピがあると紹介されていました。古巣に帰る気分でもう一度Unreal Engine 4 Scripting with C++ CookbookのUStructのレシピを読み直してみると、普通のstructに比較してUStructの最大の利点はUE4からの協力が得られるだけとありました。UStructの作成のためのレシピではUSTRUCT()、StructとGENERATED_USTRUCT_BODY()が今回のサンプルコードと同じように使用されていました。ついでに一般のC++において構造体とクラスの違いについての正しい解説も載っていて(私がこれをはっきりと知ったのは最近です。)Unreal Engine 4 Scripting with C++ Cookbookの著者のC++に対する造詣の深さを垣間見た気がしました。

<Step.2>

ゲームにおけるソフトウェアデザイン観点からのキャラクターのパラメーターをデータテーブルで管理する事について。

これを論ずるためにはGame Programming Patternsで述べられている最適なキャラクターのパラメーターの作り方と比較が必要なのです。しかしこの本、読まなくてはと思いつつ全く進んでいません。今パラパラとめくったのですが、何処に私がほしい情報が載っているのか分かりませんでした。ので今回はこの部分の考察は延期して空いた時間はGame Programming Patternsを読む事にします。

Step.3 から6はStep.1 と2の繰り返しなので考察は省略します。

<まとめと感想>

今回は、キャラクターと敵のパラメーターの種類と値を決定しました。RPGゲームデザインではもっとも基本的な部分と思われます。この節もただ教科書のサンプルをコピーするだけならば2時間もあればいいかもしれませんが、何故そんな風に設計されているのかを理解使用とすると結構大変と言う事が分かりました。