UE4の勉強記録

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

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 新しい機能を追加する3

f:id:kazuhironagai77:20200906193020p:plain

<前文>

Vtuberについて2

しかしvtuberというのは凄い発明ですね。幾ら見ても飽きないです。びっくりしたのはボイスチェンジャーを使用すればおじさんですら美少女vtuberに成れる事です。個人的にはGoogle検索エンジンに匹敵する発明だと思いました。MMDを初めて見た時も思いましたが、日本文化は他に類を見ない発想とそれを実現する科学技術を持つ稀有なもののようです。

こういう事は誇っていいと思います。いつも日本凄いと言っているネトウヨと言われる人達は、こういう事は決して褒めないですね。何でしょう。日本文化が他に類を見ない発想とそれを実現する科学技術を持つ稀有な文化だと嫌なんでしょうか?

しかし問題もある事も分かりました。見終わっても何にも学んだ事がないです。しかも時間を大量にとる為、後で別の動画を見る事も出来ません。先々週、あんなに心配していたEpic games とアップル社の裁判も現在どうなっているのか全く分かりません。大学教授とかがvtuberになって講義をストリームしてくれると遊びながら学習出来て更に良いですね。

遊びながら学習と言えば、私が作成している英語の発音が学べるゲームですが、LとRの音を持つminimal pairな単語を大量に追加しました。その音でテストしたらLやRの音が一種類でない気がしてきました。Dark LとLight Lについては私も知っていますが、それ以上にLの音があるような気がしています。そこで思い出したのですが、アメリカ人がLの発音する時、実際はThみたく舌を噛んでいる人が一定数いた気がします。Rの発音でも下の先を丸めるRと舌の奥をスプーンみたく丸めて発音するRの音は違う気がします。日本人でLとRの発音が出来ている人でもLとRを聞いたときに区別が付かないのは、単純にLとRに沢山の音があってその全部の音を知らないからかもしれません。

私は音声学を勉強したわけではないので、そういう説があるのかは知りません。しかし例え無くても、日本人がLとRの区別が付かないのは単純にLとRに沢山の音があってその全部の音を知らないから説は仮説としてはあり得る気がしています。

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

<本文>

先週からゲームのアート的な部分も制作し始めました。その結果プログラミングの勉強がおろそかになりがちなので、今週からプログラミングパートとデザインパートを分けて勉強する事にします。

Programming Part

今週はCh4_3 をGithubにでも上げようと思っていたのですが先週から考えた結果、あまりメリットがないと結論が出ました。今までも使用しなくても問題なくやってこれましたし、所詮一人で作成していますから。のでGithubにこのプロジェクトを上げるのは止めます。でなにをするかと言うと、

戦闘システムの「道具」と「逃げる」を選択した場合を作成していなかったのでそれを作成します。

f:id:kazuhironagai77:20200906193308p:plain

デザイン編

今週は先週作成したショップをマップ内に配置して道具屋と武器屋を作成します。

1. Programming Parts

1.1 逃げるボタンの実装

まず、逃げるボタンを選択したらCombatUIウィジェットの変数CombatSelectedのenumをRunにセットします。

f:id:kazuhironagai77:20200906193358p:plain

あれ、「逃げる」って選択をとる為には、BPの変更だけでなくUE4C++からの変更や戦闘エンジンそのものの変更も必要ですね。

f:id:kazuhironagai77:20200906193417p:plain

戦闘が終わった時は二つの状態しか設定していませんでした。負けた時と買った時です。これに逃げた時を追加する必要がありますね。

f:id:kazuhironagai77:20200906193435p:plain

CombatEngineのCombatEngine.hに設定されているenumの CombatPhaseにCPHASE_MakeOffを追加しました。

これで逃げるボタンを選択したら、RPGGameModeBaseクラスにあるCombatEngineの変数内の変数PhaseがCPHASE_MakeOffに変更されるようにすればいいはずです。

そのためには、まずCombatEngineクラスのTick()関数にCase CombatPhase::CPHASE_MakeOffを追加します。

f:id:kazuhironagai77:20200906193452p:plain

因みに、CPHASE_VictoryとCPHASE_GameOverは

f:id:kazuhironagai77:20200906193507p:plain

こんな感じでPhaseを変更しています。

あれ?

f:id:kazuhironagai77:20200906193522p:plain

Phase変数はPublicに指定されているのに、

f:id:kazuhironagai77:20200906193536p:plain

SetPhase() 関数がprotectedに指定されている。

支離滅裂過ぎる。

いくらなんでも私はこんなコードは書きません。教科書を見たらやっぱり

f:id:kazuhironagai77:20200906193553p:plain

教科書にそう書かれていました。

うん。あんまり教科書を信用するのも善し悪しですね。

今回はこのままにして置きますが、CombatEngine3を自作する時は全部書き直します。

変数、PhaseをCPHASE_MakeOffに変更する方法ですが、

  1. CombatEngineクラス内に、Phase変数の値をCPHASE_MakeOff に変更するPublicな関数を作成。
  2. RPGGameModeBaseクラスにその関数を呼ぶBPから実行可能な関数を作成。
  3. CombatUIウィジェットで逃げるボタンを選択した時にその関数を呼び出せるように実装。

でやってみます。

CombatEngine.hにRPGGameModeクラスから呼び出せる関数、SetPhaseToMakeOff()関数を宣言しました。

f:id:kazuhironagai77:20200906193647p:plain

更にCombatEngine.cppにSetPhaseToMakeOff()関数を実装します。

f:id:kazuhironagai77:20200906193723p:plain

今度は、RPGGameModeBaseクラスからこの関数を呼ぶ関数、SetCombatEnginePhaseMakeOff()を作成します。

f:id:kazuhironagai77:20200906193753p:plain

f:id:kazuhironagai77:20200906193801p:plain

まず、UFUNCTIONがBlueprintCallableなのでBPからこの関数は呼べるはずです。

BPとUE4C++そして生のC++の関係は民間、政府、外国の関係と思うと分かり易いと前に書きましたが、民間企業が自国の政府に要請してその政府から更に外国の政府に要請の手順を踏む事で外国政府に要件を聞いてもらうように、BP→UE4C++(RPGGameModeBaseクラス)→生のC++(CombatEngine)となっています。

ビルドも成功しました。

f:id:kazuhironagai77:20200906193841p:plain

本当にBPからPhase変数の値をCPHASE_MakeOffに変更出来ているのか確認するために以下のコードをRPGGameModeBaseクラスのTick()関数に追加します。

f:id:kazuhironagai77:20200906193859p:plain

テストします。

逃げるボタンを選択したらRPGGameModeBaseクラスのSetCombatEnginePhaseMakeOff()関数を呼び出すように実装しました。

f:id:kazuhironagai77:20200906193926p:plain

Phase変数の値はCPHASE_MakeOffに変更されているみたいですね。

f:id:kazuhironagai77:20200906193946p:plain

今度はこの中を正しく実装していきます。

f:id:kazuhironagai77:20200906194024p:plain

まずisNotPrintyet変数ですがこの中のコードを一回だけ実行するための変数です。一回実行したらFalseにセットします。それだけです。

この関数内で実行されるReportFightIsOver()関数が全てを管理しています。

f:id:kazuhironagai77:20200906194042p:plain

ReportFightIsOver()関数はBlueprintImplementableEventなので実装はBPで行います。

まず最初の部分ですが

f:id:kazuhironagai77:20200906194058p:plain

戦闘に勝った場合と負けた場合の2つに分かれています。ここにescapeを追加する必要があります。

以下に勝った場合を示します。

f:id:kazuhironagai77:20200906194114p:plain

表示するセリフが示されています。

逃げた場合のセリフをここに作成する必要があります。

以下の部分は読みましたボタンを表示しているのとそのボタンがクリックされたのを確認しているだけなので変更は必要ないです。

f:id:kazuhironagai77:20200906194131p:plain

これが読みましたボタンです。

f:id:kazuhironagai77:20200906194145p:plain

その後の部分ですが、アニメーションを流しています。戦闘に勝った時は勝利のポーズ、負けた時は敗北のアニメーションを流しています。

f:id:kazuhironagai77:20200906194211p:plain

その後で、HadReadFightIsOver()関数を呼び出しています。

f:id:kazuhironagai77:20200906194230p:plain

以下にRPGGameModeBase.hで宣言されているHadReadFightIsOver()関数を示します。

f:id:kazuhironagai77:20200906194251p:plain

実装部はこんだけです。

f:id:kazuhironagai77:20200906194307p:plain

FightIsOverIsRead変数がtrueになるとTick()関数内の以下のコードが実行されます。

f:id:kazuhironagai77:20200906194324p:plain

成程、大体分かりました。

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

まず新たなEnum, CombatOverStateを作成しました。以下に示した通り、VictoryとGameOverだけでなくEscapeがあります。

f:id:kazuhironagai77:20200906194344p:plain

このEnumを使用してReportFightIsOver()関数を以下のように変更しました。

f:id:kazuhironagai77:20200906194403p:plain

BPに移ってReportFightIsOver()関数の実装になります。Victory変数をBooleanタイプからCombatOverStateタイプに変更しました。

f:id:kazuhironagai77:20200906194418p:plain

当然、分岐もbranchからSwitchに変更しています。

f:id:kazuhironagai77:20200906194438p:plain

逃げた時のコメントを追加しました。

f:id:kazuhironagai77:20200906194455p:plain

逃走した時のアニメーションは要らないのでアニメーションパートはスキップします。

f:id:kazuhironagai77:20200906194512p:plain

これで出来たはずです。

テストします。

f:id:kazuhironagai77:20200906194528p:plain

逃げるを選択するとコメント欄に“KUMO”は逃走しました。と表示されました。

f:id:kazuhironagai77:20200906194544p:plain

そして元の場所に戻りました。出来ていますね。

1.2 道具ボタンの実装

今度は道具ボタンの実装をしていきます。最初はこっちの方が難しいと思ったのですがどうでしょうか?BPだけで実装出来そうな気もしています。

取りあえず道具ボタンを押したら、持っている道具が表示されるように実装してみました。

f:id:kazuhironagai77:20200906194602p:plain

こんな感じで表示されています。

f:id:kazuhironagai77:20200906194620p:plain

Itemウィジェットをそのまま使用したのですが字が切れてしまってますね。

カーソルを道具ボタンの上に置くと、

f:id:kazuhironagai77:20200906194644p:plain

その道具の詳細が表示されます。これはカッコイイです。

よし決めました。ここでItemウィジェットを使用するのは止めます。新しくCombat_Itemウィジェットを作ってそれを使用します。

f:id:kazuhironagai77:20200906194702p:plain

作りました。ItemウィジェットをDuplicateした物に文字の大きさを変えただけです。

f:id:kazuhironagai77:20200906194718p:plain

道具の詳細を表示するウィジェットも新しく作りました。

f:id:kazuhironagai77:20200906194735p:plain

良い感じです。

今度はアイテムボタンがクリックされた場合の実装をします。

まずクリックされたアイテムが何なのかを保持するためにCombatUIウィジェット内に変数を一個作成します。

f:id:kazuhironagai77:20200906194750p:plain

ボタンをクリックした時にこの変数にクリックされたボタンのアイテム名を保持出来るように

f:id:kazuhironagai77:20200906194807p:plain

CombatItemにCombatUIをパスしておきます。

更にcustom event, ItemButtonClickedを作成して

f:id:kazuhironagai77:20200906194822p:plain

それをCombatItemボタンが押された時に呼び出します。

f:id:kazuhironagai77:20200906194836p:plain

取りあえずここでテストします。

f:id:kazuhironagai77:20200906194851p:plain

回復薬をクリックすると左上に回復薬と表示されます。ここまでは正常に動いてるようです。

今度は、アイテムをクリックしたら対象の人が表示されるようにします。といっても対象はユーザーが操作するキャラかモンスターの2種類しかいませんが。

そう言えば、魔法を選択した場合、どうやって実装しているのかチェックし忘れていました。そっちを先に見ます。

f:id:kazuhironagai77:20200906194909p:plain

魔法を選択すると使用出来る魔法がScroll boxであるChoose内に表示されます。そしてその中から魔法を選択すると

f:id:kazuhironagai77:20200906194932p:plain

魔法の対象となるモンスター名が表示されます。

魔法を表示するウィジェットは、

f:id:kazuhironagai77:20200906194947p:plain

f:id:kazuhironagai77:20200906194955p:plain

f:id:kazuhironagai77:20200906195002p:plain

で結構しっかり作り込まれていました。

これは、Combat_Itemを作成する前に見るべきでしたね。ちょっと遅きに失してしまいましたが、今から見ていきます。

f:id:kazuhironagai77:20200906195029p:plain

まず、TargetとTargetUIがあります。TargetはGameCharacterタイプの変数で攻撃対象のモンスターを保持しています。TargetUIはCombatUIタイプの変数でこのMagicUIを保持しているCombatUIのインスタンスを保持しています。

f:id:kazuhironagai77:20200906195048p:plain

TargetとTargetUIの値はMagicウィジェット作成時にparameterとしてパスされています。TargteUIはselfをパスすればいいだけですが、Targetはどうやって収得しているか分かりません。

f:id:kazuhironagai77:20200906195113p:plain

分かりました。この関数が取ってきていました。

この関数、UE4C++のCombatUIWidgetクラスで作成していました。

f:id:kazuhironagai77:20200906195127p:plain

f:id:kazuhironagai77:20200906195134p:plain

うーん。成程、最適化からは程遠いですが、一応敵のGamecharacterクラスを返していますね。

更にMagicボタンがクリックされた場合を見て行きます。

f:id:kazuhironagai77:20200906195151p:plain

CombatUIウィジェットのTarget内のChildrenを全部消しています。魔法ボタンをクリックした時に同じ事を既にやってなかったかなと思って見直して見ると

f:id:kazuhironagai77:20200906195209p:plain

やってなかったです。

ただし、さっき作成したItemボタンをクリックした時は、

f:id:kazuhironagai77:20200906195226p:plain

思いっきりやってます。どっちがベターな方法なんでしょうか?後で考えます。

Magicボタンに戻りますが、

f:id:kazuhironagai77:20200906195255p:plain

その対象のモンスターが生きているのか確認していますね。これは1対1のバトルに変更した今では要らないはずですが、あっても問題がある訳でもないので残しているのでしょうね。

f:id:kazuhironagai77:20200906195313p:plain

その後で、今度はTargetに入るボタン、つまり対象となるモンスターを表示するウィジェットを作成しています。AttackTargetOptionウィジェットは後でみます。

f:id:kazuhironagai77:20200906195331p:plain

最後にコメントを表示していますね。これは大切でした。

AttackTargetOptionウィジェットも次いでに見てみましょう。

f:id:kazuhironagai77:20200906195346p:plain

f:id:kazuhironagai77:20200906195352p:plain

f:id:kazuhironagai77:20200906195406p:plain

ボタンを表示するためのウィジェットなのですが、CanvasPanelを使用していますね。

f:id:kazuhironagai77:20200906195424p:plain

コメントを最後に表示する処以外は普通ですね。

大体分かりました。魔法の時のやり方が楽な気がしてきました。

ただ、Targetは結局、RPGGameModeBaseクラスにある変数、EnemyMonsterとRPGGameInstanceクラスにあるMyYourHero変数なので、parameterとしてパスしなくてWidget内でアクセス出来るのでどうしようかなと思っています。

よし決めました。基本は、魔法を選択した時のやり方で多少マイナーチェンジをします。

こんな感じに作成しました。

まずCombatUIウィジェットで道具が選択された時です。

f:id:kazuhironagai77:20200906195443p:plain

そして、表示されたアイテムの一つがクリックされた時です。

f:id:kazuhironagai77:20200906195501p:plain

ここで開かれるTargetCharacterウィジェットは、AttackTargetOptionウィジェットを参考にして作成しました。

TargetCharacterウィジェットから作成された対象となるキャラを選択した時は、

f:id:kazuhironagai77:20200906195515p:plain

こうなります。

まだセリフなどは追加していませんが、これで一応動くはずです。

テストします。

f:id:kazuhironagai77:20200906195532p:plain

回復薬を選択します。

f:id:kazuhironagai77:20200906195547p:plain

使用する対象が表示されました。KUMOを選択します。

f:id:kazuhironagai77:20200906195603p:plain

あれ、動かなくなってしまいました。

調べたらこの関数を呼ぶ必要があるみたいです。

f:id:kazuhironagai77:20200906195619p:plain

f:id:kazuhironagai77:20200906195631p:plain

これは、CombatEngineを見直す必要がありますね。

今週はここまでにして残りは来週作成します。

2. Design Parts

2.1 道具屋の配置と作成

先週作成した店を配置してみます。

f:id:kazuhironagai77:20200906195704p:plain

外から見た感じです。結構いい雰囲気が出ていますね。

f:id:kazuhironagai77:20200906195719p:plain

店の後ろに回って見ました。雰囲気は良いですが、何か全体的に暗いですね。

f:id:kazuhironagai77:20200906195736p:plain

店の中に入って見ます。やはりちょっと暗すぎますね。

f:id:kazuhironagai77:20200906195751p:plain

横から見た風景です。これは暗すぎませんね。

f:id:kazuhironagai77:20200906195806p:plain

Eを押せば道具屋のUIが始まります。

まあ、最初の段階としては良い感じです。これで道具屋と武器屋の区別が付くようにします。

f:id:kazuhironagai77:20200906195823p:plain

色々考えたんですがこれが一番分かり易いです。

これでいきます。

f:id:kazuhironagai77:20200906195839p:plain

武器屋も作成しました。

f:id:kazuhironagai77:20200906195855p:plain

中身です。

2.2 これからの課題

一応、これで道具屋と武器屋が出来ました。この後、村や町そして城を作成しようと思います。それぞれ別なlevelに作成する予定です。以下の事を考える必要があると思っています。

  • 街全体のサイズ。ランドスケープで制作する場合の設定はどうすればいいのか。ランドスケープを使用しない場合は、町は平らになる。無料で配布されているマップを使用した場合はどうなのか?
  • 別なlevelに移行して戻った場合、前のlevelで採取したアイテムやモンスターが再生されて配置される。一度取ったら再生されないようにするにはどうしたら良いか?
  • Weapon ShopやItem Shopを日本語表示したい。

2.3 無料のマップの確認

Medieval Kingdomの付属のマップを開いて見ていたらPCが凄い音を立てました。正直こんなvideo cardに負担をかけるマップは使用したくないです。自分で作成する事にします。

ただもう時間が無くなってしまったので残りは来週します。

3. 感想とまとめ

今週はここまです。