UE4の勉強記録

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

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘システムを完成する6

f:id:kazuhironagai77:20201220215614p:plain

<前文>

ʌの音の位置が違う

以下にWikipediaのVowel diagramに載っていたIPAによる母音の発音表を示します。

f:id:kazuhironagai77:20201220215644p:plain

左側に書かれているClose とかOpenは口の開ける大きさを表しています。FrontとかBackとかの上側に書かれているのは舌の盛り上がっている位置を表しています。同じ位置に2つ発音があるのは、一つは唇の形がそのままで、もう一つは唇を丸める場合の発音です。

ʌの音をみると口はかなり開けて舌の奥を挙げて更に唇は丸めないで発音しています。

この表の何が大切かというと、イギリス人が世界中の言葉に使われている音を、一つの音に対して一つのalphabetで表そうとした結果がこの表だからです。つまり母音に限って言えば、口の開ける大きさと舌の形、そして唇を丸めるかどうかで、全ての言語の音を表せると当時のイギリスの言語学者は考えたんです。

ちなみに日本語のアイウエオを見てみると、この図で日本語のアの音は、äが最も近いと思われます。日本語でアと言うとき、日本語のエの時より口を大きく開けていますし、アと言う時に舌を触ってみると舌の真ん中に力が入っています。実際にこの図のäをクリックしてäのページに飛んでみると日本語のアの音が例として入っています。

ただ、この日本語の説明が完全に正しいのかは疑問で、日本語のウの音はɯに分類されていますが、実際にウと言うときはかなり唇を丸めています。日本語のウの音はUの方じゃないのかなと思います。ɯの音の例に空気が載っていますが、試してみれば分かりますが、空気の発音は実際はくーきって言っていてこの時のーの部分の発音では唇は丸めません。しかしくうきと発音した時は唇を丸めます。Vowel diagramの基準で考えれば、くーきのーの音とくうきのウの音は違う音です。

更に日本語のエの音に関して言えば、舌の先を下げて舌の前方を丸めている気がします。イのように単純に舌の前方を押し上げているのとは違うと思います。この舌の先を丸めると言う行為についてもこの表で表されているのと解釈出来るのか不明です。

と言う訳で、日本語に限って考えてみてもこの方法で完璧に全ての音を表せているとは思えませんが、イギリス人の言語学者が行っている以上、少なくとも英語に関しては100%上手く行っているはずです。

以下のもう一つのvowel diagramを見て下さい。

f:id:kazuhironagai77:20201220215705p:plain

このvowel diagramはhttps://rose-medical.com//vowel-sounds.htmlから引用させてもらった図ですが、アメリカ人が作成したvowel diagramなんです。ʌの音が見事にschwa (ə)の音に被っています。幾人かのアメリカ人の発音のコーチが主張するəの音とʌの音は同じでストレスがあるかどうかが違うだけを表しています。

これってどう解釈するのが正しいですかね。

個人的には、https://rose-medical.com//vowel-sounds.htmlのvowel diagramの方が、アメリカ英語における母音の発音を正確に表している気がしますが…。

それでは今週の勉強を始めます。

<本文>

先週、終わらなかった「11月22日のブログの「2. 今の戦闘システムを見直して問題点やバグを探しまとめます。」の「2.2 戦闘シーン毎の復習」で述べられている改善点。」の直しの続きをやります。

1. 戦闘システムの改善点の直しの続き

1.1 戦闘の終了

f:id:kazuhironagai77:20201220215748p:plain

レベルが上がった時、Userに分かるようにコメント欄に詳しい内容を報告します。

1.1.1 現状のチェック

現時点においてレベルが上昇した時に何をしているのかの確認をします。

RPGGameModeBaseクラス内のtick()関数内で戦闘に勝利した場合の中に以下に示した様に、レベルが上昇した時の処理が実装されていました。

f:id:kazuhironagai77:20201220215816p:plain

その中にReportCharacterLevelUp()関数と言うのがあり、BlueprintImplementableEventでspecifyされています。

f:id:kazuhironagai77:20201220215837p:plain

実装内容を見ると以下の様になっています。

f:id:kazuhironagai77:20201220215853p:plain

思い出してきました。これはUE4C++のUGameCharacterクラス内の変数は、武器や防具を装備した後のATKやDEFの値しか保持していないので、計算が面倒になるため、ThirdPersonCharacter内に武器や防具の無い状態でのATKとDEFの値を保持する変数を作成したんです。

ReportCharacterLevelUp()関数は、そのThirdPersonCharacter内の変数の値を変更するための関数ですね。

しかしこの関数のおかげでレベルアップした時のコメントは簡単に表示出来そうです。

1.1.2 レベルアップ時のコメント表示

ReportCharacterLevelUp()関数の最後に「レベルが上がりました。」のコメントを追加しました。

f:id:kazuhironagai77:20201220215915p:plain

これでまず試してみます。

f:id:kazuhironagai77:20201220215931p:plain

表示はされましたが、レベルが上がった事は勝利の報告の後ですべきです。

対症療法ですが、以下の様にしました。

RPGGameModeBaseクラスにBooleanタイプの変数、LevelUpを作成します。そしてReportCharacterLevelUp()関数の最後に以下の様にセットします。

f:id:kazuhironagai77:20201220215951p:plain

そして勝利しましたのコメントを表示するコードのすぐ後に、先程のレベルアップのコメントを以下の様に追加しました。

f:id:kazuhironagai77:20201220220009p:plain

結果、以下の様に表示されるようになりました。

f:id:kazuhironagai77:20201220220025p:plain

基本は出来ました。

f:id:kazuhironagai77:20201220220043p:plain

もっと細かい報告が必要ですが、現時点でコメント欄が一杯になっています。これ以上のコメントはいっぺんに表示する事が出来るように作成していないので、今回はここまでとします。

1.2 勝利のアニメーション

f:id:kazuhironagai77:20201220220103p:plain

1.2.1 勝利の時のカメラ位置の修正

直します。

確認したらカメラの位置はキャラの正面から撮影するように予めセットされていました。

f:id:kazuhironagai77:20201220220128p:plain

何で正面から撮影しないんでしょうか?

もう一度テストしてみます。

f:id:kazuhironagai77:20201220220145p:plain

勝利のポーズ時は、普通に正面から撮影していました。何回やっても前から撮影しています。

11月22日のブログを見ると、勝利のポーズ時に、後ろから撮影しています。

f:id:kazuhironagai77:20201220220201p:plain

何なんでしょうか?このバグは?

バグの再現が出来ないのでこのままで終わりにします。

1.2.2 モンスターの死亡アニメーションを追加しましょう

先週既に追加しました。

1.3 戦闘が終わってから元のマップに戻るまで

f:id:kazuhironagai77:20201220220235p:plain

これらは有ったら更に面白くなる要素なので、今回は無視して時間がある時に考えます。

1.4  元のマップに戻る

f:id:kazuhironagai77:20201220220308p:plain

1.4.1 元のマップに戻る

以下に示した様にしました。

まず元のマップに戻った時にモンスターが居たらもう一回戦闘になってしまいます。

戦闘が終了した時に、元のマップにいるモンスターは消えるようにします。

以下の方法で実装しました。

f:id:kazuhironagai77:20201220220333p:plain

f:id:kazuhironagai77:20201220220344p:plain

f:id:kazuhironagai77:20201220220352p:plain

f:id:kazuhironagai77:20201220220400p:plain

この方法では、全部のモンスターが消えてしまいますのであまり現実的な方法ではありません。

元のマップに戻って来た時に「10秒くらいモンスターから姿が見えない。」みたいな方が良いのかもしれません。

後で考えます。

元のマップに戻るのに必要な事はそのマップを開く事だけなので以下の様にしました。

f:id:kazuhironagai77:20201220220420p:plain

これで戻ります。

テストします。

f:id:kazuhironagai77:20201220220438p:plain

戻って来ました。

1.4.2 アイテムや装備品、金貨そしてキャラのパラメーターなどが、戦闘マップに行く前と変化していないか

確認します。

1.4.2.1 アイテム

戦闘前に所持しているアイテムです。この後でbattle fieldマップに移動します。

f:id:kazuhironagai77:20201220220510p:plain

戦闘後にbattle fieldマップからmap1に帰って来た後です。所有していたアイテムはそのまま残っています。

f:id:kazuhironagai77:20201220220535p:plain

念のために戦闘中に回復薬を使用した場合も試してみました。

戦闘後にbattle fieldマップからmap1に帰って来た後で、在庫表を確認すると、回復薬が消えています。

f:id:kazuhironagai77:20201220220556p:plain

アイテムの管理は大丈夫そうです。

1.4.2.2 装備品

今度は装備品の確認をします。

短剣(小)と木の盾(小)を装備品として所有しているが装備はしていない状態で戦闘してみます。

f:id:kazuhironagai77:20201220220621p:plain

f:id:kazuhironagai77:20201220220628p:plain

戦闘後にbattle fieldマップからmap1に帰って来た後で、装備表を確認すると戦闘開始前と全く同じ状態になっています。

f:id:kazuhironagai77:20201220220646p:plain

f:id:kazuhironagai77:20201220220653p:plain

今度は武器と防具を装備した状態でテストしました。

戦闘開始前と全く同じ状態で戻って来ました。

f:id:kazuhironagai77:20201220220718p:plain

正し一個バグが見つかりました。

武器と防具を装備した状態でモンスターの攻撃を受けると何故か、攻撃を受けた時のコメントではなく、アイテムを使用した時のコメントが表示されています。

f:id:kazuhironagai77:20201220220744p:plain

直します。

原因が分かりました。ReportCharacterHPisDamaged()関数の実装でダメージが0以下の場合は自動的に回復薬を使用したと判断されていました。

f:id:kazuhironagai77:20201220220802p:plain

Branchの部分をswitchに変更しました。

f:id:kazuhironagai77:20201220220821p:plain

テストします。

f:id:kazuhironagai77:20201220220839p:plain

出来てます。

道具を使用した時も元のコメントが表示されています。

f:id:kazuhironagai77:20201220220858p:plain

このコメント少しオカシイので後で直します。

1.4.2.3 金貨

薬草などを買って所持している金貨を55枚にしました。

f:id:kazuhironagai77:20201220220927p:plain

戦闘後にbattle fieldマップからmap1に帰って来た後で、所持している金貨の量を確認すると100枚に戻っていました。

f:id:kazuhironagai77:20201220220944p:plain

直します。

原因が分かりました。Map1のevent BeginPlayで金貨の所持量を100枚にセットしていました。

f:id:kazuhironagai77:20201220221004p:plain

直します。

f:id:kazuhironagai77:20201220221021p:plain

所持している金貨の量はRPGGameInstanceのconstructor内で指定しました。

f:id:kazuhironagai77:20201220221040p:plain

Map1のevent BeginPlayで金貨の所持量をセットしていたところは消しました。

テストします。

f:id:kazuhironagai77:20201220221057p:plain

10枚増えています。

f:id:kazuhironagai77:20201220221115p:plain

戦闘に勝った後、モンスターの所持している金貨を奪っていました。すっかり忘れていました。

EnemyMonsterのデータシートを見たらGoldの値が10にしっかりなっていました。

f:id:kazuhironagai77:20201220221133p:plain

戦闘に勝ったことで金貨を獲得した事を、コメント欄で報告しないといけないですが、これは後でやります。

1.4.2.4 キャラのパラメーター

ゴブリンを倒して得られるXPが50のままだとレベルが上がってしまい、キャラのパラメーターが保持されているのかどうか分からないので、ゴブリンを倒して得られるXPを20に変更します。

f:id:kazuhironagai77:20201220221159p:plain

これでテストします。

戦闘終了時、HP:70、MP:20でした。

Map1のevent BeginPlayでキャラのパラメーターを確認したところ、戦闘終了時と同じ値でした。

f:id:kazuhironagai77:20201220221217p:plain

1.4.2.5 戦闘画面との移動におけるアイテムや装備品、金貨そしてキャラのパラメーターの変化についてのまとめ

アイテムや装備品、金貨そしてキャラのパラメーターについて戦闘画面との移動によって値が変化しないか確認した。変化するモノには個別に修復した。

1.4.3 戻った先にいたモンスターは消滅しているか?

「1.4.1 元のマップに戻る」で戦闘後にモンスターは消滅するようにしたので、戻った先にいたモンスターは消滅している。

1.4.4 モンスターを倒して得た財宝を持っているか?

「1.4.2.3 金貨」で確認済み。

2. 先週のバグの直し

11月22日のブログの直しがとうとう終わったので、先週のバグの直しを始めます。

  1. 戦闘の攻撃時、自分を選択した場合のコメント欄のセリフの直し
  2. 戦闘画面の戻るボタンの作成

これしかありませんでした。直していきます。

2.1 戦闘の攻撃時、自分を選択した場合のコメント欄のセリフの直し

まず、セリフを見てみます。

攻撃を選択した後、KUROを選択します。

f:id:kazuhironagai77:20201220221543p:plain

このセリフは特にオカシクは無いです。次のセリフを見てみます。

f:id:kazuhironagai77:20201220221559p:plain

二行目がオカシイですね。KUMOはKUMOを攻撃した。が正しい文です。直します。

「“KUMO”がゴブリンを攻撃しました。」はRPGGameModeBaseクラスのOnClicked(AttackedButton)に実装されていました。

f:id:kazuhironagai77:20201220221616p:plain

以下に示した通り、攻撃対象の名前はCombatUIWidgetクラスのGetCharacterTargets()関数を使用して取得していました。

f:id:kazuhironagai77:20201220221715p:plain

今のゲームの内容ではGetCharacterTargets()関数そのものが不要ですがそれは一応残しておきます。「“KUMO”がゴブリンを攻撃しました。」のセリフの部分は外してAttackTargetButtonウィジェット内のボタンをクリックした時に発動するeventに移しました。

f:id:kazuhironagai77:20201220221734p:plain

テストします。

f:id:kazuhironagai77:20201220221750p:plain

直りました。

2.2  戦闘画面の戻るボタンの作成

これ、簡単に作れると思ったら、Combat EngineのTick()関数内をいじる必要がありました。ので中止します。

3. 戦闘システムの完成

一応、これで最低限の戦闘システムが完成しました。

今回、上記の教科書を元に作成した戦闘システムは以下の構成になっています。

f:id:kazuhironagai77:20201220221828p:plain

Combat engineを純正のC++で作成し、そのEngineとUE4C++で連絡し合って戦闘を進め、実際の3Dの映像はUE4C++とUE4BPで連絡しながらUE4BPが担当しました。

戦闘システムに関する感想を以下に示します。

3.1  純正のC++でcombat engineを作成する

敢えてC++UE4の外側にcombat engineを作成するメリットはなかったと思います。

このような作成方法で、純正のC++をUE4C++から利用出来る事は分かりました。それは有効な知識になりましたが、実際のゲーム作成において、UE4C++にある非常にゲーム作成に有効なAPIを使用しないで、最初から自分でengineを作成する意味はなかったです。

更にUE4BPと純正のC++の連絡は一端UE4C++を通してのみ可能と言う事実が、大変な足枷になりました。

3.2  UE4C++でUObjectからGameCharacterクラスを作成した事

これも、教科書で勉強している時は理由が良く分からないので、教科書の通りに作成しましたが、Characterクラスから作成した方が後々の管理が楽な気がします。ただデータの管理と言う点では、分けた方が少ないデータで済むのかもしれません。その場合でもGameCharacterクラスと言う名称はCharacterクラスとの混乱を生むので、EntityParameterみたいな別な名前を付けた方が良いと思います。

3.3  UE4C++を使用するデメリット

Compileに時間がかかり過ぎます。BPならば一瞬で終わってくれるような小さい変更でも5分とか待たされます。それ以外では特にデメリットは感じませんでした。

3.4  UE4BPについて

AIやアニメーション、そしてparticle systemなどのUE4BPにある沢山の機能の使い方が、全く知らない事を思い知らされました。この辺の勉強は個別に必要です。これから勉強します。

3.5  報告、連絡、相談について

ユーザーとしてモンスターとの戦闘を試した所、全く知りたい情報が得られていない事にびっくりしました。UIに報告、連絡、相談する機能をつける事は必須と感じました。そのためにどんな機能が必要とか、その機能はどうやって作成するのか等の情報は、この教科書には全く紹介されていません。

この辺はノウハウとして蓄積が可能な分野かもしれません。自分で研究してみます。

3.6 これからの課題

音楽を追加するのを忘れていました。来週追加します。

マップ上にモンスターを散りばめて、沢山戦闘して試してみます。

4. まとめと感想

今週はこれで終わりです。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘システムを完成する5

f:id:kazuhironagai77:20201213212231p:plain

<前文>

Juliaと言うプログラミング言語

Convolutionって複雑過ぎて困惑するって意味だったような気がするけど、数学の専門用語でもあったような気がします。うん。良く分からない時はgoogleで調べるに限る。と検索したらYouTubeにとても分かり易いConvolutionの解説がありました。そこで使用していたプログラミング言語がJuliaでした。

正直、全く聞いたことがなかったです。

f:id:kazuhironagai77:20201213212304p:plain

Convolutionの計算よりもJuliaの方に興味が移ってしまい、その後Juliaばかり調べてしまいました。そしたらかなり科学分野のためのプログラミングとしてはかなり良い言語というか、一番良い言語のような気がしてきました。現状では、データサイエンスの分野ではPythonより劣り、統計の分野ではRより劣り、更にlibraryにはバグが存在していると言う欠点があるそうですが、その辺を差し引いてもかなり魅力的な言語に見えます。

Juliaの何処が優れているのかを解説したサイトは結構あるので、それ以外に私が個人的に感じるJuliaを勉強する利点をここに挙げます。

まずこの言語を管理、推進している人達が超一流の科学者たちと言う事です。

現状ではコミュニティが小さいので、質問したら直接、超一流の科学者たちから直に回答が貰えそうです。そういう人たちから直接、科学分野のプログラミングを教わったら、1000倍くらい深く理解出来ますし、早く理解も出来ます。(勿論、正しい質問をする必要はあります。例えば、数値解析で、端っこの計算方法がどう変わるのか分からない、みたいな些細な問題であるけれども重要な問題でもある所も、超一流の科学者たちはどう計算するのが現状では正解と考えられているのかを理由も含めてバチッと教えてくれるはずです。)

次に将来的には「Juliaが出来る = 科学的なプログラミングの専門家」と言う位置付けになりそうだからです。

Python は一般的な言語でもあるので、Pythonが出来るからと言ってその人の科学分野のプログラミングがどこまで信頼出来るのかは全く不明です。Rが出来る人は統計分野では専門家でしょうが、他の科学のプログラミングにどこまで精通しているのかは分かりません。FORTRANはまさしく科学的な計算をするためのプログラミングですが、もう第一線で耐えれる言語ではないと思われます。 つまり今あるどのプログラミングに精通していても、その人が科学分野のComputingに精通している保証は無いんです。

逆もまたしかりで、理系の博士を持っている人が「Pythonが出来ます。」と言ってもどの程度プログラミングの知識があるのか不明です。ほんとに齧った程度の知識しかないかもしれません。Obama元大統領ですら知っていたBubble sortingすら知らないかもです。

Juliaならばこのギャップを埋める事が可能に思えます。「Juliaが出来る = 科学的なプログラミングの専門家」と言う立ち位置に将来的にですが、なりそうです。

それでは今週の勉強を始めます。

<本文>

. 今週の予定

今週も先週のバグを直して、その後11月22日のブログでまとめた、戦闘システムの改善点を直していきます。

1.1 先週のバグの直し

  1. NPCキャラに付けたPoint lightが昼間は消えるようにする。
  2. 武器を持った時の戦闘中のアニメーションが素手と同じ。
  3. AIの勉強を毎週少しだけやる。
  4. 戦闘時におけるモンスターの選択に幅を持たせるために

1.2 戦闘システムの改善点の直し

先週は、11月22日のブログの「2. 今の戦闘システムを見直して問題点やバグを探しまとめます。」の「2.1 戦闘シーンを復習する」で述べられた改善点を直しました。今週は「2.2 戦闘シーン毎の復習」で述べられている改善点を直します。

2. 先週のバグの直し

2.1 NPCキャラに付けたPoint lightが昼間は消えるようにする。

意外ですが、昼と夜の違いを知る方法が分からないです。

のでとりあえず、キャラと話せる状態になるまではpoint lightは消えていてもらって、キャラと話せる時だけ光るようにします。

f:id:kazuhironagai77:20201213212425p:plain

f:id:kazuhironagai77:20201213212433p:plain

最近知ったのですが、Targetには一つ以上のobjectを繋げられます。便利ですね。

テストします。

f:id:kazuhironagai77:20201213212449p:plain

NPCに近づくと光ります。

f:id:kazuhironagai77:20201213212515p:plain

離れるとNPCは光りません。

よくよく考えるとランプでも持っていないNPCが光るのはオカシイですし、こっちの方が正しい気がしてきました。これでOKとします。

2.2 武器を持った時の戦闘中のアニメーションが素手と同じ

はい。プレイヤーが操作するキャラは色々な武器を装備出来ますが、その装備した武器によって戦闘時のアニメーションが変わるのは当然です。現状では、弓を引く動作を素手の攻撃のモーションとして使用しています。全部直しましょう。

2.2.1 アニメーションのチェック

どんなアニメーションがあるのかをチェックします。

f:id:kazuhironagai77:20201213212550p:plain

弓での攻撃モーションです。

f:id:kazuhironagai77:20201213212606p:plain

2刀での攻撃モーションです。

f:id:kazuhironagai77:20201213212621p:plain

魔法杖での攻撃モーションです。

f:id:kazuhironagai77:20201213212636p:plain

素手での攻撃モーションはありませんでした。

f:id:kazuhironagai77:20201213212717p:plain

剣と盾の攻撃モーションです。

f:id:kazuhironagai77:20201213212737p:plain

最後に両手剣での攻撃モーションです。

f:id:kazuhironagai77:20201213212755p:plain

結構あるのでびっくりしました。素手での攻撃モーションがないのが残念ですね。

2.2.2 モーションの切り替えのチェック

キャラが歩行する時に、装備している武器によって歩行のアニメーションを変えた事を思い出しました。どの様に変更したのかを確認します。

f:id:kazuhironagai77:20201213212819p:plain

武器が無い時の歩きです。かなり走っています。

f:id:kazuhironagai77:20201213212839p:plain

武器を持っている時の走りです。両腕の動きが武器が無い時と比較して全く違います。

コードを見てみましょう。

ThridPersonCharacterクラスのmeshを見るとAnim ClassにMyThirdPerson_AnimBPがセットされています。

f:id:kazuhironagai77:20201213212858p:plain

MyThirdPerson_AnimBPを開くと、武器を持った状態と持ってない状態が別々に設定されていました。

f:id:kazuhironagai77:20201213212918p:plain

武器を持たない状態のアニメーション。

f:id:kazuhironagai77:20201213212937p:plain

武器を持った時のアニメーション。

f:id:kazuhironagai77:20201213212954p:plain

MyThirdPerson_AnimBP内のboolean変数であるWithWeaponがTrueの時は武器持ちのアニメーション、Falseの時は武器なしのアニメーションと言う条件になっていました。

f:id:kazuhironagai77:20201213213014p:plain

この変数にどうやって外部からアクセスするんだと思ったら、ThridPersonCharacterクラス内で以下の方法でアクセスしていました。

f:id:kazuhironagai77:20201213213031p:plain

言われてみれば当たり前ですね。

2.2.3 戦闘時の攻撃モーションのチェック

戦闘中の攻撃のアニメーションがどのように実装されているのか確認します。

ThirdPersonCharacterクラス内で実装されていました。

f:id:kazuhironagai77:20201213213055p:plain

攻撃の条件の時に、ある攻撃モーションを実行します。

2.2.3 戦闘時の攻撃モーションの直し

今週は簡単な対症療法で直します。2.2.4に根本的な直し方を示します。

f:id:kazuhironagai77:20201213213120p:plain

武器を装備していたら、剣盾装備のアニメーション、装備してなかったら今までのアニメーションを流します。

テストしてみます。

武器を装備させた状態で攻撃します。

f:id:kazuhironagai77:20201213213136p:plain

剣盾装備のモーションで攻撃しました。

武器を外した状態で攻撃します。

f:id:kazuhironagai77:20201213213152p:plain

素手の攻撃モーション(実際は素手の攻撃モーションはないので、弓の攻撃モーションで代用)で攻撃しました。

出来ました。

2.2.4 戦闘時の攻撃モーションの根本的な直し考察

今週はやりませんが、戦闘時の攻撃モーションは根本的な直しが必要です。以下の問題を抱えています。

  1. 攻撃モーションの管理にAnimationBPを使用していない。
  2. 攻撃モーションは、弓、二刀、魔法杖、剣と盾、両手剣、そして素手があるのにそれらの区別を装備する時にしていない。勿論、攻撃時のモーションでも区別していない。

これらの問題を直さなければなりません。来週以降、直していきます。

2.3 AIの勉強を毎週少しだけやる

これはバグと言う訳ではありません。先週、モンスターが攻撃の選択が出来る様にUE4_AIを少し勉強しました。

多分、先週勉強した分で、モンスターが自分で攻撃の選択が出来る様なAIの作成は可能だと思えますが、BehaviorTreeのDecoratorの種類とか、Taskの種類とか知らない事がいっぱいあるのでその辺を勉強したいです。

f:id:kazuhironagai77:20201213213234p:plain

f:id:kazuhironagai77:20201213213242p:plain

今週は、公式サイトのBehaviorTreeを読む事にします。

f:id:kazuhironagai77:20201213213301p:plain

今、最初のBehaviorTreeとBehaviorTree Quick Start Guideだけ読みました。以下に感想をまとめました。

  • BlackboardAIの脳と説明しているのは…。かえって混乱を生むのでは。
  • DecoratorTaskのそれぞれのノードの解説はBehaviorTree Node Referenceをみれば良いみたいですね。

残りは暇なときに読んでいきます。

2.4 戦闘時におけるモンスターの選択に幅を持たせるために…。

モンスターが選択出来る攻撃の種類を増やさないとモンスターの戦闘時のAIの作成はこれ以上は作成出来ません。

戦闘時に、ユーザーが操作出来るキャラが、攻撃、魔法、アイテム、そして「逃げる」から選択出来るので、同様にしようか迷っています。

アイテムはどうすべきでしょうか?

持っているアイテムは敵を討伐したら貰えるのでしょうか?

魔法だって、以下に示したように、EnemyMonsterInfoではMPの変数は作成していません。

f:id:kazuhironagai77:20201213213335p:plain

作り直すのは大変です。

更にUE4_AIは、結局if節のSyntactic sugar ですし、単純なヤツならBehaviorTreeを使用しない方が分かり易い気もします。

色々考えましたが、結論が出ましたので以下にまとめます。

  1. モンスターも攻撃、魔法、アイテム、そして「逃げる」が使用出来るように作り直します。
  2. ただし、今回は、あくまで戦闘時にモンスターのAIが選択を出来るのかどうかを試すだけなので、作り直す前に、攻撃A、攻撃Bの二つを作成してそれからAIに選択させるようにします。
  3. その結果を見てから、また考える事にします。

今週のバグの直しは以上です。

3. 戦闘システムの改善点の直し

11月22日のブログの「2. 今の戦闘システムを見直して問題点やバグを探しまとめます。」の「2.2 戦闘シーン毎の復習」で述べられている改善点を直していきます。

3.1 戦闘マップへの移動

3.1.1 捕まったモンスターによって戦うモンスターが変わるようにする

いきなりかなり難関な問題です。モンスターの出現は現在、UE4C++のRPGGameModeBaseクラス内のBeginPlay()関数内で初期化されています。

f:id:kazuhironagai77:20201213213423p:plain

で作成しています。

発生するモンスターの種類はMonsterToSpawn変数で指定しています。

f:id:kazuhironagai77:20201213213440p:plain

MonsterToSpawn変数の中身は、RPGGameModeBaseBPのMy Monsterで指定しています。

f:id:kazuhironagai77:20201213213458p:plain

この場合はMonsterBPを指定しています。

MonsterBPを開くとMeshがcomponentとして使用されており、

f:id:kazuhironagai77:20201213213516p:plain

そのメッシュにSkeleton_Swordmanが使用されていました。

f:id:kazuhironagai77:20201213213531p:plain

よし、大体分かりました。

Ch4_3Monsterクラスから派生したMonsterBPは一種類のモンスターであるSkeleton_Swordmanしか作成出来ないので、MonsterBPと全く同じですが、生成するモンスターだけが違う、MonsterBP2を作成します。

3.1.1.1 MonsterBP2の作成

MonsterBPを複製してMeshのSkeletal MeshにSkeleton Mageをセットします。

f:id:kazuhironagai77:20201213213558p:plain

Skeleon_Mageには専用のAnimationBPがありませんのでAnim Classにアニメーションをセットする事が出来ません。

f:id:kazuhironagai77:20201213213617p:plain

Skeleon_Mage専用のAnimationBPを作成します。

3.1.1.2 Skeleon_Mage専用のAnimationBPの作成

Skeleton_SwordmanのAnimationBPであるSkelSwordAniを見てみます。

f:id:kazuhironagai77:20201213213650p:plain

これしかありません。

f:id:kazuhironagai77:20201213213725p:plain

Idle/Runの中身は以下の様です。

f:id:kazuhironagai77:20201213213742p:plain

Speed変数の値はEventGraphで指定されていました。

f:id:kazuhironagai77:20201213213759p:plain

これならSkelSword1DのMage版さえ作成出来れば後は同じで良いですね。

SkelSword1Dを見てみましょう。

f:id:kazuhironagai77:20201213213816p:plain

f:id:kazuhironagai77:20201213213823p:plain

Speedが0の所でIdle、86.25の所でWalk、そして345の所でrunのアニメーションがセットされています。Mageも同じ様に作成しましょう。

Blend Space 1DからSkelMage1Dを作成します。

f:id:kazuhironagai77:20201213213840p:plain

このMage歩くのアニメーションがありません。浮いているんでしょうか?

仕方がないので、IdleとRunだけセットしました。

f:id:kazuhironagai77:20201213213856p:plain

今度は、Mage用のAnimationBPを作成します。

f:id:kazuhironagai77:20201213213913p:plain

AnimGraphから作成していきます。

State Machineを追加して名前をDefaultとしました。Swordman用のAnimationBPとやり方は全く同じです。

f:id:kazuhironagai77:20201213213929p:plain

Defaultを開いてStateを追加します。名前はIdle/Runとします。

f:id:kazuhironagai77:20201213213944p:plain

今度はIdle/Runの実装を行います。

f:id:kazuhironagai77:20201213214003p:plain

今度はevent graphを開いてSpeedの値を設定します。

f:id:kazuhironagai77:20201213214017p:plain

これで完成のはずです。

試してみます。

RPGGameModeBaseBPのMy MonsterのMonster to SpawnにMonsterBP2をセットします。

f:id:kazuhironagai77:20201213214033p:plain

勿論、MonsterBP2のMeshのAnimationのAnim ClassにはSkelMageAniをセットしておきます。

f:id:kazuhironagai77:20201213214049p:plain

Mageは飛んで追いかけて来ました。

f:id:kazuhironagai77:20201213214104p:plain

成功ですね。

3.1.1.2 戦闘中のアニメーションの変更

現状では、モンスターの戦闘中のアニメーションはRPGGameModeBaseBP内で以下のように管理しています。

f:id:kazuhironagai77:20201213214124p:plain

これにMageの場合を追加します。

f:id:kazuhironagai77:20201213214140p:plain

戦闘中のモンスターのアニメーションはUE4_AIで一括で管理するのか、別な関数を作成してそこで管理すべきなのかどうか?それともこのままダラダラ追加した方が結局は分かり易いのか?まだ結論は出ていません。

今回はこのままでやっていきます。

テストします。

Mageが攻撃しているanimationが表示されています。

f:id:kazuhironagai77:20201213214157p:plain

出来ていました。

3.1.1.3 いろいろなモンスターを発生させるために更に必要な事のまとめ

色々なモンスターを発生させるためには、更に以下の事をやる必要があります。

  1. モンスターの発生方法の管理をUE4C++で管理しているので、現在、発生出来るモンスターが限られている。
  2. 発生出来るモンスターの種類が2つしかない。もっと作成する。

今週は「捕まったモンスターによって戦うモンスターが変わるようにする。」はここまでとします。

3.1.2 キャラとモンスターの登場シーンをアニメーション化すべき

これはSkeleton _Swordmanの発生シーンのアニメーションが非常にカッコイイので、これを使用したいと思って追加しました。

f:id:kazuhironagai77:20201213214240p:plain

今回は戦闘開始時に限定してアニメーションを追加します。

プレイヤーが操作するキャラの方は単純に発生する場所を高めに設定しました。

f:id:kazuhironagai77:20201213214256p:plain

これで勝手に落下した時のアニメーションを実行してくれるはずです。

モンスターの発生方法ですがSkelSwordAnimのAnimGraphにSpawnを追加し、

f:id:kazuhironagai77:20201213214313p:plain

その中でモンスターが発生するアニメーションをプレイします。

f:id:kazuhironagai77:20201213214329p:plain

Idle/Runに移動するための条件は以下の様にしました。

f:id:kazuhironagai77:20201213214344p:plain

プレイするアニメーションは発生時の一回だけにするために、EventGraph から以下の様にしました。

f:id:kazuhironagai77:20201213214403p:plain

これで戦闘開始時にそれぞれのキャラの出現アニメーションが流れるはずです。

テストします。

f:id:kazuhironagai77:20201213214420p:plain

両方とも動きが速過ぎて、中々スクリーンショットが取れませんが、結構いい感じで出現しています。後でparticle effectを追加すれば更に見栄えが良くなりそうです。

3.2 戦闘開始前

戦闘開始前の改善点を直していきます。

以下の指摘がありました。

f:id:kazuhironagai77:20201213214441p:plain

3.2.1 タイプライター効果の追加

これは先週も述べましたが、次のプロジェクトでVNを作成するのでその時に作ります。今回はやりません。

3.2.2 読みましたボタンを少し遅らせて表示する

これもタイプライター効果がないと意味がないので今回はパスします。

3.2.3 読みましたボタンのデザインの改良

確かに。直す必要がありますね。

f:id:kazuhironagai77:20201213214524p:plain

以下の様に変更しました。

f:id:kazuhironagai77:20201213214540p:plain

3.2.4 戦闘中の画面を少し揺らす

Camera Shake ClassというBPクラスがカメラを揺らすために存在しているらしいです。

ここに詳しい解説がありました。

試してみましょう。

まずCameraShakeクラスからMonsterCameraShakeを作成します。

f:id:kazuhironagai77:20201213214603p:plain

以下の設定にしました。

f:id:kazuhironagai77:20201213214620p:plain

モンスターが攻撃した時に画面が揺れる様に実装しました。

f:id:kazuhironagai77:20201213214636p:plain

テストします。

揺れました。攻撃を食らった感じがします。

以下にスクリーンショットを示しますが、揺れた感じは撮れていませんね。

f:id:kazuhironagai77:20201213214655p:plain

大袈裟に使ったら乗り物酔い見たくなってしまうかもしれませんが、適度に使用する分にはかなり面白い機能です。

3.3 アクションの選択

アクションの選択では以下の改善点を直していきます。

f:id:kazuhironagai77:20201213214731p:plain

3.3.1コメントの文字が表示された後で「攻撃」「逃げる」ボタンは表示されるべき

これもタイプライター効果を作成しない今回のプロジェクトではパスします。

3.3.2「攻撃」「逃げる」ボタンが枠で囲われているが何のジャンルの枠なのかわからない

直していきます。

それぞれに簡単な説明を追加しました。

攻撃を選択した時です。

f:id:kazuhironagai77:20201213214801p:plain

道具を選択した時です。

f:id:kazuhironagai77:20201213214819p:plain

3.3.2 攻撃、逃げるボタンの色がバックグランドの色と同じで見にくいかもしれません

色を変えました。

f:id:kazuhironagai77:20201213214855p:plain

.4 攻撃の詳細について

攻撃を選択した後の詳細がない事に対しての改善点です。

f:id:kazuhironagai77:20201213214925p:plain

3.4.1 攻撃の種類の作成

攻撃の種類について考えましょう。と言う事です。と言っても魔法でも攻撃しているので、肉体を使った攻撃と言う意味です。

まず、アニメーションの種類から考えると、

  1. 素手
  2. 剣盾
  3. 二刀流
  4. 両手剣

があります。ただし厳密に言えばこれらは、攻撃の種類ではなく装備の仕方の種類です。攻撃の種類とは、例えば素手の場合は、

  • パンチ
  • キック
  • 頭突き
  • タックル

が該当すると考えられます。更には、

  • 防御
  • 防御重視
  • 守りつつ攻撃
  • 攻撃重視
  • 特攻

の様な戦術を主とした攻撃の種類の分類方法もあります。

以下の様に決めました。

素手、剣盾、二刀流、両手剣は装備の段階で選択出来る様にします。武器を何も持っていなければ素手、剣を持っていれば、両手剣、剣と盾を持っていれば剣盾、そして剣を二本持っていれば二刀流が選択出来るようにします。そして選択した装備方法によって戦闘時のアニメーションも変化します。

戦闘の種類は、防御、防御重視、守りつつ攻撃、攻撃重視、特攻の5種類を基本として、

素手の場合は、防御、守りつつ攻撃、特攻の3つから選択、二刀流の場合は、守りつつ攻撃、攻撃重視、特攻の3つから選択の様に戦闘の種類が選べるようにします。

更に戦闘の種類の効果は以下のようにします。

  • 防御:ATKの値を30%減らす。代わりにDEFの値を30%増加。
  • 防御重視:ATKの値を15%減らす。代わりにDEFの値を15%増加。
  • 守りつつ攻撃:そのまま。
  • 攻撃重視: DEFの値を15%減らす。代わりにATKの値を15%増加。
  • 特攻:DEFの値を30%減らす。代わりにATKの値を30%増加。

これで行きます。ただ実際に実装するのは来週以降にします。

3.4.2 攻撃の対象に自分の選べるようにする。

元々、教科書では、戦闘はチーム戦を主に考えられていたので、攻撃する対象を選択する必要がありました。今回のゲームでは戦闘は常に一対一に変更したので、攻撃対象を選ぶ必要は本当は無いんです。まあ選べるようにしておきましょう。

以下のコードをCombatUIの攻撃ボタンがクリックされた時に発動するeventに追加しました。

f:id:kazuhironagai77:20201213215046p:plain

テストします。

戦闘時に攻撃を選択すると以下のように自分も表示されますが、何か変ですね。

f:id:kazuhironagai77:20201213215104p:plain

ボタンのデザインを担当しているwidget、AttackTargetOptionが以下の様になっていました。

f:id:kazuhironagai77:20201213215122p:plain

以下の様に変更しました。

f:id:kazuhironagai77:20201213215138p:plain

その結果、以下の様になりました。

f:id:kazuhironagai77:20201213215155p:plain

取りあえずこれで良いでしょう。

自分を選択した場合のコメント欄のセリフは後で直します。

3.4.3 攻撃対象を選ぶ枠も何のグループを示しているのか不明です。題をつけましょう。

これは既にやりました。

3.4.4 ここでもボタンのデザインは見にくいです。

色を濃くしました。

f:id:kazuhironagai77:20201213215231p:plain

3.5 今まで選択した内容がコメント欄に表示される

以下の改善点を直していきます。

f:id:kazuhironagai77:20201213215257p:plain

3.5.1 確認するのが目的ならばユーザーが選択を変更出来る方法を用意すべきでは?

確認のためのコメントをもう一度読んでみます。

f:id:kazuhironagai77:20201213215323p:plain

特にオカシクはないですね。

ただしユーザーが選択を変更出来るための戻るボタンはあった方が良いですね。後で作成します。

3.5.2 コメント欄の文「“KUMO”は次の行動を決定しました。」は「“KUMO”は貴方の選択に従って次の行動を決定しました。」の方が正確に今の事態を表しているのでは?

直しました。

f:id:kazuhironagai77:20201213215345p:plain

f:id:kazuhironagai77:20201213215352p:plain

テストしている時に突然、エディターがクラッシュしてびっくりしました。再現性がない一回きりのクラッシュですがそれでも焦りました。

3.6 モンスターのターン

f:id:kazuhironagai77:20201213215417p:plain

この辺は考え中です。

3.7 ユーザーの行動1(操作するキャラのアニメーション表示 

f:id:kazuhironagai77:20201213215457p:plain

これは既に直しました。

f:id:kazuhironagai77:20201213215519p:plain

3.8 ユーザーの行動2(結果の表示)

f:id:kazuhironagai77:20201213215542p:plain

はい。次に行きます。

3.9 モンスターの行動1(アニメーションの表示)

f:id:kazuhironagai77:20201213215606p:plain

3.9.1「“ゴブリン”が“KUMO”を攻撃しました。」のコメントを追加する。

このコメントはモンスターの攻撃の種類や戦闘時の選択の種類を増やした後で、AIがそれらのどれかを選択出来るようになった後で追加します。

3.9.2 モンスターにも攻撃だけでなく、魔法やアイテムが使用出来るようにしたいです。

後で作成します。

3.9.3 ここでコメント欄に「“ゴブリン”は決定した行動を実行しました。」「“KUMO”は10のダメ―ジを受けました。」と表示するのが良いのか、画面が切り替わった後で、これらの内容を示すのが良いのかを考える必要があります。

KUMOも同じタイミングで表示されていました。のでこれで良いと思います。

3.10 モンスターの行動2(結果の表示)

f:id:kazuhironagai77:20201213215709p:plain

はい。

3.11 戦闘の繰り返し

f:id:kazuhironagai77:20201213215731p:plain

3.11.1  HPが0以下にならないようにする

簡単に直せるかと思ったら、攻撃のダメージを計算しているのは、自作のcombat engine内で、TestCombatActionクラスのTick()関数の実装部をいじる必要がありました。

f:id:kazuhironagai77:20201213215804p:plain

しかもこれだけいじっただけで、buildに5分位かかります。BPに慣れると、UE4C++で作成した部分や、普通のC++で作成した部分はいじるのが億劫になります。

結果を見ます。

f:id:kazuhironagai77:20201213215821p:plain

0になっています。

魔法の時も直します。TestCombatActionMagicクラスのTick()関数の実装部に同じコードを追加します。

f:id:kazuhironagai77:20201213215837p:plain

何で、HPがマックスを超えないようにチェックしているんですかね。回復魔法の作成はまだしてないんですが?

f:id:kazuhironagai77:20201213215853p:plain

直しました。

バグが一個見つかりました。魔法を使用した時の使用相手が敵しか表示されません。更にボタンの位置も変です。

f:id:kazuhironagai77:20201213215911p:plain

直しました。

f:id:kazuhironagai77:20201213215935p:plain

3.11.2  HPの後の:が文字と被っているので直しましょう。

直しました。

f:id:kazuhironagai77:20201213220002p:plain

f:id:kazuhironagai77:20201213220009p:plain

こんな方法で指定していました。

f:id:kazuhironagai77:20201213220025p:plain

この辺ももっと整理しやすい方法で管理したいです。後で直します。

3.12 戦闘の終了

f:id:kazuhironagai77:20201213220047p:plain

流石にレベルアップの報告機能の作成を今する体力はないです。来週に持ち越します。

4. まとめと感想

もう疲れました。今週はここまでとします。今週中に改善点を直したかったんですが流石に無理でした。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘システムを完成する4

f:id:kazuhironagai77:20201206213546p:plain

<前文>

コンテクストってどんな意味?

良く和訳されたビジネス書にコンテクストって出てくるじゃないですか。コンテンツじゃないですよ。コンテクスト。Googleで調べると「前後関係」とか「文脈」とか説明されています。しかし和訳された実際の文章でコンテクストを「前後関係」とか「文脈」で置き換えてもよく意味が分からないですよね。

コンテクストって勿論、contextのカタカナ読みです。そしてcontextを日本語に訳せば「前後関係」とか「文脈」になります。なりますが実際の文を読むと意味が分かりません。日本語と英語のある違いからcontextは日本語に訳すと全く理解出来なくなる珍しい単語なんです。

そのある違いとは、英語には一つの単語に沢山の意味がある場合が多いですが、日本語には極少数の例外を除いて一つの単語には一つの意味しかない事です。

分かり易い例を上げると、tipと言う単語です。レストランで使用する場合はチップと言う意味で、ウェイトレスらに渡す「お金」を意味します。しかし本にtipと書かれている場合は「豆知識」と言う意味です。お金と豆知識は全く意味が違います。だからアメリカ人にtipってどういう意味?と前提条件の無い状態で質問すると「それは状況によってです。」としか答えません。だって意味が全く違うんだから。

この「状況によって」と言う意味こそがcontextなんです。

  • Implementってどういう意味?」「このコンテクスト(context)では、コードを書くって意味。」
  • 「ジミーが a computerは好きだけとcomputersは嫌い。」って言ってたけど。どう意味が違うの?」「それはコンテクスト(context)が分からないと答えようがないよ。」
  • fineって「罰金」と「調子が良い」と言う意味は知っているけど?」「このコンテクスト(context)では「非常に細かい(粒子)」と言う意味。」

みたいに基本的にコンテクスト(context)は、単語の意味を説明するのに使用される言葉なんです。

はい。

日本語にそんな全く違う意味を複数持つ単語はないですね。同音な単語は結構ありますが、漢字で書いても同じで意味は違う単語って思い付きますか?

私は「お盆」しか思いつきませんでした。

だから、日本語には基本的にコンテクスト(context)に対応する概念がないんです。無いからcontextを含む分を日本語に訳すと???な文になってしまうんです。

コンテクストってどんな意味?part 2

コンテクストの良い例文がないか調べていたら、コンテクストの意味として「空気を読む」と説明しているサイトがありました。これはかなりの超訳ですが、ある意味的を射ていてかなり感心しました。例えば、今、流行りの自粛ですが、本来の意味では強制的なニュアンスはありませんが、今回の場合は半ば強制です。

  • 「自粛ってどういう意味?」「このコンテクストでは絶対やってはいけないって事。」
  • 「押すなよって言ってるけど」「このコンテクストでは押せって事よ。」

うーん。完璧ですね。

それでは今週の勉強を始めます。

<本文>

今週は以下に示した先週のバグの直しをして、

  • Hierarchical Instanced Static Meshの勉強のやり直し
  • Pause画面を開く時は、このSet Game Pausedをオンにすべきなのか?
  • スタート画面のキャラが動かせます。
  • 半分くらいのNPCは会話が可能な状態でも(!!)マークが表示されていません。
  • 現在使用していない機能を実装した箇所は消す。

その後で先週の作業の続きを行っていきます。

1.先週のバグの直し

1.1 Hierarchical Instanced Static Meshの勉強のやり直し

先週の大量の骸骨の表示ですが、

f:id:kazuhironagai77:20201206213739p:plain

ActorBPクラスから派生したクラス、MyBackgroundSkullsクラス内にHierarchical Instanced Static MeshをComponentとして追加する事で作成しています。

f:id:kazuhironagai77:20201206213800p:plain

f:id:kazuhironagai77:20201206213809p:plain

Hierarchical Instanced Static MeshではStatic meshにSkeltonHeadを指定しています。

f:id:kazuhironagai77:20201206213830p:plain

以下の方法で大量の骸骨を円周上に配置しています。

f:id:kazuhironagai77:20201206213850p:plain

以下の変数で発生する骸骨の数、骸骨同士の距離、円周のサイズ、骸骨の位置のばらつきをコントロールしています。

f:id:kazuhironagai77:20201206213909p:plain

骸骨の自転は、以下の方法で実装しました。

f:id:kazuhironagai77:20201206213927p:plain

骸骨の公転は、Hierarchical Instanced Static MeshをレベルBPから呼び出し回転を追加する事で実現しました。

f:id:kazuhironagai77:20201206213946p:plain

本来ならばUE4におけるActorクラスにComponentされたstatic meshの回転そのものを厳密に数学として調べたいです。

しかし今回はあくまでデザインが主なので、まず1000個の骸骨がHierarchical Instanced Static Meshを使用して実現する事でCPUに負担になっていない事を確認します。

やっぱり、前に見たこのtutorialが一番分かり易い説明していました。このvideo 内でFor Loopの説明を丁寧に始めたので、こんな初心者向けのTutorialを全部見る必要はない。と途中までしか見ないで作成を始めてしまったのですがやっぱり良く見るべきでした。

まず、このtutorialでは、AddInstance()関数をEvent BeginPlayで呼ばずに、Construction Scriptで呼んでいました。

f:id:kazuhironagai77:20201206214013p:plain

Construction Scriptで呼ぶ事で、viewport内でHierarchical Instanced Static Meshの全体像が確認出来ます。

f:id:kazuhironagai77:20201206214036p:plain

更に、それぞれの変数の目を開く事で

f:id:kazuhironagai77:20201206214056p:plain

Viewportのeditorから値を変更出来る様にしていました。

f:id:kazuhironagai77:20201206214117p:plain

この方法でやると、どんな風に骸骨が配置されるのかを確認しながら上記の値を変更出来ます。

Instanceの作成方法は、tutorialと全く同じだったのでactorを一個ずつ作成した時のような負担はcpuにはかかってないと思われます。

このビデオにはHierarchical Instanced Static Meshを使用した場合の優位性を測定する方法が紹介されていました。が今回はここまでにしておきます。

まあ、少しずつ勉強する事にします。

1.2 Pause画面を開く時はSet Game Pausedをオンにすべきなのか?

現状どうなっているのかの確認から始めます。

f:id:kazuhironagai77:20201206214146p:plain

Pause画面を開いたら、幾らマウスを動かしてもカメラは移動しません。Keyboardからの入力も受け付けません。

f:id:kazuhironagai77:20201206214208p:plain

更に、モンスターも追いかけて来ません。現状で理想の状態が出来ています。

コードを確認したら既にSet Game Pausedが使用されていました。

f:id:kazuhironagai77:20201206214239p:plain

更に、戻るボタンで、Set Game Pausedが解除されていました。

f:id:kazuhironagai77:20201206214301p:plain

これはPause画面に限らず、宿屋の会話でも何でもこれと同じにすべきですね。

確認します。

しました。全部なっていました。

1.3 スタート画面のキャラが動かせます。

Set Game Pausedを使用すれば動かなくなるはずです。

f:id:kazuhironagai77:20201206214339p:plain

カメラも動かなくなりました。

Set Input Mode UI Onlyに変更しました。

f:id:kazuhironagai77:20201206214402p:plain

今度は、カメラは動きますが、キャラはInputから操作出来ません。これで良いですね。

1.4 半分くらいのNPCは会話が可能な状態でも(!!)マークが表示されていません

今、確認したら、表示はされているのですが暗くて見えない時があるみたいです。

f:id:kazuhironagai77:20201206214453p:plain

Point lightを追加してみました。

f:id:kazuhironagai77:20201206214522p:plain

何となく、みんな光っています。

f:id:kazuhironagai77:20201206214602p:plain

しかしそのお陰で夜は滅茶苦茶見やすいです。

f:id:kazuhironagai77:20201206214621p:plain

(!!)マークも見やすいです。

f:id:kazuhironagai77:20201206214648p:plain

町の人なんか特に見やすいです。

f:id:kazuhironagai77:20201206214711p:plain

今回はPoint lightはそのままにしておきます。後で昼間は消えるようにします。

1.5 現在使用していない機能を実装した箇所は消す。

消しました。

正し、Show Mouse Cursor if Notノードを使用して

f:id:kazuhironagai77:20201206214736p:plain

以下の箇所を入れ替えようとしましたが止めました。

f:id:kazuhironagai77:20201206214757p:plain

更に、ワープして別のマップに移動する実装をどうやったのかもう分からなくなっているので、その辺りもいじりませんでした。

f:id:kazuhironagai77:20201206214815p:plain

この辺は戦闘システムが完成した後で直します。

2. 先週の続き

先週と言うか先々週の続きをやっていきます。

2.1 タイプライター効果の追加

f:id:kazuhironagai77:20201206214904p:plain

これは、今回のプロジェクトでは作成しません。このRPGの作成が終わった後、Visual NovelをUE4で作成します。その時にタイプライター効果も作成します。

2.2 攻撃ボタンと逃げるボタンしか表示されていない…

f:id:kazuhironagai77:20201206214927p:plain

2.2.1 コメントの直し

まず簡単なコメントから直します。以下のようにコメントを変えました。

f:id:kazuhironagai77:20201206215005p:plain

うーん。セリフが飛び出してしまいました。

f:id:kazuhironagai77:20201206215022p:plain

直しましたが、まだ文章がおかしいです。

f:id:kazuhironagai77:20201206215041p:plain

最終的にはこうなりました。

f:id:kazuhironagai77:20201206215058p:plain

2.2.2 ボタンの直し

ボタンの隙間も直しました。

道具を持っていなくて魔法も使えない時の選択ボタンの表示です。

f:id:kazuhironagai77:20201206215125p:plain

魔法が使用出来る様になった時の表示です。

f:id:kazuhironagai77:20201206215147p:plain

道具のみが使用出来る様になった時の表示です。

f:id:kazuhironagai77:20201206215203p:plain

魔法並びに道具が使用出来る時の表示です。

f:id:kazuhironagai77:20201206215220p:plain

どの場合もボタン同士の隙間は無くなりました。

2.3 戦闘時のモンスターの行動をAIに担当させる

f:id:kazuhironagai77:20201206215242p:plain

UE4のAIの作り方を完全に忘れてしまいました。

今週はUE4のAIの復習をします。

UE4C++はUE4C++と書く事にしていますが、UE4のAIをUE4AIと書くと何の事なのか分からなくなりそうなので、UE4_AIと書く事にします。

2.3.1 UE4_AIの勉強した内容を確認する

まずは過去のblogを見て今まで何を勉強したのかを確認します。

確認しました。

2019年に初めてUE4のAIの勉強をしたのですが、その時期はデータサイエンス大流行で機械学習とか深層学習以外はAIじゃないみたいな風潮が凄かったので、必死こいて大学のAIの教科書を読んでいました。だってUE4のAIってIf節しかないんですもん。

そしたら結局、AIってif節の塊で機械学習とか深層学習もどうやってif節を作成するかが違うだけって言う事が分かりました。

それで安心してUE4のAIの勉強を始めたんです。

始めたんですが、結局やったのはこのtutorialの最初の2~3個とMathew Wadstein氏のAIのチュートリアルをこれまた2~3個見ただけでした。しかしそれでもモンスターが自動でプレイヤーのキャラを追いかけるAIが完成したのでそれで完成と言う事になりました。

以上です。

2.3.2 UE4_AIの復習

兎に角、UE4のAIの作り方を完全に忘れているのでtutorialでもう一回勉強します。適当に簡単なtutorialないのかなと探していたらこのtutorialが短くて分かり易そうだったのでこれで復習します。

大体思い出しました。流石に2回目の勉強になると理解も速いです。

Tutorial自体もかなり分かり易い説明でした。一か所だけ不満だったのは、一寸不自然にenumを使用しているように思えた所だけでした。

他のtutorialも見ましたが「コレしてアレして、ハイ出来ました。」的な説明に終始していて「基本」が理解しにくい作りだったので、以下に私が簡単にまとめてみました。

2.3.3 UE4_AIの私なりのまとめ

兎に角、UE4_AIはif節の塊です。普段、

f:id:kazuhironagai77:20201206215336p:plain

と書いている事を別な方法で書くだけです。

その書き方を以下にまとめます。

2.3.3.1 UE4_AIに必要な4つのクラス

まず、UE4_AIをBPで使用するためには4つのBPクラスが必要です。

<Characterクラス>

Characterクラスから作成しました。

f:id:kazuhironagai77:20201206215416p:plain

AIにコントロールされるキャラクターが必要なのは当然ですね。

<AIControllerクラス>

AIControllerクラスから作成しました。

f:id:kazuhironagai77:20201206215439p:plain

以下に示したようにAIControllerクラスは、Controllerの派生クラスです。Player ControllerクラスのAIバーションです。

f:id:kazuhironagai77:20201206215550p:plain

プレイヤーがコントロールするキャラクターは

ユーザーのinput ―> Player Controller―>  character

によって操作されるように、AIがコントロールするキャラは、

CPU―> AI Controller―>  character

でコントロールされるわけです。

<Behavior Treeクラス>

f:id:kazuhironagai77:20201206215732p:plain

UE4_AIにおけるIf節を書くための特別なクラスです。

書き方もちょっと特殊で、木構造を使用しています。最初見た時は、まるで子供だましと思いましたが、理解が深まると、こっちの書き方の方が、可視化されているため、複雑な分岐も理解しやすい事が分かりました。

<Blackboardクラス>

黒板クラスです。

f:id:kazuhironagai77:20201206215758p:plain

さっき勉強したtutorialの説明によると、Behavior Treeクラスは何故か変数の値を保持する事が出来ないんだそうです。

そのためにBehavior Treeクラスで使用する変数の値は別なクラスで保持する必要があるそうです。それをするのがこの黒板クラスだそうです。

まあ、黒板に値を、ちょろっと書いておくと考えると分かり易いですね。

2.3.3.2 それぞれのクラスの繋げ方

クラスが単独で存在していても、それぞれを繋げないとお互いを認識出来ません。繋げ方を以下に示します。

<Characterクラス とAI Controllerクラス>

Characterクラス とAI Controllerクラスを繋げます。

f:id:kazuhironagai77:20201206215840p:plain

CharacterクラスのBPを開いて、Pawnの所のAI Controller Classに作成したAl Controllerクラスをセットします。この場合はNPC_Controllerをセットしました。

<AI ControllerクラスとBehavior Treeクラス>

Al ControllerクラスのBPを開いて、Run Behavior Tree()関数を呼び出します。そのパラメーターに作成したBehavior Treeクラスをセットします。

f:id:kazuhironagai77:20201206215905p:plain

上記の例では、NPCBTと言う名前のBehavior Treeクラスがセットされています。

<Behavior TreeクラスとBlack Boardクラス>

Behavior TreeクラスのBPを開くと、以下に示したように、BehaviorTree、BlackBoard Assetと表示されている箇所があります。

そこに作成したBlack Boardクラスをセットします。

f:id:kazuhironagai77:20201206215926p:plain

これでUE4_AIに必要な4つのクラスが全部つながりました。

2.3.3.3 Behavior Treeクラス内でif節を作成する。

やっとここまで説明出来ました。ほとんどのtutorialでいきなりBehavior Treeの説明から始まるから読者は混乱するんです。少なくとも私は混乱しました。

このBehavior Treeクラスでやる事はただ一つです。「if節を作成する事」だけです。

そしてその具体的な方法はBehaviorTreeのTutorialを見た方が分かり易いと思いますので、私がメモしておきたい事を以下にまとめます。

<変数を作成する>

Behavior Treeクラスの右上のBlackboardをクリックしてNPCBBを開きます。

f:id:kazuhironagai77:20201206220016p:plain

Blackboardクラス内でNew Keyを押すと変数が作成出来ます。

f:id:kazuhironagai77:20201206220054p:plain

こんだけです。

<Selectorとsequenceについての考察>

Selectorがif節の大本で、その下に付くsequence達がそれぞれのif節、else if節に対応していると考えられます。

If節のconditionはDecoratorノードで指定する事が出来ます。Blackboardクラスの値を使用する時はBlackboard Based Conditionを選択します。

If節のconditionが正しい時に実行されるコードは、serviceに書かれます。更にその後にtaskを追加する事も可能です。

2.3.4 Game mode クラスの変数とUE4_AI

モンスターの戦闘中の行動をUE4_AIで決定するためには、Game mode内の変数の値をBehaviorTreeにパスして、BehaviorTree内でそのモンスターが取るべき選択をAIに決定させ、その決定した選択をまたGame Modeに伝達する必要があります。今までの復習で得た知識からこれが可能かどうか試してみます。

まず、新しいGame mode baseを作成します。

f:id:kazuhironagai77:20201206220138p:plain

Attack、Magic、itemのenumeratorを持つEnumを作成します。その変数をCombat Stateと名付けてMy Game Mode内に作成します。

f:id:kazuhironagai77:20201206220201p:plain

更に、Event DispatchersでAttack、Magic、Itemを作成します。

f:id:kazuhironagai77:20201206220225p:plain

それぞれのEvent Dispatchersにcustom eventをbindします。

f:id:kazuhironagai77:20201206220242p:plain

Behavior Treeは以下のように作成しました。

f:id:kazuhironagai77:20201206220257p:plain

Selector内のsetCombatState内で以下の実装を行います。SetCombatStateはserviceクラスから派生して作成しました。

f:id:kazuhironagai77:20201206220313p:plain

State KeyはsetCombatState内の変数でタイプは、Blackboard keyです。上記のやり方で、State Keyに値をセット出来るらしいです。

そして、BehaviorTreeに戻って、State KeyをCombatStateにセットします。

f:id:kazuhironagai77:20201206220335p:plain

かなり手間ですが、これでGameMode内の変数の値が、Blackboardクラスの変数にセットされたはずです。

ここで特殊なのは、Service クラスからblackboardクラスへの値のパスの仕方ですね。Blackboard keyと言う名前の特別なオブジェクトが間に入っています。パスされた値はcopyされたのか単にreferenceされたのかも分かりません。分かりませんが、多分copyされたんでしょうね。住所を写すだけならこんな手の込んだ事をする必要ないですから。

f:id:kazuhironagai77:20201206220405p:plain

Selectorの説明ですが「左から順に実行していきますが、子供のうちの一つでも成功するとそこで実行を中止する。」と書かれています。この成功すると言う意味が子供のノードのdecoratorの条件にマッチしたと同じ意味ならif節、elseif節と同じ意味になります。

ので、

f:id:kazuhironagai77:20201206220423p:plain

CombatStateの値がAttackなら、Magicなら、そしてItemならの3つの条件を作成しました。

設定の詳細は以下の通りです。

f:id:kazuhironagai77:20201206220438p:plain

Decoratorがマッチした時に実行される内容はServiceノードで実装します。

f:id:kazuhironagai77:20201206220458p:plain

ServiceノードであるAtttack_serviceの内容を以下に示します。

f:id:kazuhironagai77:20201206220520p:plain

テストします。

f:id:kazuhironagai77:20201206220542p:plain

出来ていますね。

試しにMagicに変更してみます。

f:id:kazuhironagai77:20201206220557p:plain

出来ています。

ただし、

f:id:kazuhironagai77:20201206220613p:plain

Attack Eventは一回、発動すれば良いので、その辺の改良をするための勉強が必要です。

2.3.5 ServiceクラスとTaskクラス

ServiceクラスとTaskクラスの違いがまだ良く分かりません。

f:id:kazuhironagai77:20201206220635p:plain

結局どっちもIf節のconditionが合致した時に実行される内容です。 TaskクラスにはServiceクラスにしか出来ない何かがあるのでしょうか?

UE4_AIの勉強は、今週はここまでにします。

2.4 戦闘時のキャラのアニメーション表示時でもUIは表示させる

以下に示したように、ユーザーが操作するキャラのアニメーションではUIは消えています。

f:id:kazuhironagai77:20201206220703p:plain

一方で、モンスターのアニメーションでは

f:id:kazuhironagai77:20201206220718p:plain

UIが表示されたままです。

UIが表示されたままの方が、見栄えが良いのでユーザーが操作するキャラのアニメーション中もUIが見える様に変更します。

f:id:kazuhironagai77:20201206220734p:plain

当然ですが、アニメーションが終わった後にUIを再表示させる必要もないので、その部分のノードも外しました。

f:id:kazuhironagai77:20201206220754p:plain

試してみます。

f:id:kazuhironagai77:20201206220808p:plain

はい、ユーザーが操作するキャラのアニメーション中もUIが見える様になりました。

2.5 勝利のアニメーション時のカメラ位置を調整する。

f:id:kazuhironagai77:20201206220830p:plain

カメラの位置を変えるコードを追加しました。

f:id:kazuhironagai77:20201206220905p:plain

当然ですが、アニメーション終了後に、カメラの位置を戻すコードも追加しました。

f:id:kazuhironagai77:20201206221129p:plain

テストします。

出来ています。

f:id:kazuhironagai77:20201206221147p:plain

このスクリーンショットだとしょぼいですが、実際のアニメーションはかなりの迫力があります。

戦闘を速く終わらせるために今回は武器を持たせたのですが、

f:id:kazuhironagai77:20201206221204p:plain

武器を持っても戦闘中のアニメーションが素手の時と一緒でした。後で直します。

2.6 モンスターの死亡アニメーションの追加

f:id:kazuhironagai77:20201206221228p:plain

追加します。

Skelton_Swordman_Dieと言うアニメーションがありますのでこれを追加します。

f:id:kazuhironagai77:20201206221246p:plain

追加しました。

f:id:kazuhironagai77:20201206221304p:plain

テストします。

f:id:kazuhironagai77:20201206221319p:plain

素晴らしい死に方です。

ですが、カーソルが消えてしまいました。

2.7 戦闘終了後のカーソルの表示

直します。

色々な個所にShow Mouse Cursor If Not ()関数を追加してみたのですが、戦闘が終了するとカーソルが消えてしまいます。

これはUE4C++のRPGGameModeBaseクラスで何かカーソルの設定をしたかも。と思い調べると、

やっぱりUE4C++のRPGGameModeBaseクラスのTick()関数内のif(FightIsOverIsRead){}内で以下のように設定されていました。コメントアウトしました。

f:id:kazuhironagai77:20201206221354p:plain
そしたらずっとカーソルが表示されるようになってしまいました。

BPにCall出来る新しい関数を、このRPGGameModeBaseクラス内に作成して、if(FightIsOverIsRead){}内に実装して、その関数をBattleFieldレベルBP内で呼び出して、Show Mouse Cursor If Not ()関数をその後に実装すれば…。

面倒なので、今回はLevelBPのTick()関数に直接、Show Mouse Cursor If Not ()関数を付けちゃいました。

f:id:kazuhironagai77:20201206221415p:plain

UE4C++はCompileする時間が面倒です。ちょっと直して試しにBuildする事が出来ません。「BPでいいや。」となってしまいます。

テストします。

出来ました。

カーソルはスクリーンショットが取れないのでイメージ図はないです。

3. まとめと感想

今週はこんだけです。AIの残りは後で考えます。先々週にチェックした戦闘システムのバグを直しました。細かい部分はまだ直していないのでそれは来週以降になります。

 

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘システムを完成する3

f:id:kazuhironagai77:20201129200835p:plain

<前文>

グーテンベルグの銀河系とYouTubeのlive

Gundamに出て来るニュータイプって、活版印刷が発明された直後に起きたような人類の意識の進化が宇宙に移民した事で起きると言う意味だったはずなのに、何故か最終的にエスパーみたいな解釈になったんでしょうか。まるで途中から別人が書いたように解釈が変わっていますよね。

それはともかくとして、活版印刷が発明された直後に人類の意識が明白に変化した事を最初に指摘したグーテンベルグの銀河系はMcLuhan によって1968年に書かれました。その時代にMcLuhanは視聴者と芸能人の双方向性で進行するテレビ番組の出現を予測してそれが実現した時、更なる人類の意識の進化が起きると宣言していました。この視聴者の意見を聞きながら進行するテレビ番組って今、2020年のYouTubeのliveそのものですよね。

ただ人気のliveでは、視聴者の意見が多すぎてとても生主は全ての意見を聞く事は出来ませんね。だからまだグーテンベルグの銀河系で予測されていたほど双方向性ではありません。

それであくまでグーテンベルグの銀河系の予測が正しいと言う前提で考察すると次世代の動画サイトは以下の様になるんじゃないでしょうか?

  • コメントを書く一般視聴者側もレベルによって色分けされ、超人気生主のLiveにコメントを書くためには、最高レベルになった人だけがコメント出来る。
  • 低レベルの視聴者は、超人気生主のLiveは見るだけ。ただし一般人とほとんど変わらない人気のない生主には低レベルの視聴者でもコメントを書く事が出来る。
  • 視聴者はコメントを書いて評価される事でレベルを上げる事が出来る。書かれたコメントの評価は他の視聴者が行う。
  • 今のスパチャは、低レベルの視聴者が特別に超人気生主のLiveにコメントするために使用される。

こうなる事で、人気のliveでも生主と視聴者の双方向性が確立出来ますし、人気のない生主のliveでも視聴者の双方向性が確立出来ます。

こういうシステムを持つ動画サイトはまだ誰も作成していませんね。ひょっとしたら一攫千金のビジネスになるかもしれません。誰かが出資してくれるなら自分で作ってみてもいいかなと思います。

それでは、今週の勉強を始めます。

<本文>

今週は以下の事をやって行きます。

  • Hierarchical Instanced Static Meshを使用して戦闘システムのマップを改良する

それぞれの戦闘シーンでも直しを加えます。

  • 戦闘マップへの移動->キーボードのIを押したら、戦闘マップに移動するのではなく、徘徊するモンスターに捕まったら戦闘マップに移動するようにする。

こんな感じでやっていきます。

. Hierarchical Instanced Static Meshを使用して戦闘システムのマップを改良する

先週、作成したHierarchical Instanced Static Meshの実装を適用して骸骨の星を浮かべます。

f:id:kazuhironagai77:20201129201044p:plain

f:id:kazuhironagai77:20201129201052p:plain

全体的に暗いのでGoodSkyの設定を昼にしました。

後、UE4.24では、Hierarchical Instanced Static Meshに自転を追加するのにMark Render State Dirtyをチェックする必要がありました。

f:id:kazuhironagai77:20201129201115p:plain

UE4.26と全く同じ実装をしたのにHierarchical Instanced Static Meshが全く自転しないのでかなり悩みました。1時間位費やした上に正直クタクタになりました。

後、今のHierarchical Instanced Static Meshの実装方法が正しいのかもうちょっと確認した方がいい気がしてきました。

後でもう一回勉強します。

2. 戦闘シーンの直し

2.1 戦闘マップへの移動

モンスターに捕まったら戦闘画面に移動するようにします。

今週は動けば良い作戦で以下の様に実装してみました。

MonsterBPにboxを追加します。

f:id:kazuhironagai77:20201129201159p:plain

RPGGameModeBaseBPクラスにEvent Dispatchers、CombatBeginを作成します。

f:id:kazuhironagai77:20201129201216p:plain

このEvent DispatcherをMonsterBPとLevelBPからcallしたりbindしたりする事でMonsterBPとLevelBPの間で連絡を取り合います。

MonsterBP内でBoxにプレイヤーが操作するキャラが侵入したら、RPGGameModeBaseBPのEvent Dispatchers、CombatBeginをCallします。

f:id:kazuhironagai77:20201129201241p:plain

今度は、LevelBP内のEventBeginPlay()関数にRPGGameModeBaseBPのEvent Dispatchers、CombatBeginとEvent CombatBeginsをbindします。

f:id:kazuhironagai77:20201129201257p:plain

EventCombatBeginsの実装は、前回作成した戦闘マップに移行するための実装と同じです。

f:id:kazuhironagai77:20201129201315p:plain

テストします。

モンスターに捕まると

f:id:kazuhironagai77:20201129201329p:plain

一瞬で戦闘マップに移動しました。

f:id:kazuhironagai77:20201129201345p:plain

最初は、UE4C++のRPGGameModeBaseクラスのenum、EPlaceForEventsにPE_MonsterAttackedを追加して

f:id:kazuhironagai77:20201129201400p:plain

他のNPCのeventと同じように作成しようとしたのですが、上記のeventはユーザーがeventに参加するかどうかの選択があるのに対して、モンスターに襲われる場合は、ユーザーには拒否権がないので、別な方法で作成しました。

この実装方法がbestではないかもしれませんが、動く事は動くので今週はこれでいきます。来週以降、別なもっと良いやり方を思いついたら変更します。

2.2 新しくマップが開かれるごとにカーソルが表示される問題について

カーソルはウィジェットが開かれた時だけ表示するようにしたいです。逆にウィジェットが開かれた時は、必ずカーソルが表示されるようにしたいです。

Set Input Mode Game And UIノードのIn Mouse Lock Modeについて調べる事でこの問題、解決出来ないかと思っています。

f:id:kazuhironagai77:20201129201438p:plain

因みに最初のスタート画面から

f:id:kazuhironagai77:20201129201454p:plain

「新しく始める」をクリックしてMap1に移動する時は、カーソルは表示されません。

「新しく始める」をクリックして発動されるEventの実装には、以下のノードが使用されていました。

f:id:kazuhironagai77:20201129201513p:plain

f:id:kazuhironagai77:20201129201521p:plain

f:id:kazuhironagai77:20201129201528p:plain

まず、SetInputModeについて調べます。

このノード昔、一回勉強したのですが、今一良く分からなかったんです。今回はきちんと理解したいです。

このビデオに詳しい説明がありました。

所で、ビデオを見ている間に気が付いたのですが、ゲームが完成したらPlay画面にも何らかのUIは表示されるようになるはずです。ので常にカーソルが表示されている方が便利な気がしてきました。さらにPCでゲームをする時に同時にネットで調べものをしたり、友達とチャットしたりする必要もあるはずで、カーソルはそういう意味でも必ず表示すべきでした。

SetInputModeについて簡単にまとめておきます。

公式のサイトか上記のビデオに書かれている事以外について分かった事です。

f:id:kazuhironagai77:20201129201547p:plain

SetInputModeGameOnlyですが、SetInputModeGameAndUIとSetInputModeUIOnlyから推測して、UIへのinputが出来なくなると思いますがそれが出来ます。

このサイトの解説によると、

f:id:kazuhironagai77:20201129201610p:plain

と書かれていました。要するにUIを使用不能にするためにSetInputModeGameOnlyは使えないと言う事ですね。

しかし、この人が解答してくれるまで別な人が訳分からない事を言っていますが、自分が分かってないのに質問に答えていますよね。

もう一つ、これは日本語のサイトで見つけたのですがSetInputModeUIOnlyをキャラが走っている状態で使用するとUIが表示された後もそのキャラが走り続けるらしいです。

試してみます。

f:id:kazuhironagai77:20201129201642p:plain

走り続けています。一端UIを開いたら止められません。凄いですね。こんなの全く気が付きませんでした。

2.2.1 SetInputModeのまとめ

SetInputModeはカーソルの表示とは関係ないと言う事は分かりました。

2.2.2 Set Game Pausedについても調べます。

このビデオに知りたい事はほとんど説明されていました。

ただ一つ分からないのはUIからのInputはGame Paused中でも受け付けるのは分かりましたが、何はpauseされて何がpauseされないのかちょっと不明ですね。

Pause画面を開く時は、このSet Game Pausedをオンにすべきなのかどうか?それが問題ですね。

後でこの問題については考えます。

2.3 ゲームプレイ中は、カーソルは必ず表示する

はい。これで行きます。全部直します。

2.3.1スタート画面

これでカーソルは何時でも表示されるはずです。

f:id:kazuhironagai77:20201129201811p:plain

スクリーンショットにはカーソルは写りませんでしたが、最初から表示されました。更にplay画面を一回クリックしてもカーソルが消える事はありません。

問題も見つかりました。プレイヤーの操作するキャラがこの時点で既に操作出来ます。

f:id:kazuhironagai77:20201129201835p:plain

キャラがどっかに行ってしまいました。これは後で直します。

良く考えたら、cursorが表示していない時だけcursorを表示しろと命令するはずなので、以下の実装に変更しました。

f:id:kazuhironagai77:20201129201903p:plain

これは色々なマップから呼び出されるのでBPFunctionにShowMouseCursorifNot()関数として作成しました。

f:id:kazuhironagai77:20201129201940p:plain

こんな感じに実装し直しました。

f:id:kazuhironagai77:20201129202005p:plain

テストします。

cursorはスクリーンショットに写りませんがキチンと表示されていました。一回クリックしてもcursorは消えませんでした。

2.3.2 その他のマップやwidget

Map1の開始時にもCursorが表示されるようにしました。

f:id:kazuhironagai77:20201129202029p:plain

Map2やMap3も同じようにしました。

Pause画面が閉じた後もcursorが消えないようにしました。

NPCとの会話後もcursorが消えないようにしました。

全員と会話してcursorが消えてない事を確認しました。

f:id:kazuhironagai77:20201129202049p:plain

半分くらいのNPCは会話が可能な状態でも(!!)マークが表示されていませんでした。

f:id:kazuhironagai77:20201129202110p:plain

f:id:kazuhironagai77:20201129202120p:plain

後で直します。

Shopから出る時もcursorが消えないように変更します。

道具屋、武器屋、宿屋から出た時もcursorが消えない様にしました。

Narrationが表示される時とitemを拾う時も直します。

直しました。

これで大体全部直したはずです。

確認のためにShow mouse cursor nodeを使用している箇所を検索してみます。

f:id:kazuhironagai77:20201129202142p:plain

うわ。全然直し切っていませんでした。

最初のShow mouse cursorですが、ThirdPersonCharacterクラス内でkeyboardのMを押すと地図を開くように実装されている箇所でした。

f:id:kazuhironagai77:20201129202208p:plain

しかしMap1のLevelBPで

f:id:kazuhironagai77:20201129202230p:plain

keyboardのMを押すと魔法を習得するように実装したため、既に機能していません。

このような現在使用していない機能を実装した箇所が結構沢山残っている様です。それの整理をする必要がありますね。これも後でやります。

2.3.3 キャラの視点をマウスで操作する時にカーソルが消える時と消えない時がある

視点を操作する時だけ、カーソルが消える場合と消えない場合があります。カーソルが消えている時の方が視点がスムーズに動きます。視点を操作する時はカーソルが表示されないようにします。

これこそSet Input Modeの問題でしょう。

確認のためTestGameModeプロジェクトで試してみます。

まずはcursorだけ表示してみます。

f:id:kazuhironagai77:20201129202253p:plain

Screenshotには撮れないですがcursorが映っています。カメラの視点に合わせてcursorが動きます。大変見にくいです。

SetInputModeGameAndUIをセットします。

f:id:kazuhironagai77:20201129202314p:plain

InWidgettoFocusは開いているwidgetがないので無視します。

テストします。

Screenshotには撮れないですがcursorが映っています。カメラの視点に合わせてマウスを動かす時はcursorが消えます。大変見やすいです。

はい。確認とれました。

SetInputModeGameAndUIを使用すればこの問題は解決します。

2.3.4 キャラの視点をマウスで操作する時にカーソルを消す。

ShowMouseCursorIfNot()関数にSetInputModeGameAndUIを追加しました。

f:id:kazuhironagai77:20201129202347p:plain

ここでもInWidtettoFocusには何もセットしていません。

テストします。Map1を開きます。

Screenshotには撮れないですがcursorが映っています。カメラの視点に合わせてマウスを動かす時はcursorが消えます。

出来ました。

3. まとめと感想

もう精も根も尽きました。今週はこれでお終いです。

BPで実装するのは思っていたより複雑な事が出来る事が分かりました。プロのプログラマーからはあまり人気のないVisual programming languageであるBPですが、本格的に使用してみるとかなり面白いです。

普通のプログラミングを文章を書く事に例えるなら、Visual programming languageはブロックを組み立てて何かを作成する感じに似ています。

ただBPの問題点も同時に分かりました。カプセル化のやり過ぎで中身がどうやって実装しているのか全く分かりません。UE4C++ならばLibraryの関数を使用する時でも、その関数のコードを読む事でどんな事が起きているのか大体は理解出来ましたが、BPは完全なブラックボックスです。

例えばあるmeshにAddRotationノードを使用した後でAddTransformノードを使用した場合と、AddTransformノードを使用した後でAddRotationノードを使用した場合は、3次元における移動、回転の行列に素直に従うならば全く違う結果になるはずなのですが、UE4BPでは同じ結果になります(100%断言は出来ませんが、少なくとも私が先週と今週試した限りではそうなりました。)。この辺は素直にmeshの行列の計算結果を見せてほしいと思いました。

仕方がないので、そのmeshに親meshを追加して望みの回転と移動を追加しましたが、この方法で回転させるとGimbal lockが発生して折角quaternionで計算している意味が無くなるんじゃないと思いました。

以上です。

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘システムを完成する2

f:id:kazuhironagai77:20201122221405p:plain

<前文>

日本語の「は」と「が」の違いをアメリカ人に説明する

今の若いアメリカ人は日本のアニメが大好きですが、アニメを見ているとどうしても日本語を勉強したくなるそうです。しかしアメリカにも老害と言われる人達が居て「アニメを見ても日本語は学べない。」とアニメ好きな若者に説教するんです。この説教はどう考えてもウソです。だって日本人だってハリウッド映画を見て英語の勉強するじゃないですか。だからアニメを見て日本語の勉強は出来ます。と言うか、ある部分の日本語はアニメで勉強する必要があります。

こういう嘘が問題なのは、日本語を勉強したての初心者には親切なアドバイスに見えるし、日本語を勉強している中級者には嘘と分かっても論破出来ないところです。

そこで思いついたのですが、アニメ好きなアメリカ人に、日本語ネイティブがしている日本語の「は」と「が」の区別方法を教えてあげるのはどうでしょうか。

10年以上日本語を勉強しているアメリカ人でも「は」と「が」の違いが分からない人は結構います。と言うかほとんど全ての日本語学習者が分かっていません。「先生は「アニメを見ても日本語は学べない。」と仰っていますが、先生の日本語の「は」と「が」の使い方間違っていますよ。」と言えるようになれば「アニメを見ても日本語は学べない。」のような嘘に騙される事もありませんし、その先の日本語の勉強にも弾みがつくし自信にもつながります。

日本語ネイティブが「は」と「が」の選択する時に考えているのはたった一つの事だけです。

後ろが大切な時は「は」、前が大切な時は「が」を使用します。

例えば” Charizard is a dragon.”と言う文があった時、

の二つの訳が考えられます。この文でドラゴンが大切な時(つまり後ろが大切な時)は「は」が正解になります。ほとんどの文は大切な事は最後に述べるので「は」の使用頻度は「が」よりかなり多いはずです。稀にリザードンが大切な時(つまり前が大切な時)もあります。その時は「が」が正解になります。

先程の例文に対して

“Charizard is not a dragon.”と返答した場合は”not a dragon”の部分が大切になります。ので「は」が正解になります。「リザードンドラゴンではない。」が正解で「リザードンドラゴンではない。」といったら日本人にとって可笑しな文に聞こえます。

“Dragonite is a dragon”と更に前の返答に追加した場合は”dragonite”の部分が大切になります。だから「カイリュウドラゴン」が正解になります。しかしこの場合は「”is a dragon”の部分の方が大切。」と考える事も出来て、その場合は「カイリュウドラゴン」でも正解になります。

これを今度、アニメが好きなアメリカ人にあったら教えてあげる事にします。

それでは今週の勉強を始めます。

<本文>

今週も先週の続きをやって行きます。

  • UE4C++GameModeBaseクラスでBeginPlay()関数をoverrideするとBPEvent BeginPlayが使用出来なくなる件について調べます。先週やり残した部分です。
  • 今の戦闘システムを見直して問題点やバグを探しまとめます。
  • その内のいくつかを直します。

それではやって行きます。

1.UE4C++GameModeBaseクラスでBeginPlay()関数をoverrideするとBPEvent BeginPlayが使用出来なくなる件

どうせなんで、4.26で新しいprojectを作成してそこで実験してみます。

TestGameModeを作成しました。そこにMyGameModeをUE4C++で作成します。

f:id:kazuhironagai77:20201122221531p:plain

そこから派生したBPクラス、MyGameModeBPを作成します。

f:id:kazuhironagai77:20201122221846p:plain

MyGameModeBPをこのマップのGameMode Overrideにセットします。

f:id:kazuhironagai77:20201122221934p:plain

この状態で、EventBeginPlayにPrintStringを実装します。

f:id:kazuhironagai77:20201122222005p:plain

テストします。

f:id:kazuhironagai77:20201122222032p:plain

普通にBeginPlayのeventは発動して、helloをprint しました。

次にUE4C++のMyGameModeクラスでBeginPlay()関数をoverrideします。

f:id:kazuhironagai77:20201122222101p:plain

f:id:kazuhironagai77:20201122222108p:plain

この状態でテストします。

f:id:kazuhironagai77:20201122222126p:plain

今度は何も表示されなくなりました。今のCh4_3と同じ状態が再現出来ました。

ここから本題なんですが、UFUNCTION()+何かのSpecifierでBPのBeginPlay()関数も動かせないのかなと思っています。

今のCh4_3の状態ではUE4C++の方のBeginPlay()関数を外す訳にはいかないんです。しかしBP側のEventBeginPlay()関数も使用したいんです。その方法を探したい訳です。

このサイトをみると

f:id:kazuhironagai77:20201122222204p:plain

位ですか。可能性がありそうなのは。

その前に一応、UE4C++のMyGameModeクラスのBeginPlay()関数が実行されているか確認します。

f:id:kazuhironagai77:20201122222224p:plain

別にUE_LOGでも良かったんですが、BPでいつもPrintStringを使用しているのでUE4C++でもPrintString()を使用してみました。

f:id:kazuhironagai77:20201122222254p:plain

はい。動いています。確認出来ました。

これで試してみます。

f:id:kazuhironagai77:20201122222330p:plain

エラーになりました。

f:id:kazuhironagai77:20201122222359p:plain

では、source fileの実装はcomment outします。

f:id:kazuhironagai77:20201122222418p:plain

今度はビルド出来ました。

しかし何も表示されません。

f:id:kazuhironagai77:20201122222438p:plain

このBeginPlay()関数は呼ばれていないみたいです。

f:id:kazuhironagai77:20201122222500p:plain

別のBeginPlay()関数が出来ていました。それにHello2をPrintさせてみます。

f:id:kazuhironagai77:20201122222524p:plain

やっぱりこっちの関数だけがBPで実行されているようですね。

f:id:kazuhironagai77:20201122222543p:plain

段々分かって来ました。SuperをUE4C++のBeginPlay()関数に追加すれば良いです。

f:id:kazuhironagai77:20201122222604p:plain

試してみます。

f:id:kazuhironagai77:20201122222626p:plain

はい。出来ました。最初にBPで実装した

f:id:kazuhironagai77:20201122222643p:plain

が実行されその後で、UE4C++で実装されたBeginPlay()が実行されています。

出来ました。

2. 今の戦闘システムを見直して問題点やバグを探しまとめます。

2.1 戦闘シーンを復習する

2.1.1 戦闘マップへの移動

f:id:kazuhironagai77:20201122222727p:plain

キーボードのIを押すと戦闘マップに飛びます。

2.1.2 戦闘開始前

f:id:kazuhironagai77:20201122222749p:plain

カーソルが表示されますが「読みましたボタン」は押せません。一回画面をクリックする必要があります。その後で「読みましたボタン」を押せるようになります。

カーソルが表示されたらボタンが押せるようにした方が良いと思いますがどう直せばいいのか分かりません。

コメント欄の文字はタイプライター効果で一文字ずつ表示された方が読みやすいですね。そして全部表示された後で「読みましたボタン」が表示されると、更に分かり易くなりますね。

「読みましたボタン」を押します。

2.1.3アクションの選択

f:id:kazuhironagai77:20201122222825p:plain

コメント欄には、攻撃、魔法、道具そして逃げるから一つを選択して下さい。とありますが、攻撃と逃げるしか表示されていません。

後、コメントの文字が全て表示される前に既に攻撃ボタンと逃げるボタンが表示されています。

攻撃を選択しました。

2.1.4 対象の選択

f:id:kazuhironagai77:20201122222851p:plain

攻撃対象のゴブリンが表示されました。コメント欄には「あなたは“攻撃”そして…」と文の途中まで表示されています。これは分かりやすいと思います。

ゴブリンを選択します。

2.1.5 今まで選択した内容がコメント欄に表示される

f:id:kazuhironagai77:20201122222916p:plain

コメント欄に「あなたは“攻撃”そして“ゴブリン”を選択しました。“KUMO”は次の行動を決定しました。」と表示されました。

この辺は、後で変更出来ると更に親切かもしれませんね。まあ出来なくても及第点でしょう。

「読みましたボタン」を押します。

2.1.6 モンスターのターン

f:id:kazuhironagai77:20201122222938p:plain

コメント欄に「“ゴブリン”は次の行動を考えています。」「“ゴブリン”は次の行動を決定しました。」と表示されます。

されますが実際には何も考えていません。攻撃するだけです。この部分をAIに連結させて、モンスターも攻撃、魔法、道具、そして逃げるの選択を出来るようにしたいです。

「読みましたボタン」を押します。

2.1.7 ユーザーの行動1(操作するキャラのアニメーション表示

f:id:kazuhironagai77:20201122223002p:plain

カメラの位置が変わり、ユーザーが操作するキャラを写します。カメラはキャラが攻撃するアニメーションを写します。

2.1.8ユーザーの行動2(結果の表示)

f:id:kazuhironagai77:20201122223022p:plain

コメント欄には「“KUMO”は決定した行動を実行しました。」「“KUMO”がゴブリンを攻撃しました。」「“ゴブリン”は30のダメ―ジを受けました。」「“KUMO”は行動の実行を終了しました。」と表示されました。

ゴブリンにdouble quotation が付いていないのが一つありますね。

「読みましたボタン」を押します。

2.1.9 モンスターの行動1(アニメーション)

f:id:kazuhironagai77:20201122223043p:plain

今度はカメラがモンスターを写します。そして攻撃用アニメーションを表示しました。こっちは、ウィジェットは消えていません。こっちの方がカッコイイです。

コメント欄には「“ゴブリン”は決定した行動を実行しました。」「“KUMO”は10のダメ―ジを受けました。」と表示されました。

ユーザーの操作するキャラには、「“KUMO”がゴブリンを攻撃しました。」のコメントがありますが、モンスターにはありません。

これはモンスターが攻撃するだけだからでしょう。

直す必要がある部分ですね。

2.1.10 モンスターの行動2(結果の表示)

f:id:kazuhironagai77:20201122223114p:plain

アニメーションが終わると元の画面に戻ります。

コメント欄には「“ゴブリン”は行動の実行を終了しました。」と表示されました。

2.1.11 戦闘の繰り返し

上の攻撃を3~4回ほど繰り返した後、敵のモンスターのHPが0以下になりました。

2.1.12 戦闘の終了

f:id:kazuhironagai77:20201122223143p:plain

コメント欄には「“KUMO”は勝利しました。」と表示されました。

ここでレベルが上がっていますが、コメント欄には何のコメントも表示されていません。これは直す必要があります。

「読みましたボタン」を押します。

2.1.13 勝利のアニメーション

f:id:kazuhironagai77:20201122223202p:plain

ユーザーが操作するキャラが勝利のポーズを取ります。カメラは後ろから写したままです。カメラの位置は変えるべきですね。

モンスターは倒されているのにその場に立っています。死んだときのアニメーションを追加すべきですね。

2.1.14  戦闘が終わってから元のマップに戻るまで

f:id:kazuhironagai77:20201122223223p:plain

戦闘は終わりました。Iボタンを押すと元のマップに戻れるはずです。

2.1.15  元のマップに戻る

戻って来ました。

f:id:kazuhironagai77:20201122223247p:plain

2.2 戦闘シーン毎の復習

前節で分けたそれぞれの戦闘シーン毎の分析を行っていきます。以下にまとめたように、目的、やっている事、改善点、バグ、そしてその他の5つの観点から分析します。

  • 目的: そのシーンでやりたい事を示します。
  • やっている事: 実際にそのシーンでやっている事を示します。
  • 改善点: 具体的にどこを直すのかを示します。
  • バグ: そのシーンでのバグを指摘します。
  • その他: 上記4点以外で指摘する必要がある事を書いておきます。

2.2.1 戦闘マップへの移動

目的:戦闘マップへの移動

やっている事:Iボタンを押す事で、戦闘マップへの移動

改善点:

  • 普段のマップでモンスターに捕まったら戦闘マップへ移動するようにする。
  • 捕まったモンスターによって戦うモンスターが変わるようにする。
  • キャラとモンスターの登場シーンをアニメーション化すべき。

バグ:

戦闘マップでの登場シーンで、キャラとモンスターが空から落ちてくる時、と来ない時がある。落ちてこない時は画面に引っかかって登場している。

f:id:kazuhironagai77:20201122223357p:plain

画面に引っかかって登場している時は、見た目も変だし不快に感じる。

その他:なし

2.2.2 戦闘開始前

目的:戦闘が開始した事をユーザーに知らせます。ユーザーが戦闘が開始した事を確認します。「読みましたボタン」を押します。

やっている事:

コメント欄に「戦闘が開始しました。」を表示します。これは戦闘が開始した事をユーザーに知らせるためです。

f:id:kazuhironagai77:20201122223424p:plain

「読みましたボタン」を表示し、このボタンを押すまで次のシーンに移動しないようにしています。これはユーザーが戦闘が開始した事を理解しているかどうかの確認のためです。

f:id:kazuhironagai77:20201122223450p:plain

改善点:

  • 画面が切り替わった瞬間に、コメント欄に全文章が表示されるため、コメント欄の文章が読みにくいです。タイプライター効果を使用すべきです。
  • 最初から「読みましたボタン」が表示されています。ユーザーが文章を読み終えた後に、「読みましたボタン」は表示されるべきです。
  • 読みましたボタンのデザインをRPG風に直しましょう。
  • ユーザーが戦闘に参加している感が少ない気がします。カメラを少し揺らしてみるのはどうでしょうか?

バグ:

このシーンだけではないですが、新しいマップが開始するたびにカーソルが表示されゲーム画面をクリックする必要があります。ユーザーの予測不可能な行動を引き起こしやすく、直せるならば直ぐにでも直したいです。

その他:なし

2.2.3アクションの選択

目的:キャラの一ターン目に取る行動をユーザーが選択します。

やっている事:

  • コメント欄に攻撃、魔法、道具そして逃げるから一つを選択して下さい。と表示。
  • 選択ボタン、「攻撃」、「逃げる」を表示。(「魔法」、「道具」は使用可能時のみ表示される。)
  • ユーザーは選択ボタンから一個をクリックする。

f:id:kazuhironagai77:20201122223614p:plain

改善点:

  • コメントの文字が全て表示される前に既に「攻撃ボタン」と「逃げるボタン」が表示されています。それらのボタンは、ユーザーが文章を読み終えた後に表示されるべきです。
  • 枠で覆われた絵の中に「攻撃ボタン」と「逃げるボタン」が表示されていますが、何のグループをまとめたものか分かりずらいです。選択などの題を表示すべきです。
  • 「攻撃ボタン」と「逃げるボタン」の色がバックの絵の色とほぼ同じで見にくいかもしれません。

バグ:なし

その他:「攻撃」ボタンを選択しました。

2.2.4 対象の選択

目的:攻撃対象を選択します。

やっている事:

  • 攻撃対象である「ゴブリンボタン」が表示されました。

f:id:kazuhironagai77:20201122223703p:plain

  • コメント欄には「あなたは“攻撃”そして…」と文の途中まで表示されています

改善点:

  • 攻撃の種類がないため、攻撃方法の選択は省かれています。何らかの形で攻撃の種類も考えるべきかもしれません。素手ならパンチ、キックとかもありかもしれません。
  • 攻撃対象もゴブリンしかいません。アイテムの時の様に、自身のキャラも選べるようにすべきです。
  • 攻撃対象を選ぶ枠も何のグループを示しているのか不明です。題をつけましょう。
  • ここでもボタンのデザインは見にくいです。

バグ:特にないと思われます。

その他:ゴブリンを選択します。

2.2.5 今まで選択した内容がコメント欄に表示される

目的:ユーザーの選択した結果を表示して確認する

やっている事:

  • コメント欄に「あなたは“攻撃”そして“ゴブリン”を選択しました。“KUMO”は次の行動を決定しました。」と表示

f:id:kazuhironagai77:20201122223827p:plain

  • 「読みましたボタン」を表示

f:id:kazuhironagai77:20201122223907p:plain

改善点:

  • 確認するのが目的ならばユーザーが選択を変更出来る方法を用意すべきでは?
  • コメント欄の文「“KUMO”は次の行動を決定しました。」は「“KUMO”は貴方の選択に従って次の行動を決定しました。」の方が正確に今の事態を表しているのでは?

バグ:なし

その他:読みましたボタンを押しました。

2.2.6 モンスターのターン

目的:モンスターのターンです。モンスターがどんな行動をとるのかを決定します。

やっている事:

  • コメント欄に「“ゴブリン”は次の行動を考えています。」「“ゴブリン”は次の行動を決定しました。」と表示します。

f:id:kazuhironagai77:20201122224010p:plain

  • 「読みましたボタン」を表示します。

f:id:kazuhironagai77:20201122224043p:plain

改善点:モンスターのターンですが、モンスターは実際には何も考えていません。攻撃するだけです。この部分をAIに連結させてモンスターも攻撃、魔法、道具、そして逃げるの選択を出来るようにしたいです。

バグ:特にありません。

その他:「読みましたボタン」を押しました。

2.2.7 ユーザーの行動1(操作するキャラのアニメーション表示

目的:ユーザーが操作するキャラの攻撃アニメーションを表示します。

やっている事:

  • カメラの位置が変わり、ユーザーが操作するキャラを写します。
  • キャラは攻撃のモーションをします。

f:id:kazuhironagai77:20201122224136p:plain

改善点:ウィジェットは表示したままの方が見栄えがします。

バグ:なし

その他:なし

2.2.8ユーザーの行動2(結果の表示)

目的:ユーザーの操作するキャラがモンスターを攻撃した事とその結果どうなったかをコメント欄で報告

やっている事:

  • 「“KUMO”は決定した行動を実行しました。」「“KUMO”がゴブリンを攻撃しました。」と攻撃した事をコメント欄で報告
  • 「“ゴブリン”は30のダメ―ジを受けました。」その攻撃でゴブリンがダメージを負った事をコメント欄で報告
  • 「“KUMO”は行動の実行を終了しました。」と攻撃の終了をコメント欄で報告

f:id:kazuhironagai77:20201122224222p:plain

  • 「読みましたボタン」を表示

f:id:kazuhironagai77:20201122224249p:plain

改善点:特にないです。

バグ:ゴブリンにdouble quotation が付いていないのが一つあります。

その他:「読みましたボタン」を押しました

2.2.9 モンスターの行動1(アニメーションの表示)

目的:モンスターの攻撃アニメーションを表示

やっている事:

  • カメラがモンスターを写します。
  • モンスターが攻撃用モーションに従って動きます。こっちはウィジェットが残ったままです。

f:id:kazuhironagai77:20201122224321p:plain

  • コメント欄には「“ゴブリン”は決定した行動を実行しました。」「“KUMO”は10のダメ―ジを受けました。」と表示します。

f:id:kazuhironagai77:20201122224346p:plain

改善点:

  • ユーザーの操作するキャラには、「“KUMO”がゴブリンを攻撃しました。」のコメントがありますが、モンスターにはありません。これはモンスターが攻撃するだけだからでしょう。モンスターにも攻撃だけでなく、魔法やアイテムが使用出来るようにしたいです。
  • ここで、コメント欄には「“ゴブリン”は決定した行動を実行しました。」「“KUMO”は10のダメ―ジを受けました。」と表示するのが良いのか、画面が切り替わった後で、これらの内容を示すのが良いのかを考える必要があります。

バグ:なし

その他:アニメーションが終わるとカメラは元の位置に戻ります。

2.2.10 モンスターの行動2(結果の表示)

目的:モンスターが攻撃した事とその結果をコメント欄で報告します。

やっている事:

  • コメント欄に「“ゴブリン”は行動の実行を終了しました。」と表示します。

f:id:kazuhironagai77:20201122224427p:plain

  • 「読みましたボタン」を表示します。

f:id:kazuhironagai77:20201122224452p:plain

改善点:前節で述べた点と同じです。

バグ:なし

その他:「読みましたボタン」を押しました。

2.2.11 戦闘の繰り返し

目的:戦闘を繰り返します。

やっている事:

  • 上記の3~10を繰り返します。
  • 敵のモンスターのHPが0以下になりました。

f:id:kazuhironagai77:20201122224529p:plain

改善点:

  • HPが0以下で表示されるのはおかしいです。0にしましょう。
  • HPの後の:が文字と被っています。直しましょう。

バグ:なし

その他:なし

2.2.12 戦闘の終了

目的:戦闘の終了を報告します。

やっている事:

  • コメント欄には「“KUMO”は勝利しました。」と表示しました。

f:id:kazuhironagai77:20201122224619p:plain

  • 「読みましたボタン」を表示しました。

f:id:kazuhironagai77:20201122224649p:plain

改善点:実際は、ここでレベルが上がっています。しかしコメント欄にはレベルが上がった事に対して何のコメントも表示されていません。詳しくユーザーに報告するべきです。

f:id:kazuhironagai77:20201122224722p:plain

バグ:なし

その他:「読みましたボタン」を押しました。

2.2.13 勝利のアニメーション

目的:戦闘後のアニメーションを表示

やっている事:

  • ユーザーが操作するキャラが勝利のポーズを取ります。

f:id:kazuhironagai77:20201122224756p:plain

改善点:

  • カメラは後ろから写したままです。カメラの位置はユーザーのキャラを正面から写すべきでしょう。
  • モンスターは倒されているのにその場に立っています。死んだときのアニメーションを追加しましょう。

バグ:なし

その他:なし

2.2.14  戦闘が終わってから元のマップに戻るまで

今の戦闘システムでは、戦闘が終わっても以下に示したように戦闘マップにそのまま残っています。

f:id:kazuhironagai77:20201122224828p:plain

隠れステージにして、回りの像と会話出来るようにします。

目的:隠れステージ

やっている事:今はない

改善点:

  • 有る条件でこのステージに残れるようにする。
  • それぞれの像と会話出来るようにする。

バグ:なし

その他:キーボードのIを押して元のマップに戻ります。

2.2.15  元のマップに戻る

f:id:kazuhironagai77:20201122224859p:plain

今回は確認しませんが以下の点は後で調べる必要があります。

  • アイテムや装備品、金貨そしてキャラのパラメーターなどが、戦闘マップに行く前と変化していないか。
  • 戻った先にいたモンスターは消滅しているか?
  • モンスターを倒して得た財宝を持っているか?

2.3 今の戦闘システムの問題点やバグまとめ

かなり大変でしたが、今の戦闘システムの改善点を抽出出来たと思います。これらを直していきます。

3. 戦闘システムの改善点の内の幾つかを直します。

ハッキリ言って今週直すのはきついです。戦闘システムの改善点は来週以降直していきます。

今週は、先週紹介した大量のインスタンスをマップ内に表示する技術を使用して、戦闘マップに沢山の骸骨を表示しようと思います。

f:id:kazuhironagai77:20201122224953p:plain

3.1動画の勉強

半分位動画を見たら作り方が分かったのでTestGameModeで作成してみました。

Static mesh は1M_Cubeを使用して10000個ほど作成しました。一個一個のcubeに自転を加えて更にHierarchical Instanced Static Mesh にも回転を加えました。

f:id:kazuhironagai77:20201122225017p:plain

f:id:kazuhironagai77:20201122225029p:plain

Static meshを一個かませる事でもっと複雑で迫力のある回転が作成できました。

f:id:kazuhironagai77:20201122225054p:plain

f:id:kazuhironagai77:20201122225105p:plain

4.まとめと感想

今週は、先週改善した戦闘システムの問題点を洗い出すのに時間が取られて、ほとんどプログラミング出来ませんでした。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 戦闘システムを完成する

f:id:kazuhironagai77:20201115202544p:plain

<前文>

面白いカードゲームのコンセプトを思い付きましたのでここに書いておきます。

遊戯王系のカードゲームの弱点って、多人数でワイワイ遊べない事だと思うんです。1対1じゃないとルールが難しすぎて運用出来なくなる。

その原因は、持っているカードが無くなる事が勝ちではないからです。トランプは持ち札が0になったら大抵勝ちです。

そこで私の考えたこのカードゲームでは先に持ち札が0になった時点で勝ちになります。

木、火、水の1から13の番号が振ってあり計39枚のカード。最低限、これらのカードを持っている必要があります。更にカードを追加するのはOKです。ただし最大50枚までとします。勿論しなくてもOKです。

2が一番弱く1が一番強いです。ただし2は1にだけは勝ちます。ただし木のカードは火に弱く火のカードは木のカードに対してナンバー+3の強さになります。ただし1と2にはこの原則は通用しません。火と水、水と木の関係も同じです。相手が出したカードより強いカードを出す必要があります。出したカードは廃棄場に行きます。

それぞれのカードには特殊な能力が付随している場合があります。例えば革命、とかスキップ、廃棄場のカードを5枚元の札に戻す。みたいなのです。特別なカードは雷タイプとか、相手のカードから13を捨てさせる魔法カードとか考えられるかもしれません。

このカードゲームだと多人数で遊べます。勿論2人でも遊べます。

うーん。あまりゲームをしない私個人の感想では大変面白そうですし、blockchainの技術と結び付けたら色々な意味でもっと面白くなりそうでもあります。例えば世界で5枚しかないカードとかも作れるかもしれません。

このRPGの作成が終わったら今度はカードゲームを作ってみますか。

それでは今週の勉強を始めます。

<本文>

今週は戦闘システムを完成させます。

正しその前に先週やり残したことを直します。

  1. 宿屋に泊まった時に5秒位のラグを作成して寝た感じを出す。
  2. 起きた時は宿屋で起きる。
  3. Event dispatcherについて調べる。特にレベルBPウィジェットBP間で連絡できるかどうか調べる。

戦闘システムで大きな箇所でまだ作成していない箇所は

  1. 戦闘開始時にワープして戦闘終了時に元の箇所にワープして戻ってくる
  2. 敵のモンスターの配置とアニメーション

の二つです。更に敵のモンスターのアニメーションに合わせてコメントが必要になるはずです。

これらをやって行きます。

1.先週の直し

1.1 宿屋に泊まった時に5秒位のラグを作成して寝た感じを出す

以下のイメージを5秒間表示しました。

f:id:kazuhironagai77:20201115202702p:plain

1.2 起きた時は宿屋で起きる。

これは最初から出来ていました。場所が変わるのは別なマップに移動して戻ってくる時でした。

1.3 Event dispatcherについて調べる。特にレベルBPウィジェットBP間で連絡できるかどうか調べる。

まず、Event dispatcherがcustom eventとどう違うかが分からないです。一般的な使用方法を調べます。その上でレベルBPとウィジェットBP間での連絡の取り合いが出来るのか調べます。

大体分かりました。

Third Person Character BP内にEvent Dispatchersを作成しました。

f:id:kazuhironagai77:20201115202733p:plain

このEvent Dispatcherはcall, bindの機能があります。他にもありますがこれだけ知っていれば良いです。

まず、Callですが、

f:id:kazuhironagai77:20201115202752p:plain

手紙の絵がついています。手紙と言うよりは電話です。手紙は好きな時に開ければいいですが、電話は直ぐに出なければなりません。ではこのdispatcherはどこに電話をかけているのでしょうか?

f:id:kazuhironagai77:20201115202817p:plain

そうです。Bindした先のeventです。

こうやってevent dispatcherはBP間でeventのやり取りをします。

しかしここで問題があります。上記の例ではwidget BPとLevel BPの両方からアクセス出来るThirdPersonCharacterクラスにevent dispatcherを作成したのでwidget BPとLevel BP間で連絡とれましたが、level BPにevent dispatcherを作成した場合はどうなるのでしょうか?どのBPからもアクセスする事が出来なくなりますよね。

f:id:kazuhironagai77:20201115202856p:plain

しかし普通にLevel BPにもEvent Dispatcherがあるんです。

うーん。この辺がまだ疑問ですね。

1.4 Event Dispatcherを使用して宿屋に泊まった時に朝になるようにする。

RPGGameInstanceBP内にevent Dispatchers、SetTimerSpend0を作成します。

f:id:kazuhironagai77:20201115202932p:plain

Widget、Inn_WelcomeBPで泊まるボタンを押した時にSetTimerSpend0がcallされるようにします。

f:id:kazuhironagai77:20201115202951p:plain

LevelBPのEvent BeginPlayでSetTimerSpend0をEvent Time SpendにBindします。

f:id:kazuhironagai77:20201115203010p:plain

これでInn_WelcomeウィジェットのBP 内でSetTimerSpend0がcallされる度にLevelBP内でEvent Time Spendが実行されるはずです。

テストします。

f:id:kazuhironagai77:20201115203029p:plain

泊まった後は朝に成りました。出来ました。

完璧とは言えませんが前回の方法よりはコストが低くなっているはずです。

2. 戦闘システムにワープ機能を追加する

2.1 ワープ先のマップを作成する。

Infinity Blade:Propsをプロジェクトに追加しました。

f:id:kazuhironagai77:20201115203116p:plain

Infinity Blade:Propsから以下のPropsをコピーして、

f:id:kazuhironagai77:20201115203137p:plain

以下に示したマップを作成しました。

f:id:kazuhironagai77:20201115203156p:plain

更に、GoodSkyのBPを追加して天気を嵐にしました。

f:id:kazuhironagai77:20201115203216p:plain

実際のplay画面です。

f:id:kazuhironagai77:20201115203236p:plain

今必要な事は戦闘になった時にワープするための別のmapで、そのmapにデザインの良さや完成度は必要ありません。しかしこの2~3週間レベルデザインの勉強に費やしたので、アートとしてもマシなデザインを少しだけ心がけました。

2.2 プログラミングスキルを持つデザイナー

以下のマップは、私が最初にInfinity Blade:Propsを使用しないで作成したmapです。

f:id:kazuhironagai77:20201115203314p:plain

f:id:kazuhironagai77:20201115203322p:plain

デザイン的に満足出来ないので没にしました。

ここで気が付いたんですが、デザイナーが望むデザインをUE4内で作成するためには、結局デザイナーもプログラミングスキルをマスターする必要があると言う事でした。

例えばこの動画UE4 Optimization: Instancing)にあるように

f:id:kazuhironagai77:20201115203344p:plain

非常に沢山の隕石をmap上に配置したいとして単にstatic mesh actorを沢山配置していったらクラッシュします。もしデザイナーがこのような3D空間を再現したいならば、きちんとしたプログラミングの技術が必要になります。

今まで何となくですが、学校の勉強と芸術の能力は相反するので優れたデザイナーは英語や数学は出来ない。と言うイメージで居たのですが、実際は作りたい芸術が最初にあってそれを実現するためにプログラミングも学習すると言う、学校の勉強が出来るデザイナーも必要みたいですね。

ただ本格的にプログラミングするためには、どうしても理系の大学数学を理解していないと出来ませんが、そういうのってデザイナーはどこで勉強するんでしょうか?国立の美大とかはそういうのも勉強するんでしょうか?入試に数学があるんですかね?それとも元々理系の院卒の人で、どうしてもゲームが作りたい人がゲーム制作の専門学校なんかでデザインを勉強するんでしょうか?

2.3 ワープして戻ってくる。

結局、戦闘用のマップは以下の様になりました。

f:id:kazuhironagai77:20201115203409p:plain

f:id:kazuhironagai77:20201115203417p:plain

もう少し時間を掛ければもっと良くなりそうです。

ワープ機能を作成します。

RPGGameInstanceBPにGameCharacterの位置を保持する変数、

f:id:kazuhironagai77:20201115203442p:plain

を作成します。Defaultの値はmap1のThirdPersonCharacterの配置している場所を入れておきます。

f:id:kazuhironagai77:20201115203518p:plain

Map1のBPに以下のコードを実装しました。別なマップに移動する前にGameCharacterの位置をRPGGameInstanceBPのThirdPersonCharacterにセットしておきます。

f:id:kazuhironagai77:20201115203549p:plain

これでIをクリックしたら別のマップであるBattleFieldに移動するはずです。

BattleFieldのBPでは以下のコードを実装しました。

f:id:kazuhironagai77:20201115203608p:plain

Iを押すと元のマップに戻ります。

Map1に戻ると、EventBeginPlayが発生するので、その時に前にRPGGameInstanceBPのThirdPersonCharacterにセットした値を、GameCharacterのRootComponentの位置にセットします。

f:id:kazuhironagai77:20201115203627p:plain

これで出来るはずです。テストします。

分かり易い様に位置を移動してテストします。

f:id:kazuhironagai77:20201115203649p:plain

戦闘空間に移動しました。

f:id:kazuhironagai77:20201115203708p:plain

戻りました。前と同じ位置に戻っています。

f:id:kazuhironagai77:20201115203727p:plain

時刻がズレているのはここで記事を書いた後でスクリーンショットを取ったからです。

出来ましたね。

3. 敵のモンスターの追加

3.1 ワープ先でTestCombat()関数を呼びだせるようにする

この部分を作る事をすっかり忘れていました。

RPGGameModeBaseクラスの関数、testCombat()のspecifierをexecからBlueprintCallableに変更します。

f:id:kazuhironagai77:20201115203807p:plain

BattleFieldのレベルBP内のEventBeginPlayでRPGGameModeBaseクラスのtestCombat()関数を呼び出します。

f:id:kazuhironagai77:20201115203826p:plain

これで、BattleFieldに移った瞬間に戦闘が開始するはずです。

テストします。

f:id:kazuhironagai77:20201115203847p:plain

開始しました。

その後の戦闘も特に問題なく出来ました。

3.2 敵のモンスターの追加

それでは敵のモンスターを表示します。

3.2.1 CombatEngine内ではどのように敵のモンスターは作成されそのデータは保持されているのか調べる

登場するモンスターは一種類ではないので、色々なモンスターを登場させる必要があります。CombatEngine内でどのように色々なモンスターの登場を管理しているのかを確認します。

RPGGameModeBaseクラスのTestCombat()関数内のモンスターの作成箇所をみると

f:id:kazuhironagai77:20201115203922p:plain

色々なモンスターの登場は管理していませんでした。このやり方だと一種類のモンスターしか登場させる事は出来ません。

ウーン。今回は適当に出現させても良いかもしれませんね。

因みにデータテーブル、EnemyMonstersの内容は以下の様になっています。

f:id:kazuhironagai77:20201115203941p:plain

その後で上記で作成したRow変数をGameCharacterクラスのCreateEnemyMonster()関数にパスしてモンスターは作成されています。

f:id:kazuhironagai77:20201115204002p:plain

因みに、ユーザーが操作するGameCharacterがRPGGameInsatanceクラスで管理されているのと違い、モンスターを管理する変数EnemyMonsterはRPGGameModeBaseクラスにあります。

3.2.2 敵のモンスターの3Dはどのように作成されるのか調べる。

今度は敵のモンスターの3D イメージがどのように作成されるかを確認します。ずっと前に作成した奴です。

f:id:kazuhironagai77:20201115204030p:plain

ゲームを開始すると現れます。どうやって作成したのかはもう全く覚えていません。

確か、RPGGameModeBaseクラスでモンスターの3Dイメージの出現は実装したはずなのでRPGGameModeBaseクラスをまず見てみます。

まずヘッダーファイルから見ます。

f:id:kazuhironagai77:20201115204048p:plain

Ach4_3Monsterクラスって何?

全く覚えていません。Aが付いているってことはactorクラスの派生クラス何でしょうか?

f:id:kazuhironagai77:20201115204118p:plain

Characterクラスから派生させていました。

f:id:kazuhironagai77:20201115204136p:plain

コードを見ましたが特に追加している部分はありません。後で、Characterクラスに無い機能を追加する時のために、念のために作成したのかもしれません。

ここではCharacterクラスと同じとみなして読んでいきます。

モンスターの3Dイメージの出現は実装は、BeginPlay()関数内で実装されていました。

f:id:kazuhironagai77:20201115204156p:plain

思い出してきました。この実装部分、公式のUE4C++のチュートリアルに載っていたやり方を参考にして作成したんでした。

このやり方だと、発生させるモンスターをBP側でコントロール出来るので違うモンスターを選ぶ事が簡単に出来そうです。

f:id:kazuhironagai77:20201115204215p:plain

正し、問題もあって、BeginPlay()関数を以下に示したような形でoverrideしているせいなのか

f:id:kazuhironagai77:20201115204235p:plain

BP側のBeginPlay()関数は機能しなくなっています。

f:id:kazuhironagai77:20201115204256p:plain

この問題が何故起きるのか、どうにかすれば回避できるのか、又回避すべきなのか、全然分かりません。後で勉強します。

色々課題はありますが、モンスターの3Dイメージの出現方法はこれで分かりました。

3.2.3 敵のモンスターの3Dを出現させる。

まず、モンスターを出現させます。

と言うか前のコードをみれば新しいmapが生成された時に、既に何処かにはモンスターは出現しているのは明白なので

f:id:kazuhironagai77:20201115204322p:plain

その出現する位置を調節します。

GameCharacterの位置から推測して以下の場所に設定しました。

f:id:kazuhironagai77:20201115204343p:plain

テストします。

f:id:kazuhironagai77:20201115204403p:plain

向きが横を向いています。

Yawの値を90.0fに変えました。

f:id:kazuhironagai77:20201115204425p:plain

テストします。

f:id:kazuhironagai77:20201115204446p:plain

直りました。

正しこの方法だと、Map1に出現するモンスターの位置も変わってしまいます。

f:id:kazuhironagai77:20201115204506p:plain

これだけ直します。

3.2.4 敵のモンスターの3Dの出現位置をmap毎に変える。

最近、GetMapName()関数を知ったので試しに使用してみようと思います。

色々試して以下の方法で実装しました。

f:id:kazuhironagai77:20201115204540p:plain

まず、GetMapNameで帰ってくるマップの名前は、

f:id:kazuhironagai77:20201115204601p:plain

マップの名前+実行しているPlayの種類?に成っているのでcontainsで調べます。

BattleField(戦闘用のマップ)以外のマップではモンスターは今までの指定された位置に出現します。

f:id:kazuhironagai77:20201115204627p:plain

戦闘画面では別な位置に出現しています。

f:id:kazuhironagai77:20201115204650p:plain

後で調べたらこのサイトにGetMapName()関数の詳しい使い方が載っていました。

以下の方法で、マップの名前に付いているPrefixを外せるそうです。

f:id:kazuhironagai77:20201115204715p:plain

4. 敵のモンスターのアニメーションの追加

4.1 アニメーションの復習

もうどうやってアニメーションを作成したのか覚えていません。復習から始めます。

PlayAnimationでAttack01_BowAnimをplayしていました。

f:id:kazuhironagai77:20201115204750p:plain

このアニメーションを表示出来る様にします。

f:id:kazuhironagai77:20201115204808p:plain

f:id:kazuhironagai77:20201115204816p:plain

以下に示したようなコードを追加しました。

f:id:kazuhironagai77:20201115204834p:plain

f:id:kazuhironagai77:20201115204843p:plain

テストします。

はい。戦闘中の攻撃のアニメーションが追加されました。

f:id:kazuhironagai77:20201115204902p:plain

カメラワークを作っていないので画面は小さいですがしっかり攻撃しています。

4.2 カメラモーションの追加

前にカメラを沢山つけて映画のマトリックスみたいな映像を流す技術が紹介されている動画を見た記憶があります。

探しましたが見つかりませんでした。しょうがないので今回はモンスター攻撃時に、モンスター用のカメラに移動するだけにします。

MonsterBPを見たら、カメラ自体がなかったです。

付けました。

f:id:kazuhironagai77:20201115204932p:plain

f:id:kazuhironagai77:20201115204940p:plain

RPGGameModeBaseクラスのBP内で以下に示したようにモンスターの攻撃アニメーション時にMonsterBPのカメラに切り替わるようにします。

f:id:kazuhironagai77:20201115205003p:plain

当然ですが、攻撃が終わったら、映像を写すカメラは元のカメラに戻します。

f:id:kazuhironagai77:20201115205030p:plain

テストします。

f:id:kazuhironagai77:20201115205050p:plain

あれ、滅茶苦茶カッコイイです。

5. まとめと感想

一応、今週の予定してた部分は終わりました。戦闘システムには色々直さなくてはいけない部分が沢山ありますが、来週以降直します。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 村の地面をデコボコにする

f:id:kazuhironagai77:20201108221555p:plain

<前文>

HxHの念タイプがプログラマーにも良く当てはまる件について

HxHの念タイプは単なる空想の産物ですが、ソフトウェアエンジニアが受ける性格診断テストであるPersonality Types診断より真実を表してる気がします。知らない人にHxHの念タイプを簡単に説明すると以下の6つのタイプがあります。

  1. 強化系:兎に角、(念を)強化する。6種類の中で最も使い勝手が良いタイプ。当り。
  2. 変化系:元にあるものを変化させる。(念から電気に変化など。)
  3. 具現化系:(念から)新しい物を作り出す。
  4. 特質系:他のどれにも属さない。ガチャで言う激レア。他のタイプを超越した能力。
  5. 操作系:(念を)操作するのが得意。
  6. 放出系:(念を)放出するのが得意。

更に、これらのタイプ同士はお互いに関連していて、番号が隣り合っているタイプ(6は1と隣り合っている。)とは仲良しになりやすいとか。隣り合っている能力は(特質系を除き)それなりに習得できると紹介されています。特質系はこの中で最も優れていますが数は非常に少ないです。しかし特質系の隣りである操作系や放出系は、後天的に特質系になったりする。などの解説があります。

これを実際の職業や性格に当てはめてた場合、変な心理診断なんかより正しい診断が出来る気がしています。

例えば強化系ですが、普段はあまり練習や勉強をしないのに試合前や試験前は滅茶苦茶、集中して練習や勉強をするタイプの人がいます。自分の能力をその時だけ強化している訳です。これらのタイプの人は試合やテストの結果で、実力以上の成果を出す人が多いです。だから実社会でもある程度は成功しているはずです。つまりアタリのタイプです。

プログラマーに例えると「明日までにバグを直さなくてはならない。今日は徹夜。」みたいな状況で凄い力を発揮するタイプです。後、普段からある分野の勉強が好きでその分野に関しては誰よりも知っていると言うタイプの人も強化系だと思います。知識を強化している訳ですから。一緒に仕事していると一番頼りがいがあるタイプの人で、やっぱりアタリのタイプです。

変化系は、GitHub何かから既存のコードをコピペしてちょっとだけ変更して対応するタイプです。強化系の人から見ると勉強不足に見られる場合もあるかもしれませんが、実際それで動いてしまうんでプログラムに対しての見解の相違なんでしょうね。怠けるために努力するタイプとか、優秀だがサボる癖があるとか、言われる人に多いタイプです。

放出系は(知識を)放出するのが得意なタイプです。強化系の人が10知っているとその内の1しか話さないのに対して、放出系の人は1しか知らなくても10くらい話します。人に教える事で自分も上達するタイプでもあります。このタイプは講師や技術営業向きで、教えている内容が強化系の人から見るとちょっと間違えているように見えても、生徒や部外者からすると10倍くらい分かり易いので大変有り難い存在です。ただしHxHで行っている通り(感情を)放出するのも得意なので短気な人が多いです。

放出系の人は放出するための知識、変化系は変化元の知識が必要なので、強化系の人と仲良くする必要があります。一方で強化系の人にとって変化系の人は自分の成果を1.5倍にしてくれますし、放出系の人は自分の成果を10倍にも20倍にも拡大して宣伝してくれるので組むメリットは十分にあります。ので大抵、この3タイプはつるんでいます。これもHxHに書かれている通りです。

具現化系は習った知識で直ぐに何か作るタイプです。Linux kernelを作成したLinus Torvaldsや、アップルの創設者であるSteve Wozniakがこのタイプです。日本で言えば、世界で最初のCPUを作成した人とか、MMDを作った人、更にrubyを作った人なんかが具現化系タイプだと思います。そこまで有名な人でなくても休みの日にアプリを開発したり、サーバーを自作したりWeb siteを作成する人は具現化系です。

このタイプ、残念ながら一番外れです。能力に対して一番割食っているタイプです。テストなどの成績は強化系には勝てません。放出系ほどプレゼンも上手くありません。更に変化系ほど楽も出来ません。後述しますが、操作系はプログラマーの世界ではかなりの勝ち組ですので唯一の外れタイプです。

操作系は、ずばりハッカーです。彼らは他人のPCや企業のサーバーに侵入して自分の好きな様に操作します。多人数で遊ぶゲームでCheatingのツールを使用して自分だけ特別な操作が出来る様にしたりとか、ハッキングで得た大量の他人のIPからbotを使用してDDos攻撃をしたりとか、そこまでいかなくてもSpamを送ったり、兎に角、他人の資産を操作して自分のために使用する技術に長けています。このタイプ、具現化系と違いどんなにプログラミングに通じていても自分で何かを作成しようとは思いません。他人の作った物をどうやってコントロールする事しか考えません。

私がアメリカにいた時のルームメートの一人がまさにこのタイプでした。彼は電気工学の博士課程の生徒でCPU関連の研究をしていたんですが、ゲームが大好きで好きなゲームが発売されると勉強や研究そっちのけで一日中ゲームしているんです。そんなにゲームに人生を捧げている彼でも、敗北の時は来ます。そうなると彼は直ぐにCheatingのツールを制作してズルして戦うんです。そんなにゲームが好きならゲームそのものを作成すれば良いと思うんですが、ゲームを自分で作る事には興味が全くないんです。ゲームの中で勝つ事に極振りしているんです。

特質系とはプログラマー界のセレブや大金持ちになった人たちを指すと考えます。見事に具現化系と操作系からしか特質系になっていません。例えばビルゲイツ氏やスティーブジョブ氏。一見具現化系のようですが、彼らの経歴を知ると実際は操作系ですね。一方、Googleの創設者やIBMの創設者はその経歴を知ると完全に具現化系です。

こんな感じでHxHの念タイプによる分析は結構当たると個人的には思っています。

それでは今週も勉強を始めます。

<本文>

f:id:kazuhironagai77:20201108221734p:plain

今週はこの中の「実際のゲーム内の町の地面をデコボコにする。」を行います。

10月4日のブログに書かれている通りLandScapeを使用して地面をデコボコに出来る事は確認しました。その時は以下の示した条件で作成しましたが、この条件が適切なのか全く分かりません。その辺から調べます。

f:id:kazuhironagai77:20201108221758p:plain

そしたら。実際のプロジェクト内の町にLandScapeを追加して町の地面をデコボコにします。

1. LandScapeの条件の確認と最適化

1.1 付属のSampleの数値を調べる

Medieval KingdomのサンプルMap1を調べて見ます。

f:id:kazuhironagai77:20201108221843p:plain

デコボコな地面はGround_Map_1で作成されています。

f:id:kazuhironagai77:20201108221900p:plain

Ground_Map_1はStaticMeshActorで作成されていました。

因みにデータのサイズは566KBです。

f:id:kazuhironagai77:20201108221920p:plain

うーん。

Landscapeで作成された場合、Landscapeと表示されています。

f:id:kazuhironagai77:20201108221942p:plain

f:id:kazuhironagai77:20201108221951p:plain

因みにLandscapeのデータのサイズは以下に示したように、大体9000KBでした。

f:id:kazuhironagai77:20201108222011p:plain

データのサイズが10以上違います。

2つの地面の大きさが全然違う可能性もあるので並べて比較してみました。

f:id:kazuhironagai77:20201108222048p:plain

SM_Ground_Map1から作成されたインスタンスの上を歩いて見ましたがLandscapeと比べて特に劣っていると感じる点はありませんでした。

f:id:kazuhironagai77:20201108222115p:plain

dataのサイズが十倍以上違うのは問題です。

Blenderか何かでSM_Ground_Map1のようなものを作成してimportすべきでしょうか?

うーん。

f:id:kazuhironagai77:20201108222139p:plain

Blenderで作成するとなると今週の予定から大きく外れますね。更にBlenderの使用方法の勉強もしないといけません。今はBlenderUE4を同期してBlenderでデザインしたのを即UE4で見れるようになっているらしいです。

先にlandscapeについて調べてみます。

あ。これってLevel全体のデータ量でした。

f:id:kazuhironagai77:20201108222159p:plain

Landscapeだけのデータ量は分かりませんね。

まず。Landscapeを作成する時に決定するparametersたちが実際の何を表しているのかを確認します。

f:id:kazuhironagai77:20201108222221p:plain

Section sizeが7x7 Quadsですが、以下に示した黄色の枠で覆った正方形の枠が7x7で表示されています。

f:id:kazuhironagai77:20201108222241p:plain

今度はSection sizeを15 x 15 Quadsに変更してみます。

f:id:kazuhironagai77:20201108222304p:plain

何と、黄色の枠で覆った正方形の枠は7x7で変化しませんでした。しかしその正方形内に存在する四角の数が

f:id:kazuhironagai77:20201108222322p:plain

15 x 15 Quadsに変化していました。

f:id:kazuhironagai77:20201108222349p:plain

ただし、この最小単位の正方形の大きさは一緒で、

f:id:kazuhironagai77:20201108222415p:plain

f:id:kazuhironagai77:20201108222423p:plain

結果としてLandscape全体のサイズが大きくなりました。

これらの事から推測するとSectionとは

f:id:kazuhironagai77:20201108222443p:plain

この黄色で囲った正方形の部分を指しています。

今度の   parameterはSection Per Componentです。

f:id:kazuhironagai77:20201108222507p:plain

を2x2に変更しました。

f:id:kazuhironagai77:20201108222525p:plain

f:id:kazuhironagai77:20201108222533p:plain

f:id:kazuhironagai77:20201108222551p:plain

に変化しました。4つのsectionが一個のcomponentに入っている状態です。

よし、これで分かりました。

まずLandscapeの最小単位の正方形のサイズは、どんなにparameterを変更しても同じです。変わりません。

次に、その最小単位の正方形を束ねる2つの単位があります。SectionとComponentです。

f:id:kazuhironagai77:20201108222626p:plain

通常1対1で設定されているのでSectionとComponentは同じですが、SectionはComponentの内側に存在しています。

一個一個のsectionのサイズはSection sizeで決定されます。

f:id:kazuhironagai77:20201108222644p:plain

一個一個の最小単位の正方形、Quadsのサイズは同じなので数が多くなるほどSectionのサイズは大きくなります。

最後にComponentの数を決定出来ます。

f:id:kazuhironagai77:20201108222708p:plain

これでLandScapeの大きさは決定されます。

はい。ここで疑問です。

Section/componentは1x1 sectionで固定したと仮定します。

Landscapeの大きさは同じで、sectionのサイズを大きくしてcomponentの数を小さくした場合と、sectionのサイズは小さくしてcomponentの数を大きくした場合はどちらがデータが小さくなるのでしょうか?

このサイトにComponentについての説明がありました。

更にお勧めの設定が載っていました。

f:id:kazuhironagai77:20201108222735p:plain

このページのお勧めの設定の中でmapの地面のサイズに最も近い奴を選択すると

f:id:kazuhironagai77:20201108222756p:plain

この二つになります。下の設定を使用しました。

f:id:kazuhironagai77:20201108222818p:plain

すこし大きいですね。

f:id:kazuhironagai77:20201108222853p:plain

こんな感じです。

でもこれでLandscapeのparameterの最適解は分かりました。

後は、Landscapeのデータのサイズが分かれば最高なんですがどこにLandscapeのデータを保持しているのかが分かりません。

ただ分からないですが、Landscapeのデータのサイズは凄い小さい気がします。ので今回はLandscapeでやる事にします。

2. LandScapeを追加して村の地面をデコボコにする

それでは実際に作成します。

f:id:kazuhironagai77:20201108222930p:plain

f:id:kazuhironagai77:20201108222937p:plain

で作成します。

f:id:kazuhironagai77:20201108222957p:plain

f:id:kazuhironagai77:20201108223007p:plain

こんな感じになりました。

中を見るとこんな感じです。

f:id:kazuhironagai77:20201108223026p:plain

f:id:kazuhironagai77:20201108223034p:plain

f:id:kazuhironagai77:20201108223044p:plain

大分、真っ平ら感が無くなりました。

うーん。もっと難しいと思っていたのですが簡単に出来ました。

3. 村の制作のTutorialをみて勉強する

3.1 Fantasy village: Unreal Engine 4 - Landscape - UE4 - Environment Design

このサイトのビデオです。

20分程度見ましたが特に勉強になる事はありませんでした。Landscapeのデザインと言っていますが実際は、landscapeは既に完成していてその上にレベルデザインをしているだけです。そのレベルデザインの手法も知らないようなテクニックは使用されていませんでした。

1~2年前にLevel deignにチャレンジした時は、滅茶苦茶なマップしか作れませんでしたが、その間に特に練習した訳ではありませんが、今回はかなり綺麗なdesignが出来ました。

それで率直な感想ですが、Level design自体にはそんなに勉強する事ないんじゃないかなと思いました。

にも関わらずLevel designをする人が少ないのは気になります。作成した作品に対して著作権が付きにくいなどの制作意欲を削ぐ問題があるのかもしれません。

これで今週の予定であるLandscapeの勉強は終りですが、時間が大幅に余ってしまったので、次の課題である昼と夜の作成の続きをやります。

4. 昼と夜の追加の続き

10月4日のブログで昼と夜をGood Skyを使用して作成しましたが、一端時間を指定したら途中で変化させる事は出来なくなりました。Good Skyの機能をもっと良く調べて昼と夜を自由に変化させたいと思います。

4.1 Good Skyの復習

Good Sky のSky Beta項目に以下に示したような設定でセットすると自動で昼と夜になります。

f:id:kazuhironagai77:20201108223145p:plain

こんな感じです。

f:id:kazuhironagai77:20201108223207p:plain

f:id:kazuhironagai77:20201108223213p:plain

f:id:kazuhironagai77:20201108223222p:plain

しかし自主的に昼と夜を変える事は出来ませんでした。

4.2 別な方法で昼と夜を自主的に変える。

色々試した結果。以下の2つの事が判明しました。

4.2.1 判明した事実

1. “Enable Auto Day/Night Cycle in Game?”にチェックを入れると完全にオート化してしまいます。

f:id:kazuhironagai77:20201108223304p:plain

以下に示した方法で、play中にEnable Auto Day/Night Cycle in Game?をoffにセットしても昼と夜は自動で変化し続けます。

f:id:kazuhironagai77:20201108223357p:plain

2. “Enable Auto Day/Night Cycle in Game?” のチェックを外した状態(以下に示したような設定の場合)Directional Light Actorにセットしたdirectional lightのY軸の値を変化させる事で昼夜を変化させる事が出来ます。

f:id:kazuhironagai77:20201108223432p:plain

以下の方法で、Directional lightのY軸の角度を50度に変更します。

f:id:kazuhironagai77:20201108223451p:plain

f:id:kazuhironagai77:20201108223500p:plain

こんな感じになります。

f:id:kazuhironagai77:20201108223520p:plain

が、

f:id:kazuhironagai77:20201108223538p:plain

です。

4.2.2 昼と夜の実装

これらの事実を利用して以下のコードを実装しました。

f:id:kazuhironagai77:20201108223603p:plain

はい。   “Enable Auto Day/Night Cycle in Game?” のチェックを外した状態で、昼と夜を自動で表現出来る様になりました。

f:id:kazuhironagai77:20201108223633p:plain

f:id:kazuhironagai77:20201108223641p:plain

f:id:kazuhironagai77:20201108223649p:plain

f:id:kazuhironagai77:20201108223657p:plain

4.3 宿屋に泊まって寝た後は必ず朝になる

当然な事です。しかし“Enable Auto Day/Night Cycle in Game?” のチェックが入った状態だと、外部の操作を全く受け付けないのでこれが実現出来ませんでした。しかし4.2.2で制作した方法なら単にTimeSpend変数の値を0にセットし直すだけで朝に戻るはずです。

以下のコードで試してみます。

f:id:kazuhironagai77:20201108223722p:plain

夜になるのを待ってIを押します。

f:id:kazuhironagai77:20201108223745p:plain

朝になりました。

f:id:kazuhironagai77:20201108223806p:plain

4.4 戦闘時に別なmapに飛んで戦闘終了後に戻ってくる時、時間は止まっているべき

まだ作成していませんが、戦闘時は別なmapに飛んで戦う予定です。戦闘終了後に元のmapに戻ってきたら、何もしなければ必ず朝になってしまいます。これの対策をします。

別なmapに移れば、GameInstanceクラス以外で保持しているデータは全て消えてしまうので、Directional lightのY軸の角度はGameInstanceクラスで保持する必要があります。

GameInstanceクラスにFloatタイプの変数、DirectionalLightAngleYを作成します。

f:id:kazuhironagai77:20201108223835p:plain

新しいmapを開く前に、TimeSpend変数の値をGameInstanceクラスの変数、DirectionalLightAngleYにセットします。

f:id:kazuhironagai77:20201108223856p:plain

もう一度、mapを開く時はTimeSpend変数の値はGameInstanceのDirectionalLightAngleY変数からコピーします。

f:id:kazuhironagai77:20201108223916p:plain

こんな感じで時刻を保持して別なmapに移動して戦闘し、戦闘終了後にその時刻に戻ってくる事が出来るはずです。

f:id:kazuhironagai77:20201108223936p:plain

Uを押して別なマップに移動して戻ってきます。

f:id:kazuhironagai77:20201108223956p:plain

同じ時刻に戻ってきました。

4.5 時刻の管理にFrame rateに頼っている件について

全部上手く行っているように見えますが、実はこのやり方、Tick()関数のDelta secondが0.008333秒である事を前提に定数6を掛けています。

f:id:kazuhironagai77:20201108224020p:plain

一日を3時間程度に設定しようと思っているのですがTick()関数のDelta secondが変わるとこの定数である6.0も変える必要がある気がしています。これについて検証します。

上記の計算と全く同じ計算をエクセル上で再現してDelta Secondsが1秒の場合と0.5秒の場合の0から60秒における値の変化を計算しました。

f:id:kazuhironagai77:20201108224039p:plain

全く変わりませんでした。

この結果から推測するに定数の6.0はDelta Secondsが変わっても変える必要はないみたいです。

確かにこの結果を見て考えるとFrame rateが大きくなれば、Delta Secondsは小さくなるので結果は同じになってもオカシクはないです。

うーん。1時間位、この問題について考えていました。結果的にはこの問題は杞憂でした。考える必要はなかったですね。

5. 昼と夜をプロジェクトに追加する

Ch4_3にgood skyを追加して昼夜を作成します。

だだし。Ch4_3にはレベルが複数あり、それぞれにgood sky BPを追加する必要があります。その辺りをどのように実現するか考えなければなりません。

5.1 前節で作成したのと機能をCh4_3のmap1に作成します。

取りあえずは、前節で作成したのと全く同じ物をmap1に作成します。

何故か赤いです。

f:id:kazuhironagai77:20201108224119p:plain

滅茶苦茶調べました。

SkyLightのMobilityをStaticに変更したら直りました。

f:id:kazuhironagai77:20201108224137p:plain

f:id:kazuhironagai77:20201108224147p:plain

理由は分かりません。

f:id:kazuhironagai77:20201108224212p:plain

f:id:kazuhironagai77:20201108224218p:plain

f:id:kazuhironagai77:20201108224227p:plain

f:id:kazuhironagai77:20201108224236p:plain

全体的に暗いです。

Auto Exposureの設定を元に戻しました。

f:id:kazuhironagai77:20201108224305p:plain

これはいい感じになりました。

f:id:kazuhironagai77:20201108224325p:plain

5.2 宿屋に泊まったら朝になるようにセットします。

これ簡単だと思っていたんですが、何とLevel BPの変数は他のBPから呼び出す事は出来ないんだそうです。仕方ないので以下の様にしました。

宿屋のWidgetであるInn_Welcomeで泊まるボタンをクリックした時に、以下のコードが実行されます。

f:id:kazuhironagai77:20201108224351p:plain

そしてGameInstanceクラスのその変数をLevelBPでFrame毎にチェックします。

f:id:kazuhironagai77:20201108224409p:plain

Trueだった場合はLevelBPのTimeSpend変数の値を0にセットします。

f:id:kazuhironagai77:20201108224427p:plain

テスト結果を示します。

夜に宿屋に泊まります。

f:id:kazuhironagai77:20201108224446p:plain

朝になりました。

f:id:kazuhironagai77:20201108224503p:plain

一瞬で朝になるので、BGMの変化があるいは、5秒間位の睡眠中のアニメーションを追加する必要がありそうです。後でやります。

出来ました。出来ましたがFrame毎にGameInstanceクラスの変数の値をチェックすると言うかなりコストの掛かる事をしなければならなくなりました。Event dispatcherを利用したらLevelBPと他のBPの連絡も取れやすく成ったりしないんでしょうか?これも後で調べて見ます。

5.3 戦闘後に元のmapに戻っても時間は一緒にする

前節で説明した通り、戦闘時は別なmapに飛んで戦う予定です。戦闘終了後に元のmapに戻ってきたら必ず朝になってしまいます。これを直します。

やり方は前節と全く同じです。Iを押したら、別なmapに移動します。その時GameInstanceクラスのTimeSpend変数に今のLevelBPのTimeSpend変数の値をコピーします。

f:id:kazuhironagai77:20201108224535p:plain

そしてもう一度、このmapを開くときは、さっきとは逆にLevelBPのTimeSpend変数にGameInstanceクラスのTimeSpend変数の値をコピーします。

f:id:kazuhironagai77:20201108224650p:plain

テストします。

f:id:kazuhironagai77:20201108224715p:plain

戻って来た時も時刻は同じです。ただし場所は変わっています。これは後で直します。

f:id:kazuhironagai77:20201108224734p:plain

 6. まとめと感想

今週はここまでです。最も大変と思っていたLandscapeの箇所が簡単だったので来週やる予定だったGoodSkyを使用して昼夜を再現する部分もやりました。来週は戦闘システムの改良を行います。