UE4の勉強記録

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

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する Bug直しとSave Part 2

f:id:kazuhironagai77:20210124230308p:plain

<前文>

HxH式操作系念能力者識別方法

U.S. capitolに侵入した人たちは、最低でも10年位の懲役を食らうらしいです。最近の捜査では彼らを手引きした議員か職員が居る事が明らかにされ、捜査関係者やその場にいた議員から、計画的なクーデターであったと、認識され始めています。ネットでも彼らの側に立っていた保守派、トランプ支持派のInfluencer達も急速にトークダウンして今までの自説を引っ込めて、黙るか、平然とU.S. capitolに侵入した奴らとは関係ないと開き直るかしています。

まだ、日本にはこのアメリカの空気の変化が微妙に伝わってはいませんが、日本におけるオ〇ムのサリン殺人事件の様な扱いになって来ている気がします。

トランプ大統領を含めたこの暴動をincitement(扇動?)した人達は、もう完全に自分だけが助かるのが目的になっていて、特にトランプ大統領に関しては、中で暴動を起こした人達が恩赦を求めても完全に無視しています。

可愛そうと言えば甘いと言われてしまうかもしれませんが、保守派の扇動に騙されてU.S. capitolに突撃した人は、多分、国内テロリストとして残りの人生を生きていかなければならなくなりました。飛行機に乗る事すら出来なくなるかもしれません。トランプ大統領とその支持者たちは「今回の大統領選挙が、不正によって本当は選挙に勝っているにも関わらず、負けてしまった。」と一見、誰にでも分かるような嘘をネットで何百回も繰り返し主張し、実際に極僅かですが、真面目なアメリカ人を騙す事に成功しました。

これほど騙され損な事はないですが、先週述べた理由によりこのような事件はこれから沢山起きてくると思われます。

その理由とは「ネットのAIが同じような事を言うサイトやビデオばかり薦めてくる。」事です。それによって「今回の大統領選挙が不正である。」の様な骨董無形な話にも視聴者や読者には真実に映ってしまいます。

私はこれに気が付いたのは、Epic Game社とAppleとの裁判の時です。YouTubeのお勧めに出てくるサイトの全てがEpic Game社が悪いとしか言わないんです。後で自分で検索したら、沢山の人がEpic Game社の味方をしているにも関わらず、お勧めに出て来るビデオは全てEpic Game社が悪いで統一されていたんです。

今回のU.S. capitolにおける暴動の後に、私自身で確認しましたが「今回の大統領選挙が不正によって本当は選挙に勝っているにも関わらず負けてしまった。」と主張している人のビデオを一個見たら、立て続けに似たようなビデオが表示されました。その中には、弁護士などが作成したビデオがありました。一般人の立場なら、そんな人たちが言っているなら正しいのだろうとなっちゃいますよ。

だから今回の事件の主犯は、ネットのAIなんです。

ここを直さない限り、同じような事件は必ず起きます。それまでは、我々に出来る対策は、このような扇動をあおる詐欺に引っかからない様に気を付ける事だけです。具体的に言うと、彼らの主張に対して科学的に正しいのか検証する事です。しかし常識的に考えれば、一般人には、そんな事が出来る時間も、予算も、そして専門的な知識もあるわけないです。

そういう時間もない、予算もない、そして専門的な知識もない状況で最適な判断を下すために、先週、思いついたHxH式操作系念能力者識別方法をここに紹介します。

前に書きましたが、HxHの念能力者のタイプ分類に沿ってプログラマーを分類すると結構当たっています。このHxHの念能力者のタイプの一つが操作系で、このタイプは何かを操作する事に長けているタイプです。この分類方法は、実際にはプログラマーだけでなくあらゆる専門職にも使えて、今回の事件に当てはめると、ネットで扇動していた人達は操作系に当てはまります。

彼らは、色々言っていますが、やっている事は、扇動(操作)する事だけです。このタイプが指示する側にいたら、間違いなく自分は扇動(操作)されていると考えるべきです。この操作系を見つける方法の一つは寄付の支払い方を確認する事です。寄付をする段階になった時に操作系は絶対しないです。騙されている側の人は飯を抜いてでも寄付します。しかし扇動(操作)する側は絶対に寄付しません。

あなたを扇動する操作系が見つかったなら、あなたは操作されている事は確認出来たわけです。そして操作されている間は最適な答えを見つける事は出来ないと自覚しましょう。なぜなら操作されている間は、あなたは操作している人の望み通りに動いているだけだからです。この操作されている状態を外すだけで、最適解を見つける確率が0ではなくなります。

ではどうやって外すかと言う事なんですが、これもHxHの中に答えが既に提示されています。操作系は別の操作系がコントロールしている人は操作出来ないんです。つまり既に誰かに操作されている状態の人を操作する事は出来ません。これってかなり真実でしょう。今回の暴動に参加した人の中に、イスラム教の人や、日本のアニメやゲームが好きな層、更に言えば白人以外の人っていますか?私が見た限りでは一人もいませんでした。例え居ても極少数でしょう。(何故か、ロシア訛りの英語を話す人やPCゲームのファンは居たみたいです。これは本筋から離れるのでここでは述べません。)

先程述べたEpic Game社とAppleとの裁判の例でも、口の悪い言い方をすれば、私自身が既にUE4の信者であったので「Epic Game社が悪い」と言う扇動(操作)に罹らなかったんです。だから、何かの信者(アニメでも、ゲームでも、スポーツでも、そして勿論、宗教も)に前もって成っていればいいんです。

そこで私のお勧めは、プログラミング教の信者になる事です。プログラミングの勉強を一生続けると、死後天国に行けると言う信仰です。一定のレベル以上のプログラマーは休みでも何かしらの勉強をしています。これは普通の人から見ると大変きつそうですが、天国に行くための修行と捉えればきつくても納得です。

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

<本文>

1.今週の作業

今週はUE4_AIについての勉強をします。

  1. TaskクラスのAbort、ExecuteとTick関数とdecoratorクラスのObserver abortsの設定の関係について
  2. 先週やったTutorialの続き
  3. 実際に使用するモンスター様のAIの作成

を行います。

2. TaskクラスのAbort、ExecuteとTick関数とDecoratorクラスのObserver abortsの設定の関係について

先週「Unreal Engine 4で極めるゲーム開発」を読んでいたらTaskクラスのAbort()関数は、Observer abortsでLower Priorityのような設定でAbortされた時に発動すると書かれていました。目から鱗です。これを確認します。

2.1 SequenceノードとSelectorノードの復習

ですが、全てのノードの機能についての確認を最初にやっておきます。まずSequenceノードとSelectorノードについての確認です。

まずはSelectorノードのテストです。

以下のコードを作成しました。

f:id:kazuhironagai77:20210124230449p:plain

実行されるのはどのTaskでしょうか?もしSelectorのChildの一つでもSucceedしたらそこで止まります。

ので、PrintStringのみが実行されるはずです。

テストしてみます。

f:id:kazuhironagai77:20210124230511p:plain

PrintStringのみが実行されました。ここまでは予測通りです。

次にこのゲームを終了します。どのTaskのAbortが実行されるのでしょうか?

実行されているのは、PrintStringノードのみです。だからPrintStringのAbortのみが実行されるはずです。

テストして試してみます。

f:id:kazuhironagai77:20210124230528p:plain

その通りでした。

今度は以下の様にSequenceノードを代わりに使用します。

f:id:kazuhironagai77:20210124230545p:plain

SequenceはFailureするまでChildノードを次々に実行するはずです。ので

  • PrintString
  • PrintString1
  • PrintString2
  • PrintString3

が表示されるはずです。

f:id:kazuhironagai77:20210124230622p:plain

あれ?

PrintString Task内でFinish Executeノードを追加するのを忘れていました。

f:id:kazuhironagai77:20210124230643p:plain

PrintString1、PrintString2、PrintString3にもFinish Executeノードを追加しました。

もう一度テストします。

f:id:kazuhironagai77:20210124230702p:plain

なんと永遠にPrintされています。

Selectorの場合ももう一度試してみます。

f:id:kazuhironagai77:20210124230721p:plain

Selectorも永遠に実行されていました。しかもこの場合、終了した後、Abortが呼ばれません。

Successのチェックを外してみます。

f:id:kazuhironagai77:20210124230738p:plain

最後まで全てのTaskを実行して、その後また最初のtaskに戻って実行しています。

f:id:kazuhironagai77:20210124230755p:plain

この場合も終了した後、Abortは呼ばれません。

うーん。なるほど。Abortは途中で収容した時に呼ばれるのかもしれませんね。

2.2 Abort Eventを呼ぶ

検証のために以下のコードを作成しました。

f:id:kazuhironagai77:20210124230817p:plain

ChooseNumberは0に指定しています。

Finish Executeは全部Trueに変更しました。

テストします。

f:id:kazuhironagai77:20210124230834p:plain

想像していた通りの結果がプリントされています。

今度は、ChooseNumbarを途中で2に指定してみます。

f:id:kazuhironagai77:20210124230925p:plain

PrintString1(Execute)が終了した時にChooseNumberが2に成りました。そしてその後にPrintString2(Execute)とPrintString3(Execute)がプリントされています。Abortはプリントされませんでした。

f:id:kazuhironagai77:20210124230949p:plain

Observer AbortがNoneにセットされているからでしょうか?

Selfに変更してみます。

f:id:kazuhironagai77:20210124231005p:plain

今度は、PrintString(Execute)を実行した後、にChooseNumber is set to 2が実行されています。そしてその後で、直ぐにPrintString2(Execute)が実行されています。

これは、先週紹介したサイト

f:id:kazuhironagai77:20210124231023p:plain

この説明に合っているように見えます。

もう少し厳密に検討するためにWait taskを追加します。

f:id:kazuhironagai77:20210124231041p:plain

これでもう一度テストします。

最初はObserver Abort をNoneにセットしました。

f:id:kazuhironagai77:20210124231056p:plain

今度は分かり易いです。PrintString(Execute)が実行した後、Waitノードが実行されている時に、ChooseNumberが2に変更されました。しかしその次のノードであるPrintString1(Execute)は実行されています。全てのノードの実行が終了した後で、ChooseNumberが2である条件に該当するNodeが実行されています。

これはサイトにある説明通りです。

今度はselfに変更します。

f:id:kazuhironagai77:20210124231245p:plain

今度も前回と同じ様に、PrintString(Execute)が実行した後、Waitノードが実行されている時に、ChooseNumberが2に変更されました。今回は次のノードであるPrintString1(Execute)は実行されず、ChooseNumberが2である条件に該当するNodeが実行されています。

これもサイトの説明通りです。

しかしこの時にAbortは呼ばれていません。何故なんでしょうか?

ピンと来ました。Waitノードを外してPrintString1 taskにDelayノードを追加します。

f:id:kazuhironagai77:20210124231335p:plain

これでテストしてみます。

f:id:kazuhironagai77:20210124231352p:plain

出来ました。今度はAbortが呼ばれています。

PrintString1 taskの実行中にChooseNumberの値が2に変化したので、PrintString1の実行をabortして、ChooseNumberが2である条件に該当するNodeを実行しています。

2.3 Observer Abort の確認、None、Self、Lower PriorityそしてBothの違い

2.3.1 None

上記のSelfと同じ条件でNoneをやってみます。サイトの説明通りならNoneの場合は、abortは呼ばれないはずです。

f:id:kazuhironagai77:20210124231425p:plain

PrintString1 taskの実行中にChooseNumberの値を2に変化しましたが、今回はその変化を無視してPrintString1の実行を続行しています。

予想通りでした。

2.3.2 Lower Priority

f:id:kazuhironagai77:20210124231456p:plain

と書かれています。これだとこのテストだとNoneと同じ結果になるのでは?

と思いテストの条件をちょっとだけ変える事にします。

しかしその前に一応確認のテストをしておきます。

f:id:kazuhironagai77:20210124231624p:plain

うん。同じ結果ですね。

一応、Bothの場合もテストしておきました。

f:id:kazuhironagai77:20210124231644p:plain

こちらはSelfと同じ結果です。

それではテスト条件を以下の様に変更します。

f:id:kazuhironagai77:20210124231707p:plain

PrintString1の隣にPrintString2を追加しました。

まずNoneの場合です。

f:id:kazuhironagai77:20210124231737p:plain

PrintString1を実行している途中で、ChooseNumberの値を2に変化しました。しかし最後までPrintString1を実行し、更にPrintString2を実行しています。その後でChooseNumberの値が2の時の条件に合うSequenceのTaskを実行しています。

今度はselfです。

f:id:kazuhironagai77:20210124231831p:plain

PrintString1を実行している途中で、ChooseNumberの値を2に変化しました。PrintString1の実行を中断し、その後でChooseNumberの値が2の時の条件に合うSequenceのTaskを実行しています。PrintString2は実行していません。

Lower Priorityの場合です。

f:id:kazuhironagai77:20210124231953p:plain

あれ、PrintString2は実行されないと思ったのですが、想像していたのと違う結果ですね。

ちょっと考えます。

分かりました。Lower Priorityの場合、

f:id:kazuhironagai77:20210124232016p:plain

この右側の部分が全て無視されるはずです。

そしてbothの場合は、Self + Lower priorityとなり、全部中止になるはずです。

これを明確に表示するために、以下のコードを作成しました。

f:id:kazuhironagai77:20210124232256p:plain

このコードの特徴はPrintStringとPrintString3のFinish ExecuteをFailureで返す所です。

これで、実際のコードの流れをもっと細かく見れます。

PrintString1を実行している途中で、ChooseNumberの値を2に変化しました

Noneの場合

f:id:kazuhironagai77:20210124232350p:plain

予測通りですがそのまま実行しています。PrintString2(Execute)を実行した後でSelectorにSuccessを送るので、selectorは最初からやり直しています。

Selfの場合

f:id:kazuhironagai77:20210124232456p:plain

予測通りPrintString1(Execute)の実行を中止してabortを発動しています。その隣のtaskであるPrintString2(Execute)は実行されません。Selectorに戻ると、selectorはSuccessの信号を受け取っていないのでそのまま次のnodeを実行しています。

Lower Priority

f:id:kazuhironagai77:20210124232548p:plain

これでもNoneと同じ結果になってしまいました。

Noneとの違いを確認するために、PrintString2(Execute)のFinish Executeの返しをFailureにします。

Noneの場合です。

f:id:kazuhironagai77:20210124232624p:plain

PrintString3、PrintString4が実行されています。

Lower Priorityの場合です。

f:id:kazuhironagai77:20210124232645p:plain

この場合、PrintString1(Execute)のみ実行されて、PrintString2、PrintString3、PrintString4はabortされると思われるのですがされていませんね。Noneと全く同じ形になってしまっています。

何故なのでしょう?

Both

f:id:kazuhironagai77:20210124232746p:plain

これもSelfと全く同じ結果になっていますね。

2.3.3 Observer Abortまとめ

もし私のObserver Abortのそれぞれの要素に対する理解が100%正しいとしたら、Lower Priorityが効いていないと考えると全部辻褄が合います。

まあ今回は、そういう可能性もある位で、留めておいてObserver Abort はNoneとSelfだけ使用するようにします。

3. 先週勉強したTutorialの続き

3.1 Guard AI

特になしです。単にServiceを使用してNPCの歩くスピードを調節しただけ。しかしNPCとの追いかけっこが結構面白くなってきました。

3.2 Guards Searching

特に新しい関数とかは出て来ませんでした。どうやったらAIが本物のGuardみたく動くのかを勉強する感じです。

f:id:kazuhironagai77:20210124233232p:plain

Abortした時は、failureになると思っていたのです、finish abortで返してもそうなんでしょうか?Loopの下のBlackboard Conditionはselfにセットされているので、途中で条件が変わると行動が変化します。

f:id:kazuhironagai77:20210124233257p:plain

流石に、追い駆けっこそのものは滅茶苦茶面白いです。これだけでゲームに成りそうなくらいの完成度です。

何でこんな面白いんでしょうか?

NPCの反応が、生きた人間そっくりだからでしょうか?

不思議ですね。

3.3 Melee Attacks (Animation)

Meleeってどういう意味だったけ。と調べたらグーグルの一番最初に以下の説明が表示されました。

f:id:kazuhironagai77:20210124233332p:plain

もっと、抽象的な概念を想像してたんでびっくりです。

アニメーションの作成方法で今まで知らない方法を学びました。

まずAnimation Montageです。

f:id:kazuhironagai77:20210124233424p:plain

f:id:kazuhironagai77:20210124233433p:plain

もしかしたら一回位は、作成した事位はあるかもしれませんが、ほとんど初めて聞いたクラスです。使用目的や使用方法などは全く分かりません。

次はAnimationBlueprintクラスのAnimGraphで先程作成したslotとDefaultをblendする方法です。

f:id:kazuhironagai77:20210124233451p:plain

これも初めて勉強する方法でした。

正直、完全に理解したとは言えません。Tutorialに書かれてる通りにやったら動きました。と言う感じです。UE4_animationは一度しっかり勉強する必要がありますね。

f:id:kazuhironagai77:20210124233507p:plain

Blendしたanimationをスクリーンショットで撮ったんですが、動作がほとんど終了していました。

3.4 Melee Attacks (Behaviour)

今度は前節で作成した攻撃モーションをNPCが使用します。

以下に示した様に最終的には出来ましたが、結構大変でした。

f:id:kazuhironagai77:20210124233535p:plain

以下に示したtaskのChasePlayerのfinish Executeがfailureで返されていて次のMelee Attackが呼ばれなかったです。

f:id:kazuhironagai77:20210124233557p:plain

このバグに気づかなくて、直すのに30分位かかりました。

新しい関数としてSimple Parallelを習いました。

f:id:kazuhironagai77:20210124233648p:plain

Simple ParallelのFinish Modeには2種類あり、immediateは左に接続したmain taskが終わり次第、終了します。DelayedはBackgroundで実行されている他のTaskが終了するまで待ちます。

f:id:kazuhironagai77:20210124233707p:plain

Get Distance to関数は二つのActorの距離を返します。

f:id:kazuhironagai77:20210124233724p:plain

3.5 Hearing Perception

まず、AIのデバッグをするために ‘を押してくださいとありますが、

f:id:kazuhironagai77:20210124233759p:plain

Shift+7を押しても何も表示されません。

これは日本語キーボードには対応していないのかもしれないと思い日本語でこれについて書いてある記事を探したらここにありました。

その記事を参考にして1を押すとGamePlayデバックが表示されるように設定し直しました。

f:id:kazuhironagai77:20210124233826p:plain

表示されるようになりました。

Perceptionを表示しています。

f:id:kazuhironagai77:20210124233845p:plain

でも何も表示されない時もあります。

f:id:kazuhironagai77:20210124233901p:plain

何も表示されない時は、Debug actorがNoneになっていました。

f:id:kazuhironagai77:20210124233921p:plain

表示される時は、

f:id:kazuhironagai77:20210124233954p:plain

NPC_2になっています。どうやってDebug actorを選択しているのか、今は分かりません。

Hearing Perceptionについて

一応、Tutorial通りに作成しましたが、Hearing Perceptionそのものについては、個別のTutorialで教えてほしかったです。そして今まで作成したSight Perceptionとの結合方法を教えてくれた方が全然分かり易いと思いました。

複数の知覚を設定した時に、どの知覚から信号を受け取ったのか判別する方法は、以下に示しました。

まず、AIPerceptionStimuliSourceのReport Noise EventのTagに名前を追加します。

f:id:kazuhironagai77:20210124234017p:plain

AI_Controller側で刺激を受け取りTagから名前を調べます。

f:id:kazuhironagai77:20210124234105p:plain

3.6 Damage Sensing

このTutorialからコメント欄が文句の嵐になっていてます。多分何か問題があるのでしょう。ここからは軽く見る事にします。

前のtutorialのバグの直しから始まっています。プログラマーなら自分で直せるはずなので直しそのものは重要ではないですが、その後のテストで、

  • NPCがパトロール中にプレイヤーを目撃した場合
  • NPCがパトロール中に何かの音を聞き、プレイヤーを確認した場合
  • NPCがパトロール中に何かの音を聞き、調査に行った途中でプレイヤーを確認した場合

を確認しているのを見て、そう言う事だったのかと思いました。確かに門番がパトロール中に侵入者を目撃したら、追いかけていきます。不審な音が聞こえたらパトロールのルートを外れても確認します。そしてその過程で侵入者を見つけたら追いかけます。

こういう事を真剣に考える事で人間らしく反応するAIが作成できる事が分かりました。

Damageの設定そのものはAction Gameか何かの時に本格的に勉強すると思いますので、今回はUE4にはこんな機能もあるのか位に留めておきます。

3.7 Nav Link Proxy

Nav Link Proxyの使い方の説明で、NPCが高い所から低い所にNav Link Proxyを使用して降りる場合を説明していました。たった5分の説明なのでこれだけでNav Link Proxyを使いこなす事は出来ないと思いますが、大変興味深い機能と思いました。

この次のTutorialでNPCが低い所から高い所に移動する場合について説明するとありましたが、次のtutorialはAI EQSについてでした。

3.8 AI EQS Part 1とAI EQS Part 2

EQSは試験的な部分がまだありますと説明されていたんで、AIの基礎を勉強中の私が特別、積極的にやる必要はないと思い、軽く動画を見るだけにしました。

4.まとめと感想

今週はここまでで時間が無くなってしまいました。モンスターのAIの改良は来週やる事にします。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する Bug直しの続きとSaveの改善

f:id:kazuhironagai77:20210117212957p:plain

<前文>

長母音と短母音について

英語の発音記号についてなんですが、ネットとかで英語の発音を教えているほとんどのアメリカ人は大学で言語学の学位を取った人じゃないんです。だから話半分に聞いておくべきなんです。そう言う考え方もあるよね。ぐらいで。

その例として、今回ここで述べておきたいのは、long i とshort iの音の長さは同じと言う説です。

あるサイトでleaf とliveのそれぞれのlong i とshort i の音の長さを測定したんですが全く同じ長さだった。とありました。実際はこのサイトだけでなく結構な数の英語の発音を指導するアメリカ人がlong i とshort i の音の長さは同じと主張しています。

ただそのサイトで提供されていたleaf とliveの音を聞くとliveのiの音の方が短く聞こえたので、本当に二つの単語のiの音の長さが同じなのか自分で測定してみました。

f:id:kazuhironagai77:20210117213034p:plain

そしたら、0.235秒でLiveはVの音が聞こえるますが、LeafのFの音はまだ聞こえないんです。

つまり、私が測定した限りではshort iの音はlong iの音より短かったんです。

本当にこのサイトの主は自分で音を測定したんですかね?

査読付きの論文にうそのデータを載せたら大変です。しかしその辺にたむろしているオッチャンが、運動部の練習している高校生に「水取らないと、危ないぞ。」と言っても、それって「水分を取るべきって言うべきで、生理的食塩水なら運動中に摂取すべきですが、単なる水道水を運動中に摂取したら…」と言い返す人はあんまりいないです。

英語の発音についても同じ事が言えます。ネットで英語の発音を指導するアメリカ人のほとんどは大学で専門に勉強した人ではなく単なるその辺のオッチャンと同じレベルの人です。その人が述べる指導方法で上達する事が出来ないのは自明ですが、それをわざわざ指摘する人は野暮なだけです。

所がですが、一端ネットに載ってしますと、その人が凄腕のコーチなのか、その辺のオッチャンなのか、区別が付きにくくなってしまうんです。その辺のオッチャンがどこかの有名大学の教授の様に見えたり、その逆もあったりします。

だからネットで言われている意見については自分でチェックするのは非常に大切と思います。

アメリカ議会の占拠と殺人

今回の大統領選挙で、不正が行われていると信じてアメリカ議会の占拠をした人達はとうとう殺人まで犯してしまいました。個人的には、この事件の本当の犯人は、偏った意見を述べるinfluencerを一人でも見ると、同じような意見を持つ人ばかり表示するネットのAIだと思います。反対意見が全く表示されない状態で、同じような意見を色々な有名人が述べたら、一般の人は直ぐに信じてしまいます。

しかしほとんどのアメリカ人は単純に、右が間違いで左が正しい、そして問題は解決したと安心しています。

今回の事件で、右寄りの意見を言っていた人は暫く肩身の狭い思いをすると思いますし、実際反省すべきですが、アメリカのリベラルが同じ間違いをこれから犯さないとは私には思えません。今回の問題を引き起こした原因であるネットのAIについて誰も言及しないからです。

私は近い将来、今度は以下の分野でリベラルが同じような問題を起こす可能性があると思っています。

  • 私は、地球温暖化はかなり怪しいと思っています。これから地球は寒くなると思っています。これは単なる個人的な見解ですが、地球温暖化ビジネスに未来のトランプが潜んでいたら。今回以上の事件を将来、リベラルが起こす可能性は非常に高いと思われます。
  • 福祉の充実こそが左派に求めらてれている政策です。しかし実際にそれを実行するリベラルは民主党で浮いた存在になるでしょう。適当な政策でお茶を濁すはずです。個人的な注目は大学の学費です。これが下がるかどうかで民主党の福祉に対する本気度が分かると思います。
  • 無意味なポリコレの強制。これは日本人には馴染みの問題過ぎて、私がここで解説する必要はないですが、一応述べておきます。絵を無くしても問題の解決にはつながりません。

これから犯罪をそそのかす、このようなデマに騙されないためには、前述した発音の問題の様に、一々チェックするのが最適ですが、実際は忙しすぎるのと専門分野以外の話は正しく理解する事すら出来ないので不可能です。

これに対する秘策を実は思いついちゃったんです。

名付けてHxH式操作系念能力者識別法です。

これについて解説すると長くなりすぎて勉強時間が取れなくなってしまうのでこの話は来週に回します。

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

<本文>

1. 今週の予定

先週、量が多すぎるので中止した2つのバグを直します。

  • 徘徊するモンスター用に別なAIを作成
  • PlayAnimationノードの使用は中止。アニメーションはAnimBPで一括して管理する

更に、今まで適当にしていたsave機能を完成させたいと思います。

1.1 (!!)の表示について

先週、作成した後でInstance editableなBoolean 変数を一個作成すれば、わざわざRPGGameModebaseBPに新しい変数を作成する必要もないと思ったのですが、面倒だったので試さなかったです。

試してみます。

f:id:kazuhironagai77:20210117213150p:plain

f:id:kazuhironagai77:20210117213159p:plain

f:id:kazuhironagai77:20210117213207p:plain

f:id:kazuhironagai77:20210117213216p:plain

これで、RPGGameModebaseBPに作成した新しい変数を使用しないで、(!!)の表示が出来るはずです。

テストします。

f:id:kazuhironagai77:20210117213234p:plain

出来ました。

RPGGameModebaseBPのItemNameToPickup変数はいらないので消します。

因みに、拾ったitemを消去する関数内でもRPGGameModebaseBPのItemNameToPickup変数は使用していましたが、以下のように実装を変更する事で、RPGGameModebaseBPのItemNameToPickup変数を使用しないでも収得したアイテムが2度以上表示される事は無くなりました。

f:id:kazuhironagai77:20210117213255p:plain

一応テストした結果も記録として残しておきます。

f:id:kazuhironagai77:20210117213329p:plain

2. 徘徊するモンスター用に別なAIを作成

徘徊するモンスター用のAIを作成します。

2.1 AIの復習

もう一通りの基本は理解しているので細かい点の復習を兼ねてこのサイトUE4_AIの勉強をします。前に途中までこのサイトでUE4_AIの勉強をしたので選びました。

Part 2 AI Perceptionまで見て思ったんですが、これそのまま作成したら十分です。

今のAIはプレイヤーが操作するキャラがNavの領地に入ると、モンスターはどうやってプレイヤーの操作するキャラを見つけたのか分からないけど猛烈に追いかけて来ます。それでモンスターに視覚のようなモノを与えたかったのですが、このチュートリアルはそのものズバリです。そのまま作成して使用する事にします。

前回、勉強した時UE4のAIの全体像が知りたかったので、正直駄目なtutorialとは思いました。2020-12-06のブログで述べましたが「UE4_AIそのものは単なるif節である。」との認識こそが、当時の私が知りたかった事でした。その次のステップとして「AIがコントロールするキャラの動きをもっと自然にしたい。」となった時にこのtutorialは非常に役に立ちます。

前回の勉強をまとめたブログを参考にしつつ、もう一回このtutorialを勉強します。

2.1.1 The Behaviour Tree

この回のTutorialは、AIController、BehaviorTree、BlackboardなどのUE4_AIを構成するクラスの使用方法を説明しています。Behavior TreeについてはTaskの作成についてかなり細かく説明してます。

ただし、私がUE4_AIの基本を説明するために最も大切であると考える「UE4_AIそのものは単なるif節である。」の認識を得る事はこのTutorialからは不可能です。そういう意味では初心者にとってはあまり良くないです。

前回勉強した時のブログを読むと「この回のtutorialに載っている事は全部知っている事である。」とまとめられていました。

今、読み直すと「全部知っている」は完全な嘘で、例えばTaskクラスのEvent関数である以下に示したものの違いは今でも分かっていません。

f:id:kazuhironagai77:20210117213423p:plain

やっぱり人間は本当は良く分かっていない時には「全部分かった」の様な過剰な自信をみせる発言をしてしまうんですね。

一寸だけ、これらのEvent関数の違いを調べて見ましょう。

このサイトにAIとGenericの違いの説明がありました。

f:id:kazuhironagai77:20210117213442p:plain

この説明によるとAIControllerを使用した場合は、AIのEventを選択する必要があると書かれています。

Abort、ExecuteとTick関数の違いについては、見つかりません。

このサイト

f:id:kazuhironagai77:20210117213502p:plain

簡単なBehavior Treeを作成してテストしたら理解出来るとありましたので、試してみます。

以下の様なBehavior Treeを作成しました。

f:id:kazuhironagai77:20210117213520p:plain

Task、PrintStringを以下の様に作成しました。

f:id:kazuhironagai77:20210117213538p:plain

これでテストします。

f:id:kazuhironagai77:20210117213557p:plain

普通にPrintされました。

次にAbort AIを接続します。

f:id:kazuhironagai77:20210117213615p:plain

これでテストします。

f:id:kazuhironagai77:20210117213637p:plain

あれ、何も表示されません。

この方法じゃ単純過ぎてAbortは発動しないのかと思って停止したら、その瞬間に表示されました。

f:id:kazuhironagai77:20210117213655p:plain

AIをAbortした時とは、そのAIが開放された時、つまり一般的に言えばゲームを終了した時に発動するTaskのようです。

そうするとTickはそのAIを使用している間、ずっと発動していると言う事になります。

Tickも確認してみましょう。

f:id:kazuhironagai77:20210117213713p:plain

テストします。

f:id:kazuhironagai77:20210117213730p:plain

その通りでした。

一般的なTaskを作成する分にはExecute AIだけ使用すれば良いみたいです。

2.1.2 AI Perception

前のブログを読んでみたらAIPerceptionについて結構勉強していました。はっきり言って何も覚えていません。動画を見るとAIPerceptionを使用してAIが操作するキャラに視覚を追加しています。ここで知りたい事は、どうやってAIが操作するキャラに視覚を追加出来るのかです。

それをまず勉強します。その後で、前のブログに書かれているAIPerceptionについての勉強を読んでみます。

AI Perceptionを使用する前に、Behavior Treeを以下に示した様に変化させました。

f:id:kazuhironagai77:20210117213809p:plain

Blackboardに作成したBoolean変数であるCanSeePlayerがfalseなら左側、trueならば右側のSequenceが実行されるだけです。

f:id:kazuhironagai77:20210117213826p:plain

このCanSeePlayer変数の値をAIPerceptionを使用して変えます。

まずAIPerceptionですがAIControllerクラス内に作成します。

f:id:kazuhironagai77:20210117213845p:plain

f:id:kazuhironagai77:20210117213852p:plain

作成したら、AIPerceptionのSenses Configにelementを一つ追加します。

そしてImplementationの項目をAlSense_Sightにします。

これは視覚を使用すると言う意味だそうです。

f:id:kazuhironagai77:20210117213910p:plain

以下の項目にチェックを入れましたが、単に敵も味方の第三者も同列に扱うと言う意味です。
詳しい説明は後のtutorialに出てくるそうです。

f:id:kazuhironagai77:20210117213928p:plain

今度は見られる方のキャラクター、この場合はThirdPersonCharacterにAIPerceptionStimuliSourceを追加します。

これが在る事でAIPerceptionから知覚される事が可能になります。今回の場合は視覚によって見つかると言う事になります。

f:id:kazuhironagai77:20210117213957p:plain

更に、AIPerceptionStimuliSourceのAI Perception内でAuto Register as SourceをチェックしてRegister as Source for SensesにAlSence_Sightを選択します。

こうする事で、敵から視覚によって認識されるようになります。

f:id:kazuhironagai77:20210117214024p:plain

そして、AIController内で以下ように実装します。

f:id:kazuhironagai77:20210117214039p:plain

On Target Perception Updated(AIPerception)を使用する事や使用方法については特に疑問はないんですが、これだと一端BlackboardのCanSeePlayer変数がtrueになったらその後はずっとtrueのような気がします。調べてみたらそんな事は無かったですが少し不思議です。

まあ、それ以外でそんなに難しい事はありませんね。

  • 見る側にAIPerception
  • 見られる側にAIPerceptionStimuliSourceを追加する事
  • Eventの開始はAIController 内にOn Target Perception Updated(AIPerception)を使用する事

と言う事ですね。

今度は前に勉強した内容を読んでみます。

一杯書かれていて、沢山勉強していますが、先程説明した

  • 見る側にAIPerception
  • 見られる側にAIPerceptionStimuliSourceを追加する事

が分かっていないみたいです。それで大変混乱しています。

去年、理解出来ていなかった内容が今は理解出来ると言う事は、UE4の勉強が着実に進んでいる証拠でもあります。だからまあ良いでしょう。

2.1.3 Chasing the Player

一端、追いかけ始めたら普通ならずっと追いかけるはずです。しかし今のAIはキャラを見失ったら適当な場所に行ってしまいます。それを直します。

Behavior Treeのdecorator、blackboardの設定であるFlow ControlのObserver abortsの値をBothに変更します。

f:id:kazuhironagai77:20210117214225p:plain

しますがこの意味が良く分かりません。一応説明しているのですが、よく聞き取れません。

このサイトに説明されていました。

f:id:kazuhironagai77:20210117214251p:plain

分かりました。

これは何かを実行している途中で、If節の条件が変わってしまった時の対応について決めているです。

  • 普通のif節だったら、条件をチェックした後にその条件が変わっても無視してそのまま進行します。これと同じなのがNoneです。
  • Selfに設定した場合は、今いるNodeとその下にあるNodeの実行を止めて、隣のnodeを実行します。
  • Lower Priorityは今いるnodeとその下に付いているnodeは実行しますが、今いるnodeより右側にあるnodeは無視します。
  • 最後のBothは全部中止して一つ上のnodeに戻ります。

これで合っているはずです。Bothにセットしていますので、Playerを見かけたら何かをやっている途中でもPlayerを追いかけてくるはずです。

テストします。

正直、良く分かりません。色々条件を変更しましたが、完全に上記の解釈が合っていると確認は出来ませんでした。

Playerが逃げ続ける場合、AIはPlayerに対して真っ直ぐ追いかけるはずですが、実際はジグザグに追いかけています。これを直します。

直し方は単純で、新しいTaskを作成してそれをMove toの代わりに使用します。

f:id:kazuhironagai77:20210117214324p:plain

ジグザグは直りましたが今度はカクカクするようになってしまいました。

解決策はCool down Decoratorを追加する事です。Cool down Decorator はDelay関数と同じ働きをするみたいです。

f:id:kazuhironagai77:20210117214342p:plain

0.2秒にセットしたらカクツキが無くなりました。

最後にカーブを曲がるときにもっとスムーズに曲がれるようにします。

以下の部分の設定を変更しました。

f:id:kazuhironagai77:20210117214358p:plain

この項目はPlayer controllerによってキャラクターが操作されている場合にcheckすべきなんだそうです。だからcheckを外します。

f:id:kazuhironagai77:20210117214416p:plain

こっちの設定は回転する時にスムーズに回転するための設定らしいです。正直良く分かりません。

テストしてもこの部分は実際の違いは正直分かりませんでした。

以上です。

最後に前、勉強した時は何と言っていたのか確認します。

まず、Behavior Treeのdecorator、blackboardの設定であるFlow Controlの設定についてですが、以下の様に述べています。

f:id:kazuhironagai77:20210117214439p:plain

これって一個ずれていますね。と言うか、今回勉強した時は、この説明の部分何言っているのか聞き取れなかったんですが、前回勉強した時は普通に聞き取れていたみたいですね。イギリス訛りなんで結構聞きにくい部分があるんですが、日本に帰って来てからlistening力落ちていますね。結構ショックです。

もう一回、今度は真剣に聞いてみます。

言っていますね。Noneの時に一連の動作を止めて、次のNodeの動作をすると。これはこのサイトの説明と違いますね。このサイトではNodeはなにもしないと言っています。多分サイトの説明の方が合っているんでしょう。Tutorialの説明はLower Priorityの辺りからモゴモゴ言って何を言っているのか分からなくなっていますから。

簡単なテスト方法を思いついたら後で確かめてみます。と思ったら、前回も同じ事言っていました。

その後の解説は今回と全く同じです。最後のAIが回転する所が良くなっていると言っているが全然分からないと今回と全く同じ事言っています。

そして最期にこんなのAIの勉強じゃないと怒っています。

まあ。UE4のAIの基本を知りたい時に、こんな走り方の微調整のやり方を教わっていたら、頭に来るのは分かります。ただ今回は実際のモンスターのためにもっとマシなAIを作成したいと言う目的があるので、モンスターが回転する時の微調整なんかも大切です。のでもうちょっとだけ勉強を続けようと思います。

2.1.4 Patrolling NPC

今度はNPCにパトロールをさせるそうです。正直この機能こそモンスターのAIに追加したかった機能です。しっかり勉強しましょう。

まずactorクラスからBPを作成します。PatrolPathと名付けます。

f:id:kazuhironagai77:20210117214502p:plain

このクラス内にVectorタイプのarray変数であるPathPointを作成します。

この変数はInstance editableです。

f:id:kazuhironagai77:20210117214519p:plain

このクラスのinstanceをマップ内に配置します。

f:id:kazuhironagai77:20210117214536p:plain

配置したinstanceのPathPointに3つの要素を追加します。

f:id:kazuhironagai77:20210117214552p:plain

f:id:kazuhironagai77:20210117214600p:plain

注意点として最初の要素の値は0,0,0である必要があるそうです。

このInstanceで指定した場所にNPCは移動していきます。つまりNPCにパトロールをさせると言う事は、このPatrolPathのinstanceが保持するPathPointの値をどうやってUE4_AIに渡すのかと言う事につきます。

次にNPCのBPを開きPatrolPathタイプの変数、PatrolPathを作成します。

f:id:kazuhironagai77:20210117214639p:plain

この変数はInstance Editableに指定します。

配置したNPCのinstanceのPatrol Pathに先程作成したPatrolPathのInstanceを指定します。

f:id:kazuhironagai77:20210117214704p:plain

これは名前がクラスとinstanceが同じなので混乱しますが、全部Instanceです。

なので、それぞれのNPCに違う場所のパトロールをさせる事が出来るはずです。

次にBehaviorTreeを示します。

f:id:kazuhironagai77:20210117214729p:plain

Blackboardには2つの変数を追加しました。

VectorタイプであるPatrolPathVectorとintタイプであるPatrolPathIndexです。

f:id:kazuhironagai77:20210117214745p:plain

最初のTaskであるFindPathPointで移動する場所のVectorNPCからBlackboardに新しく作成した変数、PatrolPathVectorにパスします。

次のTaskであるMove toでその場所に移動します。

その次のTaskであるIncrementPathNextでPatrolPathIndexの値を1だけ増やします。

FindPathPointの実装です。

f:id:kazuhironagai77:20210117214802p:plain

NPCをどうやってパスするんだと思っていましたが、EventReceiveExecuteAIから直接得る事が出来ました。

そこさえ分かれば後は普通のUE4_AIの作成と同じです。

前回の勉強の記録もチェックしておきます。

3Dwidgetについての解説が書かれていました。

f:id:kazuhironagai77:20210117214820p:plain

これは重要な事でした。

後は、ソフトウェアデザインの観点からComponentとしてPatrolPathタイプの変数を作成しているのが注目と述べていました。これは言われてみればそうです。あの当時はゲームにおける正しいソフトウェアデザインとは何か?について凄い考えていたのでこんな事も考えていたんですね。

2.1.5 Patrolling NPC Continued

NPCがずっとパトロールする様にセットします。

取りあえず、前回の勉強ではどんな事を言っていたのか確認したら

f:id:kazuhironagai77:20210117214842p:plain

とありました。

思い出してきました。この辺はもうやりたくなくて兎に角辞める理由を探していた所に、tutorialで言っている通りに動かないのでこれ幸いとここで中止にしたのでした。

ただ、本当に Tutorial通りにやっても駄目だった場合はどこが駄目だったのか記録する必要はあったと思います。その辺も含めてもう一度やってみます。

f:id:kazuhironagai77:20210117214859p:plain

普通に出来ました。

最後までやったのですが普通に動きました。 Tutorial通りにやっても何処にも問題ありませんでした。

うーん。

これはどう総括すべきなんでしょうか?

. まとめと感想

今週は、UE4のAIの復習をやっているだけで終わってしまいました。しかしUE4のAIも一通り勉強する必要があるので、来週もこのままtutorialの続きをやる事にします。

UE4_AIはどの辺まで理解すれば基本は出来たと言えるのでしょうか?

TaskクラスのEvent Abort AIやDecoratorのObserver abortsの値であるSelf やLower Priorityも理解する必要があるのでしょうか?

この辺の疑問を解消するため、久しぶりに「Unreal Engine 4で極めるゲーム開発」を読んでみました。23章に少しだけですがかなり深堀した解説がありました。その説明は私の理解している機能とちょっと違っていたので「え!そんな意味だったの?」とかなり驚きました。TaskクラスのEvent Abort AIやDecoratorのObserver abortsの機能に関しては、来週も引き続き勉強します。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する モンスターを配置する part 2

f:id:kazuhironagai77:20210110213544p:plain

<前文>

自作ゲームを売る

今年の目標ですが、自作したゲームを販売します。

1.良いゲームを作る

個人で作成する以上、AAAのような作品は作れません。しかし買う側からしたら個人が作成したから精度が低いとかは関係ないです。買って得したゲームにしたいです。

それで色々考えています。

  • ストーリーを重視
  • ゲームをプレイした人の意見を取り入れる
  • ゲーム好きな人の意見を取り言える
  • 佳作を目指す。満点は狙わず、及第点を狙う。
  • 一点だけ凄い所を作る

1.1 ストーリーを重視

学生時代、本読むのが大好きだった私は、ストーリーを作成するのが大好きなので、この辺で他のRPGと差を出せると思います。面白いストーリーには理由があります。その辺を丁寧に整理する事で自分のゲームのストーリーを面白く出来ると思います。

1.1.1 次の町にたどり着くたびに、新しい情報を得る

ストーリーは変化です。変化は新しい情報を得る事で生まれます。

1.1.2 Character development

ユーザーが新しい情報を得る時に、ユーザーが操作するキャラもその情報を得ます。その時にそのキャラクターがどのように成長するかが、ストーリーが面白くなるかどうかの分かれ目です。

1.1.3 Character そのものの魅力

キャラが成長する前にそのキャラそのものに魅力がないとユーザーは興味を示しません。魅力的なキャラが登場する必要があります。

最近、鬼滅の刃がヒットしているらしいですが、登場するキャラの魅力が他の漫画に比べて段違いです。

1.1.4 Villain の深堀り

日本のアニメがアメリカで受けている理由の一つが、悪役が単に悪い奴でなく、もう一人の主人公である所です。Villainが何故そう言う行動を取るのかの細かい設定が大切です。

1.2 ゲームをプレイした人の意見を取り入れる

ゲームを買ってプレイしてくれた人のコメントを参考にして改良する。

1.3 ゲーム好きな人の意見を取り言える

これが大切なんですが、どうすれば意見を貰えるのか分かりません。検討中です。

1.4 佳作を目指す。満点は狙わず、及第点を狙う

バグを残して販売、他人の著作物の無断使用、などの最低限のルールを破ってしまわない様に気を付ける事。

1.5 一点だけ凄い所を作る

一点だけAAAのゲームですら出来ない凄い事をやるのだったら、materialをもっとアニメ調に変化する事とかですかね。

2. 情報ハイウェイに乗る

上記に良いゲームを作るには何が必要なのかを考察したのですが、何かが違うと思いました。そこで思いついたのがこれです。このアイデアは元々、テレビとインターネットの違いとは何なのか?を考えていて発見したのですが、かなり深堀出来そうなアイデアです。

まだどのような切り口で分析したら良いのか分かっていませんので、今回は簡単にアイデアだけを説明します。

情報ハイウェイに乗るとは、YouTubeの急上昇に載ったり、世界的なinfluencerに紹介されたりする事を指します。

ゲームを売る為には、この情報ハイウェイに乗る必要があります。しかも良い評価と共に乗る必要があります。悪い評価と共にYouTubeの急上昇に載ったらそれこそ載らない方が良かったとなります。所謂炎上というやつです。

2.1 天気予報

具体的な考えは、まだあまり発展していませんが、日本に帰って来てびっくりした事の一つが、みんなが良く天気予報を見る事でした。確かに日本では雨になると絶対に傘が必要になるので明日の天気は結構大切です。情報ハイウェイにおいて天気予報はかなり重要な位置を占めている気がしてます。

2.2 流行りに乗る事と著作権

アメリカで日本のアニメが大ブームになった時、英語圏にいた極少数のアニメの専門家は瞬く間に有名に成りました。ポケモンgoが流行った時も同じでポケモンgoについて解説するYouTuberは一気に有名に成りました。

この流行りに乗る事と情報ハイウェイに乗るはある意味同じです。しかし勝手に流行り物を自分のゲームを売るために利用したら著作権違反で訴えられてしまいます。

著作権を破る事と引用は全く違います。

簡単に言えば、誰々が著作物でこう言っていますが、私はこう思います。というのが引用です。

しかしネットの世界(日本だけでなくアメリカも)ではこれらはゴッチャにされて、地位の高い人が低い人の著作物を無断で利用する時は引用にあたり、逆の場合は著作権違反になる。と言うまるで畜生の世界のルールがそのまま、実践されています。

だからアニメの批評をするのに、そのアニメの絵を載せるのは引用であって著作権違反ではないんですが、現実では出版社の意向に沿わない批判をした場合は直ぐに著作権違反で訴えられ、本当に著作権違反かどうかも検討されない内にネットから削除されます。これは何も日本に限った事じゃなくて、アメリカでも大体同じです。最近でもあるvtuberのイラストがペドに当たるんじゃないか?と批判したYouTubeのchannelが著作権違反で一発で消されました。vtuberのイラストがペドに当たるかどうかを語るにはそのイラストを引用する必要は絶対あると思われますがお構いなしです。

この辺の事情を理解した上で、流行り物に乗りまくって情報ハイウェイに入る事が大切と考えます。

2.3 局所的に有名になる

情報ハイウェイに乗る前に、ある一部の人だけには高評価を得る。つまり局所的に有名に成る事も戦略として大事だと思います。この局所の人達に誰を選ぶのか?それを考えるのは結構難しいです。私としては日本とアメリカにいる日米のハーフやバイリンガルの人達の間で高評価を得るのがやりたいのですが、そんなネットワーク存在していませんし。プログラマーの間で高評価を得るのはかなり難しいですし。

それで思いついたのですが、近所や知り合いに売って高評価を得る。というのは結構有りかと思っています。

色々考えましたが結構方法はありそうです。それでは今週の勉強を始めます。

<本文>

今週はもっと沢山のモンスターを配置して問題を抽出します。

先週のバグについては特に今週やらないといけない部分はないと思われるのバグの直しは無しです。

1.モンスターをもっと配置してバグを見つける

以下の様に並べてみました。

f:id:kazuhironagai77:20210110213858p:plain

単にMonsterSpawnDataを増やしただけです。

f:id:kazuhironagai77:20210110213915p:plain

戦闘してみます。

途中で死ぬかと思ったらレベルが上がって全回復して全部の敵を倒せました。面白く無いです。ゴブリンを倒して得られる経験値を調節します。

f:id:kazuhironagai77:20210110213934p:plain

もう一度戦ってみます。

今度は死にました。

そしたらエラーです。

f:id:kazuhironagai77:20210110213952p:plain

これから直していきます。

戦闘で負けた時に実行するコードを以下の様に変更しました。

f:id:kazuhironagai77:20210110214008p:plain

テストします。

簡単にテスト出来るように主人公のパラメーターをモンスターより弱くします。

f:id:kazuhironagai77:20210110214123p:plain

エラーは出なくなりました。

f:id:kazuhironagai77:20210110214141p:plain

しかし沢山のバグがまた出て来ました。

バグ1

戦闘で主人公が死んでいるのに復活しています。

f:id:kazuhironagai77:20210110214205p:plain

バグ2

「もう一度始める」ボタンを押すと戦闘から始まります。戦闘前の場所から始まるべきです。

f:id:kazuhironagai77:20210110214315p:plain

1.1  バグ1の直し

Set Anim Instance Classを外しました。

f:id:kazuhironagai77:20210110214338p:plain

むごたらしく死んでいます。

f:id:kazuhironagai77:20210110214358p:plain

1.2 バグ2 の直し

Game Overになると以下のwidgetが開かれるのですがどこから開いているのかが分かりません。

f:id:kazuhironagai77:20210110214516p:plain

幾らBPを見てもわからないのでUE4C++を見たら、こっちで実装していました。

f:id:kazuhironagai77:20210110214535p:plain

これを読むと、Widgetの方だけ変更すればバグは直せそうです。

Widget、GameOverScreenのreStartButtonをクリックした時に実行するコードを以下の様に変更しました。

f:id:kazuhironagai77:20210110214556p:plain

テストします。

「もう一度始めるボタン」を押すとMap1の元の位置に戻って来ました。

f:id:kazuhironagai77:20210110214739p:plain

直りました。

1.3 GameOverScreenのデザインの改良

GameOverScreenに、少なくとも「止める」「スタート画面に戻る」ボタンの追加は必要だと思います。

以下の様に作り直しました。

f:id:kazuhironagai77:20210110214905p:plain

UIのみ操作出来る様にSetInputModeUIOnlyを追加しました。

f:id:kazuhironagai77:20210110214932p:plain

SetInputModeUIOnlyを追加しましたが、このやり方が正しいのか正直覚えていません。戦闘画面に入るとcharacterの操作は全く出来ませんが、その時にSetInputModeUIOnlyを使用している形跡が全く無いです。後でもう一度この事は調べます。

「もう一度始めるボタン」の実装は以下の様にしました。

f:id:kazuhironagai77:20210110215002p:plain

特に記述する事はありません。前の実装ではExecute Console Commandを使用していました。

「ゲームの終了ボタン」は以下の様に実装しています。

f:id:kazuhironagai77:20210110215026p:plain

QuitGameを使用するのは初めてです。使用方法に特に気を付ける事は無いはずです。

「スタート画面に戻るボタン」の実装は以下の通りです。

f:id:kazuhironagai77:20210110215105p:plain

RPGGameInstanceに保持しているデータは全て消す必要があると考えていますが、後でその辺は直します。

テストします。

f:id:kazuhironagai77:20210110215127p:plain

即興で作成した割には中々いい感じです。

この画面でキャラを操作しようとしましたが動きません。SetInputModeUIOnlyを使用するのが正しいやり方なのか一寸覚えていませんが、目的は達せられています。

「もう一度始めるボタン」をクリックします。

f:id:kazuhironagai77:20210110215203p:plain

戦闘が始まる前に戻って来ました。

セーブポイントを作成したら、その場所からやり直すようにしたいです。

「ゲームの終了ボタン」を押したら、ゲームが終了しました。

「スタート画面に戻るボタン」を押したらゲームの最初に戻りました。

f:id:kazuhironagai77:20210110215224p:plain

スタート画面のボタンは新しく始める以外は適当にしか作成していないので、後でこれらも直します。

2. 更なるバグの発見

更にバグを見つけるために配置したモンスターを倒していきます。

2.1  向きの保持

戦闘画面から頻繁に元のマップに戻ってくると酔い易くなっている事に気が付きました。

2.2 戦闘が永遠に終わらないバグ

プレイヤーが操作するキャラがすぐ負けてしまうので、武器と防具を装備させて戦闘に参加したら、お互いの防御力が互いの攻撃力を上回り、お互いにダメ―ジが与えられなくなりました。

f:id:kazuhironagai77:20210110215307p:plain

戦闘が終わりません。

2.3 拾えるアイテムは(!!)表示を追加

戦闘システムのバグではありませんが、NPCとの会話の様に、拾えるアイテムは(!!)で知らせてほしいです。

f:id:kazuhironagai77:20210110215334p:plain

(!!)をアイテムに追加します。

2.4 徘徊するモンスター用の別なAIが必要

プレイヤーの操作するキャラがモンスターが動ける領域に侵入すると、全てのモンスターが、プレイヤーの操作するキャラ目がけて追いかけて来ます。

f:id:kazuhironagai77:20210110215358p:plain

f:id:kazuhironagai77:20210110215410p:plain

非常に単調な動きで、不自然に感じます。

2.5 待ち伏せするモンスターの作成

ある領域に侵入するとモンスターが出現するパターンも面白いと思います。

2.6 武器を装備した状態で戦闘開始したらアニメーションが…

武器を装備した状態で戦闘開始したらアニメーションが最初の戦闘の時だけ武器持ちのアニメーションで、2回目から武器なしのアニメーションになってしまいました。

f:id:kazuhironagai77:20210110215503p:plain

正直、こんなに沢山のバグが見つかってショックです。バグ出しに挑戦して良かったです。

3. バグだし条件の設定

プレイヤーの操作するキャラのパラメーターの値を変えて、8体のモンスターと戦う事で、バグを見つけようと思います。

3.1 魔法使い的なパラメーター

プレイヤーの操作するキャラのパラメーターの値を、魔法中心で戦う事を強いられる値に変更しました。

f:id:kazuhironagai77:20210110215544p:plain

一体倒すと、HPが40、MPが70、そして金貨が110になっていました。

f:id:kazuhironagai77:20210110215605p:plain

因みにポーズ画面を押すとモンスターの追跡も止まっていました。

f:id:kazuhironagai77:20210110215630p:plain

MPは70も残っていますが、HPは40しかありません。道具屋に行って回復薬を買えるだけ買って残りのモンスターと戦う事にします。

アイテムの回復薬を使用した後に、ゴブリンの攻撃を受けたら、アイテム使用時用のコメントが表示されました。

f:id:kazuhironagai77:20210110215649p:plain

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

一応、8体全部倒せました。しかし持っていた金貨100枚が0になってしまいました。

f:id:kazuhironagai77:20210110215715p:plain

ゲームを終了したらエラーが出ていました。

f:id:kazuhironagai77:20210110215740p:plain

戦闘中に流しているBGMを保持しているはずのBattleBGMに値が無いと言っているみたいです。

f:id:kazuhironagai77:20210110215804p:plain

これは多分ですが、戦闘中、ほったらかしてブログを書いていたら戦闘BGMの演奏が終わったので、勝手に消滅してしまったと思われます。

確認します。

戦闘BGMが終了する前に戦闘が終了した時は、ゲームを終了してもさっきのエラーは出て来ません。

f:id:kazuhironagai77:20210110215839p:plain

今度は戦闘BGMが終了するまでほっときます。

エラーが出ました。

f:id:kazuhironagai77:20210110215904p:plain

エラーの内容は微妙に違いますが、同じ個所です。これだけは今直してしまいます。

元の音源のLoopingにチェックを入れます。

f:id:kazuhironagai77:20210110215922p:plain

これで戦闘中に戦闘BGMが終了する事はないはずです。

テストしてみます。

戦闘中にして、10分位別な作業していましたが、戦闘BGMが続いていました。戦闘終了後にゲームを終了してもエラーは出ませんでした。

直りました。

3.2 初級者的なパラメーター

今度はMMPを20まで下げてみました。

f:id:kazuhironagai77:20210110215948p:plain

これでテストしてみます。

最後の2体が倒せずに死んでしまいました。

f:id:kazuhironagai77:20210110220017p:plain

ゲームオーバーの時は、専用の音楽がほしいですね。

特にバグは見つかりませんでしたが、モンスターやプレイヤーが操作するキャラのパラメーターの調節は、ゲームの面白さに直結する事が分かりました。

3.3 ギリギリモンスターに勝てない位のパラメーター

やっぱり最初はモンスターにぎりぎり勝てない位が良い気がします。以下のパラメーターで試してみます。

f:id:kazuhironagai77:20210110220047p:plain

一体も倒せず負けました。

f:id:kazuhironagai77:20210110220215p:plain

現在は負けてもペナルティが一切ないので何か作成したいです。

落ちている剣を拾って装備しました。

f:id:kazuhironagai77:20210110220249p:plain

その状態で戦ったらギリギリ勝てました。

一回死んでからアイテムが置いて在る場所に戻ると、既にアイテムを取ったにもかかわらず新しいアイテムが置いて在りました。

f:id:kazuhironagai77:20210110220308p:plain

これも後で直します。

特に新しいバグは見つかりませんでした。

3.4 プレイヤーが操作するキャラのパラメーターの調節で分かった事

最初はモンスターが倒せない位で、短剣を装備したらギリギリ一体倒せる位、更に盾を装備する事で、沢山のモンスターと戦えるのが最適な値と感じました。これを達成する為にはもはやゲーム一部を作成するだけでなくゲームそのものの作成が必要と思いました。

回復薬ですが、戦闘後に使用するには有効ですが、戦闘中に回復薬を使用しても意味がないです。これはこれで面白いと思いました。

4. バグの直し

これまでに以下のバグが見つかりました。

  • 戦闘画面から戻って来た時に向きが変わっている。
  • 戦闘が永遠に終わらない時がある。
  • 拾えるアイテムにも(!!)表示を追加
  • 徘徊するモンスター用に別なAIを作成
  • 待ち伏せするモンスターの作成
  • 2回目から武器なしのアニメーションになるバグの直し
  • 戦闘中にアイテムの回復薬を使用した場合、次の攻撃の後にもアイテム使用時用のコメントが表示されるバグ
  • 同じアイテムが何回も取れるバグ

これらのバグを直していきます。

4.1 戦闘画面から戻って来た時に前と同じ向きにする

まず、戦闘が始まった場所をどうやって保持しているのかを確認します。

モンスターの領域にプレイヤーが操作するキャラが侵入するとRPGGameModebaseBPにあるdispatcherのCallCombatBeginがcallされます。

f:id:kazuhironagai77:20210110220404p:plain

RPGGameModebaseBP にあるCombat BeginはMap1上で以下のコードにbindされています。

この時にRPGGameInstaceBPにある変数、Third Person Character Locationにプレイヤーが操作するキャラの現在の位置が保持されます。

f:id:kazuhironagai77:20210110220609p:plain

取りあえずここまでRotationも同じように作成します。

RPGGameInstaceBPに、Third Person Character Locationにプレイヤーが操作するキャラの現在の回転を保持するための変数、ThirdPersonCharacterRotationを作成します。

f:id:kazuhironagai77:20210110220636p:plain

Combat Beginの実装部にこの変数に、プレイヤーが操作するキャラの現在の回転値をコピーさせます。

f:id:kazuhironagai77:20210110220655p:plain

これで戦闘画面に移動する前のプレイヤーが操作するキャラの向きは保持されました。

その値は、もう一度Map1が開かれた時に読み込まれていたので、その時に一緒にプレイヤーが操作するキャラの向きが読み込まれるようにしました。

f:id:kazuhironagai77:20210110220715p:plain

テストします。

f:id:kazuhironagai77:20210110220733p:plain

キャラクターの向きは直っていましたが、カメラがキャラクターを向いています。

その結果、カメラを一回転する必要が出て来て、結局酔います。

色々弄りましたが、カメラの位置の直し方が分かりませんでした。今回はここまでとしてカメラの位置の直しは後でやる事にします。

4.2 戦闘が永遠に終わらない時がある。

お互いの防御力が攻撃力より高い場合、ダメージを与える事が出来ないので永遠に戦闘が続きます。

これは「逃げる」ボタンが機能すれば解決します。

逃げるが成功するためにはCharacterのLuckが敵のLuckより上である必要があります。

f:id:kazuhironagai77:20210110220804p:plain

取りあえず、以下の様に設定を変更してLuckの値が同じならば逃げる事が出来る様にします。

f:id:kazuhironagai77:20210110220824p:plain

テストします。

逃げれましたが以下のようにエラーが大量に発生しました。

f:id:kazuhironagai77:20210110220856p:plain

以下の実装部のSetVolumeMultiplierがEmptyと言っています。

f:id:kazuhironagai77:20210110220924p:plain

調べて見たら、DownVictoryMusicを戦闘から逃げた時もtrueにセットしていました。

f:id:kazuhironagai77:20210110220947p:plain

直します。

テストします。

f:id:kazuhironagai77:20210110221014p:plain

今度はエラーが無くなりました。

f:id:kazuhironagai77:20210110221031p:plain

Luckが敵のモンスターより低い場合でも30%の確率で逃げ出せるようにしました。

これで逃げるボタンを押し続ければいつかは戦闘から逃げ出せるように成ったはずです。

テストします。

敵のモンスターのLuckをプレイヤーが操作するキャラよりも高く設定しました。

f:id:kazuhironagai77:20210110221050p:plain

ひたすら、戦闘から逃げたら4回目くらいで逃げれました。

f:id:kazuhironagai77:20210110221110p:plain

4.3 拾えるアイテムにも(!!)表示を追加

まず、NPCにどうやって(!!)を追加したのか確認します。

f:id:kazuhironagai77:20210110221131p:plain

TextRenderを使用しています。

TextRenderのvisibilityは以下の方法でオンオフされています。

f:id:kazuhironagai77:20210110221155p:plain

結構複雑な仕組みをしています。

思い出してきました。最初、Box内に侵入したらTextRenderのvisibilityをオンにセットしていたのですが、この方法だと全部のNPCのtextRenderがOnになってしまったんです。それで上記のような方法を採用したんでした。

ただこの方法だと、まだ一つ一つのinstanceまでは区別出来ないので、以下の方法でやる事にします。

まず、RPGGameModebaseBPにこれから拾うアイテムの名前を保持する変数を作成します。

f:id:kazuhironagai77:20210110221222p:plain

次に、DroppedItemBase内にそれぞれのインスタンスの名前を保持するための変数、ItemNameを作成します。

f:id:kazuhironagai77:20210110221239p:plain

f:id:kazuhironagai77:20210110221248p:plain

このItemNameはInstance Editableにしてそれぞれのinstanceで違う名前が持てるようにします。

f:id:kazuhironagai77:20210110221315p:plain

更に、TextRenderとPointLightをDroppedItemBaseに追加します。

f:id:kazuhironagai77:20210110221337p:plain

プレイヤーが操作するキャラがボックス内に侵入したら、そのボックスが属しているDroppedItemBaseのインスタンスが持つItemNameの値、つまり名前を、RPGGameModebaseBPのItemNameにコピーします。

f:id:kazuhironagai77:20210110221356p:plain

勿論、プレイヤーが操作するキャラがボックス外に出た時は、RPGGameModebaseBPのItemNameにNoneをセットする事で、それまでセットされていた名前を消します。

f:id:kazuhironagai77:20210110221442p:plain

そして、RPGGameModebaseBPのItemNameにセットされている名前とプレイヤーが操作するキャラが侵入しているboxが属しているDroppedItemBaseのインスタンスが持つItemNameの名前が一致しているか確認します。

f:id:kazuhironagai77:20210110221506p:plain

一致した場合は、設置したPointLightとTextRenderが表示されます。しない場合は表示されません。

これで放置されているアイテムが拾える場合は、そのアイテムの上に(!!)が表示されるはずです。

テストしてみます。

(!!)マークが表示されました。きちんと剣だけ表示されています。隣の盾には(!!)マークは表示されていません。

f:id:kazuhironagai77:20210110221530p:plain

念のために、隣に移動して見ると今度は盾の上に(!!)が表示されました。そして剣の上の(!!)は消滅しています。

f:id:kazuhironagai77:20210110221547p:plain

出来ました。

4.4 徘徊するモンスター用に別なAIを作成

現状では、全てのモンスターは、プレイヤーが操作するキャラが、NavMeshBoundsVolumeが指定した範囲内に侵入した途端、全力でそのキャラを追いかけるだけです。持ち場を設定してその範囲内に、プレイヤーが操作するキャラが侵入した時だけ追いかけるようなAIを作成したいです。

つい最近UE4のAIの勉強をやったのに既にやり方を忘れてしまいました。やる内容が大量にありそうなので、この部分は別に枠をとって後でやる事にします。

4.5 待ち伏せするモンスターの作成

ある場所にプレイヤーが操作するキャラが侵入したらモンスターが突然発生しだすトラップ的な物を作成したいです。

以下の様に作成しました。

f:id:kazuhironagai77:20210110221617p:plain

MonsterTrapの方ですが、Boxを追加しただけのActorです。

f:id:kazuhironagai77:20210110221636p:plain

このBox内にプレイヤーが操作するキャラが侵入したら以下のコードによってモンスターが発生します。

f:id:kazuhironagai77:20210110221719p:plain

突然、モンスターが発生するとユーザーが混乱するかもしれないので、以下のwidgetも追加しました。

f:id:kazuhironagai77:20210110221737p:plain

テストします。

以下の様にモンスターが現れました。結構急に出て来ます。

f:id:kazuhironagai77:20210110221754p:plain

これでOKとします。

4.6 二回目から武器なしのアニメーションになるバグ

これは一番直さなければならないバグです。

原因が分かりました。

PlayAnimationノードを使用しているため、いつも使用しているMyThirdPerson_AnimBPを一端外している様です。そのためMyThirdPerson_AnimBPの変数であるWithWeaponの値が消滅してしまったようです。

f:id:kazuhironagai77:20210110221821p:plain

この事からキャラのアニメーションはAnimBPで一括して管理した方が良くPlayAnimationノードの使用は中止した方が良いと思われます。

ただし、この理論が正しいかの確認をするために、一端、以下に示したコードを戦闘で武器を装備した状態で攻撃をした後に追加しました。

f:id:kazuhironagai77:20210110221839p:plain

これでテストします。

f:id:kazuhironagai77:20210110221856p:plain

出来ています。

理論は正しい様です。

PlayAnimationノードの使用は中止してキャラのアニメーションはAnimBPで一括して管理する。これも結構な量になりそうなので別に枠をとって後でやります。

4.7 戦闘中にアイテムの回復薬を使用した場合、次の攻撃の後にもアイテム使用時用のコメントが表示されるバグ

理由が分かりました。

プレイヤーが操作するキャラがアイテムを選択すると対戦しているモンスターの選択も自動的にアイテムになってしまっています。

以下の方法で直しました。

f:id:kazuhironagai77:20210110221920p:plain

Itemを選択したと判断されてもその後で、プレイヤーが操作するキャラかモンスターを検査します。モンスターの場合はAttackと同じセリフになるようにします。

テストします。

f:id:kazuhironagai77:20210110221937p:plain

プレイヤーが操作するキャラがアイテムを使用した後に、モンスターが攻撃した時も正しいセリフが選択されています。

直りました。

4.8 同じアイテムが何回も取れるバグ

4.8.1 BPを使用してアイテムを動的に生成する

現状、マップ内に直接Instanceを配置したケースしかないので、BPで動的に作成したItemをマップ内に配置出来る様にします。

まず、Dropped Item Baseを作成するためには、最低でも以下のパラメーターが必要です。

f:id:kazuhironagai77:20210110222008p:plain

それで以下のStructureを作成しました。

f:id:kazuhironagai77:20210110222025p:plain

そのStructureをタイプとしたArray、ItemSpawnDataをRPGGameInstanceBPに作成しました。

f:id:kazuhironagai77:20210110222048p:plain

試しに2つほど要素を作成しておきます。

f:id:kazuhironagai77:20210110222111p:plain

今度はmap1内でSpawnItemsと名付けた関数を作成します。この関数はItemをSpawnするための関数です。以下に実装部を示します。

f:id:kazuhironagai77:20210110222134p:plain

Map1のEventBeginPlay関数でこの関数を呼び出します。

f:id:kazuhironagai77:20210110222153p:plain

テストします。

f:id:kazuhironagai77:20210110222211p:plain

Itemは出来ています。

BPを使用してItemを動的に生成する事が出来ました。

4.8.2 収得したアイテムが二度と生成されないようにする。

PickUpItemウィジェットに以下の関数を作成します。

f:id:kazuhironagai77:20210110222234p:plain

f:id:kazuhironagai77:20210110222242p:plain

この関数を「拾う」ボタンが押された時に実行すれば、そのボックス内にあったアイテムは二度と生成されなくなるはずです。

f:id:kazuhironagai77:20210110222301p:plain

テストしてみます。

アイテムを拾った後、戦闘を行い、map1に戻って来ました。拾ったアイテムがある位置に行ってみるとその場所にあったアイテムは生成されていませんでした。

f:id:kazuhironagai77:20210110222332p:plain

一応、完成です。

5.まとめと感想

今週はここまでとします。

今年は、正月返上でやろうとしたのですが、結局休んでしまいました。だから今回は2週間分まとめてブログに載せています。

 

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する モンスターを配置する。

f:id:kazuhironagai77:20201227204610p:plain

<前文>

発音記号の謎

前回、言い足りなかった事を補足します。IPAのVowel Diagramは口の開く大きさ、下の形、唇の丸さで、全ての言語の母音を表せるはずです。更に一つの音に一つのsymbolが割り当てられているはずで、英語のaのように二つ以上の音を一つのsymbolで表す事は禁じられているはずです。

所が、IPAのVowel Diagram でʌの位置を見ると、open-midのBackで唇は丸めない。とありますが、別の特にアメリカで作成されたVowel Diagramの内の幾つかはʌが中央に位置しています。

これは、

  1. ʌが2つの別な音を一つのシンボルで表している。
  2. 口の開く大きさ、下の形、唇の丸さだけでは、英語の音を正確に表す事は出来ない。

のどちらかになってしまうのではないのか?そうなるとどちらの場合も発音記号の定義に矛盾が生じるのではないでしょうか?

と言う事が言いたかったんです。

言語学は門外漢ですが、これって熱力学で仕事の向きによって式に‐がついたり、つかなかったりするのとは訳が違うレベルの矛盾に見えます。

発音記号に拘る理由

私が今回、こんなに発音記号に拘ったのは、こんな問題を抱える発音記号ですが、可能性も秘めている気がしているからです。口の開く大きさ、下の形、唇の丸さで、音を指定すると言う方法は誰でも学べます。Minimal pairのアプリを作成していて分かった事の一つに、Minimal pair で訓練すると大人でもɑ 、æ 、ʌの音の違いは直ぐに理解出来るのですが、次の日になると簡単に忘れてしまいます。

所が、顎の下に手を置いて、ʌの音を発する時は顎が絶対下がらない様に、逆にɑの音の時は顎が必ず下がる様にアと言うと教えると、誰でもそれなりにɑ とʌの音を区別して発せる事が出来ます。Minimal pair で訓練した時に比べて音の違いを認識する事に関しては非常に劣りますが、この方法だと次の日になっても忘れません。

この二つの学習方法は補い合う事で、学習者に正しい発音を教授する事が出来るのではないでしょうか?と思っているからです。

発音記号に拘る理由2

全く個人的な意見ですが「Rの音は3つある。Lの音は2つある。」気がしています。

これは、minimal pairでrとlを比較して分かった事なんですが、Rの音には、

  1. Erなどのr音で、発音記号で言うɝ
  2. 動物が唸るような音が付随するr
  3. 日本語のラリルレロと全く区別付かないr

の3つが少なくとも私にとっては存在しています。

そしてL の音には、

  1. Elなどのl音で、所謂dark lの音。多くのアメリカ人はThのように舌を突き出してこの音を発音している。n音に似ている。
  2. 日本語のラリルレロと全く区別付かないl

の二つが存在しています。

しかし普通のアメリカ人にとってはこれらの音は全てLかRの音で2つの音です。

口の開く大きさ、下の形、唇の丸さで音を指定すると、これらの音が違う音であると指定出来ます。そしてそうすると学習する方は非常に楽になります。

日本人に特化した英語発音の訓練方法

余りに発音記号ばっかり検索していたらYouTubeのお勧めが全て発音記号に関するビデオになってしまいました。その中で面白かったのが、日本人に特化した英語発音の訓練方法を紹介しているアメリカ人が何人もいた事です。その中で特に優秀だと思った人の英語発音の説明ですが、口の開く大きさ、下の形、唇の丸さ、に

  • 顎の付け根の開く大きさ、
  • 唇の横に対する開き具合、
  • イントネーション

が追加されていました。英語の文を読むのにイントネーションが大切なのは実感していますが、英語の音素にもイントネーションが大切とは盲点でした。

後、私が追加したいのはあいうえおなどの日本語の発音です。日本人は、日本語の発音が完璧なのにそれを利用しない手はないです。

英語発音の学習用ソフトウェア

発音を聞いてVowel Diagram内にその人の発音を記録するソフトウェアがYouTubeのお勧めに出てました。試してみた所、再現性に難がありましたが面白い試みだと思いました。

Scientific computing は元々、数値解析やその計算結果の可視化から始まったため、化学、工学、医学なとの所謂理系の研究とは密接に結びついていますが、言語学などの文系分野との連携はあんまり聞きません。先程出てたʌの音ですが、ʌを構成する要素(口の開け具合、舌の位置、唇の形、イントネーションなど)を定性的に特定して、それらを定量的に測定出来れば、英語学習者がもっと簡単にʌの音を理解出来るソフトウェアが開発出来ると思われます。

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

<本文>

先週、戦闘システムを一応完成させてから色々考えたんですが、バグは沢山あるだろうと。その沢山のバグを見つけるためには、いっぱい戦闘する必要があると思いました。色々な条件で、沢山の戦闘を行うには、マップ上に沢山のモンスターを配布して、プレイヤーが操作するキャラにそのマップ内を探検させるのが最適だと思います。

後、先週、戦闘時のBGMを追加すると言って忘れていました。それを追加します。

今週の作業

  • 戦闘時のBGMの追加
  • モンスターを配置する

1.戦闘時のBGMの追加

1.1 BGMを追加します。

マップBattleFieldに移ったら流れるBGMを追加しました。

f:id:kazuhironagai77:20201227204946p:plain

Mega Game Music Collectionの音楽を使用しました。

f:id:kazuhironagai77:20201227205005p:plain

無料の時に貰ったんですが、今見たら5000円位の値段で売られていました。無料の時に貰っておいて良かったです。

1.2  「ボタン」を押したら音がするようにする

クリックした時に音がしたら、クリック出来た事が簡単に確認出来ますね。

f:id:kazuhironagai77:20201227205028p:plain

しました。音はstarter contentsにあるサンプルを使用しました。

f:id:kazuhironagai77:20201227205048p:plain

その他のボタンにも音を追加します。

f:id:kazuhironagai77:20201227205107p:plain

1.3 勝利の時に別の音楽が流れるようにする

そのためには、まず今まで流れていた音楽を止める必要があります。

このサイトに以下の様に書かれていました。

f:id:kazuhironagai77:20201227205136p:plain

マジで…

やり直します。

調べたら、BattleFieldのBGM以外は直す必要がなさそうなのでそれだけSpawnSound2Dに変更します。

f:id:kazuhironagai77:20201227205206p:plain

またLevelBPとWidgetでreferenceのやり取りをする必要が出て来ました。

RPGGameModeBaseBP内でEvent Dispatchersを使用します。名前はCombatOverVictoryとします。

f:id:kazuhironagai77:20201227205226p:plain

BattleFieldのレベルマップ内でbindします。

f:id:kazuhironagai77:20201227205245p:plain

戦闘に勝利した時に、CombatOverVictoryを呼び出してBGMを止めます。

f:id:kazuhironagai77:20201227205301p:plain

試してみます。

音楽はここには載せられませんが、音がぶつ切りです。徐々に音を小さくしたいです。

調べたのですが、音を徐々に小さくする機能はないみたいなので、以下の様にしました。

f:id:kazuhironagai77:20201227205319p:plain

BattleFieldのTick()関数に以下のように実装しました。

f:id:kazuhironagai77:20201227205337p:plain

試します。

綺麗にBGMがfade outしました。

Victoryの音楽も同様にします。

と思ったのですが、victoryの音楽を流しているRPGGameModeBaseBPはTick()関数をUE4C++ で使用していて呼べなかったです。

前に試した実験では、UE4C++ のTick()関数内でSuperを使用すればBPで Event Tick()が呼べたはずです。試してみましょう。

f:id:kazuhironagai77:20201227205356p:plain

BGMの音を小さくした方法と全く同じやり方で実装しました。

f:id:kazuhironagai77:20201227205414p:plain

試してみます。

音がいきなり小さくなってなくなってしまいました。

音を小さくするのは最後にしました。

f:id:kazuhironagai77:20201227205432p:plain

最低限の音は出る様になりました。

2. モンスターの配置について

2.1 モンスターの出現方法についての考察

モンスターの出現方法ですが、現在3つ知っています。

  1. UE4C++からSpawnする。
  2. BPからSpawnする。
  3. 直接Map上に配置する。

それぞれの長所と短所を明らかにして、どの方法でモンスターを配置するのがベストなのかを決定します。

Googleで調べたんですが全然、この話題、全然引っかからないです。

それで、自分でまとめてみました。一応断っておきますが、あくまで現状における私の推測なので実際は違うかもしれません。

2.1.1 動的な生成か、静的は生成か

BPからspawnする場合は動的、直接map上に配置する場合は静的と表現されています。この表現を一般的なC++と同じ意味だと考えると、直接map上に配置する場合はモンスターのデータはstack上に、BPからspawnする場合はheap上に配置されると推測されます。UE4C++はscript言語としてC++を採用しているだけで、BPと機能は同じである。と考えるとUE4C++からSpawnする場合も動的にモンスターを生成していると考えられます。

このゲームのモンスターはcharacter classから派生していますので、サイズは結構大きいはずです。動的な生成方法の方が良いと思われます。

2.1.2  生成のスピード

モンスターは大量に生成するので、生成のスピードが速いほうが好ましいです。この点ではUE4C++が最も優れていると考えられます。

2.1.3  生成のランダム性

UE4C++とBPはランダムな位置にモンスターを生成出来ますが、直接mapに配置する方法はランダムな位置での生成は出来ません。

2.1.4  Compileの時間

UE4++は一寸コードを変えるだけでもbuildに時間が取られますが、BPは直ぐに終わります。

2.1.5  モンスターの出現位置の可視化

動的にモンスターを生成する場合は、実際にゲームをプレイするまでマップのどの辺にモンスターが出現するのか不明です。直接Map上に配置するとその辺りの事は段違いに分かり易いです。

2.1.6  まとめ

それぞれの方法に長所と短所がある事が分かりました。それらの点を考慮すると制作の段階において、変えるのがベストな気がします。

初期段階:試しにモンスターを生成する

この時は、直接map上に配置するのがベストと思われます。ゲームをプレイしなくても、モンスターの位置を視覚的に確認出来ます。ある面積内にランダムにモンスターを生成したい時だけ、BPを使用すべきです。

中期段階:モンスターの生成する位置がどんどん決定していく

一端、モンスターの生成する位置が決定したら、BPで動的に生成する方法に変更すべきです。上記の理論が正しいと言う前提の話ですが、そうする事でstack memoryへの負担が減り、沢山のモンスターを生成出来ると思われるからです。

最終段階:モンスターの生成場所が完全に決定した後

全てのモンスターの生成をBPでするようになったら、製品化する前にUE4++でモンスター生成の部分を書き直すべきです。そうする事で素早くモンスターを生成出来るしPCへの負担も少なくなるはずだからです。

2.2 モンスターを直接Map上に配置してみる

直接map1にモンスターを配置してみます。

f:id:kazuhironagai77:20201227205625p:plain

あれ、モンスターがいません。

モンスターBPを見たら、モンスターが出現する時のアニメーションを追加していました。

f:id:kazuhironagai77:20201227205645p:plain

このアニメーションのせいでモンスターが地面に潜っています。

f:id:kazuhironagai77:20201227205704p:plain

それはともかくとしてこの方法で生成したモンスターでも機能するのか試してみます。

f:id:kazuhironagai77:20201227205722p:plain

領域内に侵入すると凄い勢いで追いかけて来ます。

f:id:kazuhironagai77:20201227205743p:plain

捕まると戦闘画面になります。

モンスターを倒した後、map1に戻ってくると、復活したモンスターとの戦闘がもう一度始まってしまいます。

2.2.1 モンスターが復活しないようにする

色々な方法を検討したんですが、BPで動的にモンスターをspawnさせてから復活しないようにする方が簡単であるとの結論になりました。のでそっちを先にやります。

2.3 モンスターをBPから動的に生成してみる。

今のモンスターの位置を記録しておきます。

f:id:kazuhironagai77:20201227205817p:plain

同じ場所にモンスターを生成してみます。

f:id:kazuhironagai77:20201227205836p:plain

テストします。

f:id:kazuhironagai77:20201227205906p:plain

出来てました。簡単過ぎて拍子抜けです。

2.4 倒したモンスターを消滅させる

2.4.1 モンスターを2体spawnさせる

倒したモンスターだけを消滅させる必要があるので、モンスターを2体spawnさせます。

モンスターが徘徊する場所を作成します。

f:id:kazuhironagai77:20201227205930p:plain

それぞれのモンスターを管理するためのstructを作成します。

f:id:kazuhironagai77:20201227205948p:plain

RPGGameInstanceBP内にこのstructを使用してArrayを作成します。

f:id:kazuhironagai77:20201227210012p:plain

このArrayでそれぞれのモンスターの消滅を管理します。

Map1のレベルBP内に以下の関数を作成します。この関数でモンスターをspawnします。

f:id:kazuhironagai77:20201227210035p:plain

上記の関数、SpawnMonstersをLevelBPのEvent BeginPlay内で呼びます。

f:id:kazuhironagai77:20201227210058p:plain

テストします。

f:id:kazuhironagai77:20201227210116p:plain

モンスターが2体出現しています。

成功ですが、向きがオカシイです。調節します。

f:id:kazuhironagai77:20201227210135p:plain

直しました。

ついでに戦闘も試してみます。

f:id:kazuhironagai77:20201227210150p:plain

手前のモンスターの領域にしか入っていないのに、後ろのモンスターも動き始めました。しかも全く同じ動きをしています。

これは直さないと。

確認のためNav Mesh Bound Volumeを以下の様に設置してモンスターの発生箇所を道の端に変更しました。

f:id:kazuhironagai77:20201227210210p:plain

2匹のモンスターは別々な動きをしていました。

f:id:kazuhironagai77:20201227210227p:plain

正し、モンスターのAIをもう少し発展させる必要はありそうです。この辺は来週以降、検討します。

2.4.2 MonsterBPの改良

倒したモンスターのみ消滅するためには、MonsterBPから作成したそれぞれのInstanceに名前を付ける必要があります。ので新しいnameタイプの変数をMosterBPに追加します。MonsterNameと名付けました。

f:id:kazuhironagai77:20201227210258p:plain

後、この変数の初期化を忘れるとモンスターを倒した時に初期化しないので、Expose on Spawnにチェックしました。

MonsterBP生成時に、MonsterNameのargumentをパスするように変更します。

f:id:kazuhironagai77:20201227210316p:plain

Play中に生成されたそれぞれのモンスターのMonster Nameを見ると、

f:id:kazuhironagai77:20201227210332p:plain

f:id:kazuhironagai77:20201227210340p:plain

それぞれの名前が保持されています。

2.4.3 倒したモンスターのみを消滅させる

RPGGameInstanceBPにNameタイプの変数を作成します。

f:id:kazuhironagai77:20201227210404p:plain

ここに戦闘中のモンスターの名前を記録しておきます。

以下に示した様に、戦闘開始時にmonsterBP内で記録しました。

f:id:kazuhironagai77:20201227210421p:plain

戦闘終了時に、以下に示した方法でRPGGameInstanceBP内のMonsterSpawnData内にある戦闘中のモンスターのSpawnMonsterをfalseに変更します。

f:id:kazuhironagai77:20201227210440p:plain

ゴチャゴチャし過ぎて分かりづらいですが、後で綺麗に整理し直します。

Boolean変数のSpawn MonstersをfalseにしてしまうとUE4C++から生成する全てのモンスターの生成を止めてしまうので、一寸外しておきます。

テストします。

一体目のモンスターを倒してmap1に戻ってきた所です。

f:id:kazuhironagai77:20201227210457p:plain

一体目のモンスターは消滅していますが、二体目のモンスターは普通にいます。ここまでは計画通りです。

二体目のモンスターも倒しました。戦闘画面から戻ってくるとモンスターは消滅しています。

f:id:kazuhironagai77:20201227210542p:plain

成功しました。

2.5 UE4C++で生成したモンスターについて

こっちのモンスターはUE4C++から生成しているので戦闘で倒しても消滅しません。Boolean変数のSpawn Monstersをtrueのままにしているからです。

f:id:kazuhironagai77:20201227210604p:plain

UE4C++から生成しているモンスターは、今は単なる邪魔者なので生成するのを中止しておきます。

f:id:kazuhironagai77:20201227210622p:plain

消えました。

f:id:kazuhironagai77:20201227210640p:plain

特に問題もないみたいです。

2.6 Error 表示について

あれ、Output Logにエラーが表示されています。

f:id:kazuhironagai77:20201227210704p:plain

調べます。

こいつのせいでした。

f:id:kazuhironagai77:20201227210719p:plain

正直すっごい時間かかりました。すっかりこれを作成した事を忘れていました。消します。

直りました。

2.7 MonsterBPに追加したMonsterName変数に対する考察

f:id:kazuhironagai77:20201227210741p:plain

Monster Nameはexpose spawnと設定したのでMonsterBPを生成する時にMonster Nameをパラメーターとしてパスする必要があります。

こんな感じにです。

f:id:kazuhironagai77:20201227210759p:plain

しかしUE4C++からMonsterBPを生成する場合、以下に示した様に、モンスターを生成するためのクラスはMonsterクラスの派生クラスである事しか指定していません。

f:id:kazuhironagai77:20201227210815p:plain

f:id:kazuhironagai77:20201227210823p:plain

そしてMonsterクラスの派生クラスである、MonsterBPはその条件に一致するため生成する事が出来ます。

f:id:kazuhironagai77:20201227210846p:plain

しかしこの方法だと、Monster Name変数はMonsterBPからinstanceを作成する時に指定されません。

勿論エラーにはなりませんが、出来るだけMonsterBPのinstanceが生成される時に、Monster Name変数の値を指定しておきたいです。後でこの問題について検討します。

2.8  RPGGameInstanceBP内のMonsterSpawnData内のSpawnをfalseに変更する部分の整理

今週は時間が余ったので、2.4.3で述べた部分の実装部がかなり不必要に複雑になってしまった部分の整理を今やってしまいます。

複雑になっている原因はRPGGameModebaseBP内でEvent BeginPlayが使用出来ないため、一々GameInstanceにアクセスして、RPGGameInstanceBPにcastしてRPGGameInstanceBPの変数にアクセスしているからです。

RPGGameModebaseBP内でEvent BeginPlayが使用出来る様に直します。

f:id:kazuhironagai77:20201227210942p:plain

テストします。

f:id:kazuhironagai77:20201227211000p:plain

f:id:kazuhironagai77:20201227211008p:plain

Event BeginPlayが使用出来る様に成りました。

Event BeginPlayでGame InstanceをcastしてRPG Game Instance BPにします。それをMy Game Instanceに保持します。

f:id:kazuhironagai77:20201227211028p:plain

MonsterSpawnData内のSpawnをfalseに変更する部分はそれでも複雑なので関数にしました。

f:id:kazuhironagai77:20201227211046p:plain

その関数を呼び出して終りです。

f:id:kazuhironagai77:20201227211105p:plain

テストします。

f:id:kazuhironagai77:20201227211123p:plain

前と同じ様に戦闘出来ました。問題なしです。

3. まとめと感想

今週はここまでです。来週はもっと広範囲にモンスターを配置して、配置したモンスターと戦う事で見つかっていないバグを発見したいです。

 

「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の残りは後で考えます。先々週にチェックした戦闘システムのバグを直しました。細かい部分はまだ直していないのでそれは来週以降になります。