UE4の勉強記録

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

Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する  Effectの勉強など Part 4

f:id:kazuhironagai77:20210516232423p:plain

<前文>
<インド英語についてと>
皆さんはインド英語についてどういう印象をお持ちですか?
私は、独特な訛りがあって聞きづらいですが、英語そのものはネイティブと同じ位出来る。と言うイメージを持っています。訛りも幅があってアメリカ人とほとんど変わらないアクセントで話す人もいれば、全く聞き取れない人もいます。
私がアメリカにいた時は、インド人の英語は、工学部の大学院生以外のアメリカ人からは「あいつら何言っているか分からねー。」とはっきり言うとバカにされていました。
それが最近知ったのですが、最近インド人に対するアメリカ人の態度がガラッと変わっているらしいんです。
それは何故かと言うと、YouTubeのおかげだそうです。
アメリカの大学では、日本の大学と比較すると工学や理学は物凄い人気です。理学部は卒業後に医科大学など進学するために行くため、工学部は卒業後、即高収入が得られる仕事が得られるため世間からの評価も凄く高いです。
それで毎年大量の学生が理工系を専攻するのですが、大体の生徒は落第して専攻を変えるか別な大学へ転校するかの選択を迫られます。
そういうわけで理工系を専攻する学生は必死こいて勉強するわけですが、ほとんどの生徒は教科書読んだり、授業を聞いただけじゃ何言っているのか分からない訳です。それでYouTube先生の出番ですが、ほとんど全ての教科でYouTubeで教えているのがインド人なんです。
それもインド訛り全開の英語でです。
学生はそれこそ藁をも掴む思いでYouTubeを見る訳で、訛りなんて気にしているどころじゃない訳です。そして聞いてみると教授の教え方より遥かに分かり易い。やっと理解出来た。との高評価と感謝に繋がるそうです。
確かに、私がYouTubeで検索して出て来るtutorialできちんと作成されているモノの中にインド人が作成した物はかなり多いですし、その評価はかなり正確であると私も思います。
ここで、驚きなのが、インド人でもYouTubeでTutorialを作成する人達は、ずっと勉強して来たのに欧米の大学院に入学出来なかった、いわゆるインド人の中での負け組の人達なんです。それでもその人達は腐らずに、真面目に勉強を続けてその成果をYouTubeに発表していたんです。それが突然、花開いてとんでもない高評価をアメリカ人から得る結果になったようです。
この話を聞いて私が思ったのは、日本人でも物理や数学でYouTubeではないにしてもTutorialを作成していた人は結構いたんです。でもみんな日本語で作成していたんで日本人から評価されなかったから止めちゃったんです。
一般的に言っても日本人は英語の勉強には熱心ですが、英語で動画は作成しません。英語で動画を作成した方が、後からアメリカ人からの高評価を得られて得なのではないかと言う事です。
Computer scienceの分野で言えば、アメリカ人が日本人からどうしても習いたい分野が一つあります。
それはアニメ調の3d グラフィックの実装方法です。
これ、日本人のプロの方が英語でTutorialをYouTubeに挙げたら、とんでもない高評価を得られるはずです。ひょっとするとMITクラスの大学から教授としてお誘いがくるかも知れないレベルの評価を得るかもしれません。
それでは今週も勉強を始めます。
<本文>
1.今週の予定
以下の事をやる予定です。

  • NiagaraCascadeの勉強の続き(特にコストについて)
  • モンスターの配置についての考察
  • 魔法陣の改良
  • Landscape4で戦闘しまくってバグ出しをする。
  • Landscape4で戦闘後、沼にワープするバグの直し

これだけだと直ぐに終わってしまうかもしれません。もし直ぐに終わるようならば、

  • アイテムや宝箱を拾う機能を作成する。
  • モンスターの攻撃方法を増やす。
  • モンスターの種類を増やす。
  • モンスターが復活する機能を追加する。
  • Landscape4に昼夜を追加する。
  • 魔法のEffectを増やす。

などを行います。
2.Niagaraの勉強の続き
2.1 Events and Event Handlers Overview [2] を勉強します
いつものサイト、Niagara Visual Effects [1] のStarting Outで唯一勉強していないEvents and Event Handlers Overview [2] を勉強します。

 

f:id:kazuhironagai77:20210516232645p:plain

先週、一応Emitterが3つあるNiagara systemをちょっとだけいじったので、多分このDocumentを勉強して理解出来るレベルには達しているでしょう。

Events

f:id:kazuhironagai77:20210516232723p:plain

最初のこの一文の意味が分かりません。
EmitterのPropertiesを調べたらありました。

f:id:kazuhironagai77:20210516232841p:plain

こういうのは初心者向けのDocumentなんだから図付きで説明して欲しいです。
ここだけ簡単に文章で説明して残りは図と文章の両方で説明されると整合が取れてない分、読者は混乱します。
<<Location Events>>
Location Eventsを試すために新たなNiagara systemとEmitterを作成します。名前はNE_EventTest、NS_EventTestとしました。先週、議論した通りEmitterのPrefixもNS_を使用するとNiagara systemとの区別がつきにくくなるので、NE_としました。

f:id:kazuhironagai77:20210516232926p:plain

Required Persistent IDsにチェックを入れて

f:id:kazuhironagai77:20210516232950p:plain

Particle Update CategoryからGenerate Location Eventを選択します。

f:id:kazuhironagai77:20210516233013p:plain

なるほど。EventsはGenerate Collision Event、Generate Death Event、Generate Location Eventの3つしかないんですね。

f:id:kazuhironagai77:20210516233044p:plain

Generate Location Eventを追加しました。
Errorが出ています。

f:id:kazuhironagai77:20210516233108p:plain

良く分からないのでFixを選択します。

f:id:kazuhironagai77:20210516233831p:plain

Errorに説明があったようにSolve Forces and Velocity moduleが追加されました。
それは兎も角として、Generate Location Eventを追加するとこのEmitterから作成される全てのParticleはそれぞれの位置情報を生成するそうです。
その情報を別のEmitter内のEvent Handlerが受け取って何かするそうですが、
Generate Location Eventはこれ以上何もしなくても良いでしょうか?

f:id:kazuhironagai77:20210516233854p:plain

うーん。分かりませんね。
この謎を解くためには先にEvent Handlerを勉強すべきですね。
<Event Handlers>
以下に示した説明によれば、Event HandlerはEvent Handler PropertiesとReceive Eventの二つがあるそうです。

f:id:kazuhironagai77:20210516233949p:plain

f:id:kazuhironagai77:20210516233957p:plain

Event Handler PropertiesはどのEvent moduleのEventを受け取るのかを指定するみたいですね。
なるほど。
これで先程の謎も解けました。Event自体が、電話というより町のサイレン放送みたいなものなんですね。例えば「5時ですよ」と放送があったらそれを聞いた子供たち(Event Handler)が野球を止めて家に帰るとかを考えると納得出来ますね。
Event module側で、どのhandlerにどんな情報を送るのか決めるのではなく、Event側は持っている情報をみんなに送る訳です。その中でHandler側が必要な情報を自分で取捨選択して受け取る訳ですね。
実際に作成してみます。
先程、作成したNS_EventTest内に、同様に先程作成したNE_EventTestを追加しました。

f:id:kazuhironagai77:20210516234028p:plain

Event Handler用に新しくEmitterを作成しようとしたらEmptyというEmitterがNS_EventTest内から選択出来る事を見つけました。これを使用します。
EmptyのEvent Handler categoryにEvent Handler Moduleを追加します。

f:id:kazuhironagai77:20210516234052p:plain

設定も同様にしました。

f:id:kazuhironagai77:20210516234116p:plain

そしてEvent Handlersに必要なもう一つのModuleであるReceive Eventを追加します。

f:id:kazuhironagai77:20210516234140p:plain

追加しましたが、このModuleの目的が分かりません。公式のDocumentの説明も良く分かりません。
これでお終いでした。
2.2 UE4 4.25 Niagara Event Handler Simplest Example [3] を勉強します
Eventについて簡単に説明したtutorialを探したらそのものズバリのタイトルがありました。UE4 4.25 Niagara Event Handler Simplest Example [3] です。
軽く最初だけ見たら、インド英語ですね。今週は前文でインド英語について述べたので何か奇縁を感じます。もう少し見てみます。
はい。これで勉強する事にしました。非常に分かり易くEventの使用方法について説明しています。
まずEmitterを作成しています。このTutorialではFountainを選択していました。

f:id:kazuhironagai77:20210516234247p:plain

Fountainの発音ですが、フォウンテインが正しい発音ですが、フォウンッインって発音する人も結構いた気がします。英語には小さいツの音はないらしいんですが、私はFountainをtなしで発音する時、小さいツが入ってると思うんです。フォウンインじゃなくてフォウンッインってフォウンとインの間にためがある気がします。
今回、気になって調べたんですが、Fountainをフォウンッインって発音しているサイトがまず見つからなかったです。やっぱり英語には小さいツの音はないんでしょうか?

f:id:kazuhironagai77:20210516234332p:plain

EmitterなのでNE_をPrefixに付けました。
今度は、Eventを受け取る側のEmitterを作成します。

f:id:kazuhironagai77:20210516234411p:plain

f:id:kazuhironagai77:20210516234420p:plain

最後にこれらのEmitterを使用するNiagara systemも作成します。

f:id:kazuhironagai77:20210516234453p:plain

作成したNiagara systemを開いて、2つのEmitterを追加します。

f:id:kazuhironagai77:20210516234512p:plain

まず最初に、最初のemitterのParticleが死んだら、次のEmitterが発動するEventを作成するそうです。
このTutorialではSelectionから追加しています。
Generate Death Eventを追加しました。

f:id:kazuhironagai77:20210516234538p:plain

何かErrorが出ています。Logを見ろと書いてあるので見ると

f:id:kazuhironagai77:20210516234555p:plain

Requires Persistent ID’sのチェックを忘れていました。

f:id:kazuhironagai77:20210516234617p:plain

TutorialでRequired Persistent IDsの意味を簡単に説明していました。
Niagara systemで作成される全てのParticleはIDを持っているそうです。しかしそのIDはParticleがUpdateするたびにどんどん変わってしますそうです。所がこのRequired Persistent IDsにチェックを入れるとそのIDはずっと変わらずに同じになるそうです。
更に、GPUで計算した場合もEventは使用出来ない事も説明していました。そうでした。その事はすっかり忘れていました。
TutorialではGenerate Death EventのGap Correction Amountの値が1.5になっているんですが、私のは0になっています。

f:id:kazuhironagai77:20210516234637p:plain

一応、1.5に変更しときました。
更に、Generate Death Eventを受け取るためにNE_OmnidirctionalBurstのEvent Handler CategoryにEvent Handler Propertiesを追加します。
Source、Execution ModeそしてSpawn Numberを以下の様に変更しました。

f:id:kazuhironagai77:20210516234718p:plain

Spawn NumberはEventで前のEmitterのParticleが消滅したと連絡が入った時に、このEmitterのParticleを何個発生されるのかを指定するためのものだそうです。つまりこの設定だと5個のParticleが発生するわけです。
見にくいのでOminの方のParticleの色を赤に変えます。
Particle SpawnのInitialize ParticleのColor Modeで変更してました。

f:id:kazuhironagai77:20210516234738p:plain

このTutorialにおける、この辺の細かい説明はかなり親切に感じます。
私の結果は、全然Eventを受け取っていません。最初からOmni Directional Burstが前回で発動しています。

f:id:kazuhironagai77:20210516234759p:plain

何ででしょう?
Tutorialの続きを見たら分かりました。Receive Death Eventを追加してなかったからです。

f:id:kazuhironagai77:20210516234836p:plain

出ました。

f:id:kazuhironagai77:20210516234901p:plain

やっぱり私の作成したヤツちょっとオカシイです。一回しかOmniのParticle を作成しません。

f:id:kazuhironagai77:20210516234928p:plain

はい。これもTutorialに説明されていました。
Emitter State CategoryのLife Cycle ModeをSystemにセットすれば良いそうです。

f:id:kazuhironagai77:20210516234946p:plain

凄い。直りました。

f:id:kazuhironagai77:20210516235004p:plain

このエフェクト、血飛沫の作成にも応用出来そうですね。
もう少しEventの効果をはっきりさせるためにFountainのparticleの数を減らします。

f:id:kazuhironagai77:20210516235022p:plain

確かにFountainのparticleが消滅した時に赤いOmniのparticleが生成しています。

f:id:kazuhironagai77:20210516235128p:plain

ただ、私のヤツは最初に赤い爆発が起きているです。

f:id:kazuhironagai77:20210516235154p:plain

これはTutorialにはないです。
多分何かやり忘れているんでしょう。
はい。Tutorialに完璧に説明されていました。
以下に示した白いひし形の印があると最初の爆発が起きるそうです。

f:id:kazuhironagai77:20210516235212p:plain

このひし形を消したら爆発は無くなるそうです。
消し方はこのひし形を選択してDeleteキーを押せばいいそうです。

f:id:kazuhironagai77:20210516235231p:plain

消えましたね。
最初に現れていた赤い爆発もなくなりました。

f:id:kazuhironagai77:20210516235248p:plain

出来ました。

f:id:kazuhironagai77:20210516235314p:plain

次はCollusion Eventで作成するそうです。
やり方は前と同じですので、これは結果だけ示します。

f:id:kazuhironagai77:20210516235334p:plain

FountainのParticleが地面と衝突した時に赤い爆発が起きています。
最後にLocation Eventです。

f:id:kazuhironagai77:20210516235353p:plain

FountainのParticleの位置がupdateするたびに赤い爆発が起きる様に成りました。
一応これでEventの使用方法について理解出来ました。
3.Cascadeの勉強の続き(特にコストについて)
今週は、公式のdocumentにあるVFX Optimization Guide [4]を勉強します。
最初から以下のように書かれていました。

f:id:kazuhironagai77:20210516235949p:plain

Matineeってなんでしたっけ。
公式のdocumentである、Matinee and Cinematics [5]では以下の説明がされていました。

f:id:kazuhironagai77:20210517000151p:plain

ああ。アニメーションを作成するやつか。
何となくは知っていますが、この分野も本格的な勉強した事はなかったです。後で勉強する事にします。
<Common Offenders For Poor Effects Performance>
一般的にいって、以下のヤツがコストが掛かりますと説明しています。

f:id:kazuhironagai77:20210517000226p:plain

うーん。何が言いたのか分からないよ。
Tick TimeはTick一回にかかる時間の事でしょう。全てのParticle SystemがUpdateするのに必要とされる時間。って書かれているって事はCascadeに限っての話をしているのか。
あ、分かった。
ここに載っているのは、Cascadeを実行する時に、行われるそれぞれの処理の中で、油断すると凄い仕事量になってPCがヒィヒィ言ってしまう箇所を述べているんです。
多分、Cascadeを実際に実行すると、10とか20の工程がある訳です。その工程の中でいつも凄いコストがかかったり、実行するのが特別遅かったりする工程を担当している箇所を4つ説明しているんです。
一般的に言って3D Graphicsで最も処理が遅くなる箇所って、CPUからGPUにデータを送る時でしょう。
この4つの中だと、Draw Callsがその処理を担当しているんでしょうか?そんな風に読めなくもないですね。
上記の仮定に基づいて上の4つを私が超訳すると、

  • Overdraw:一枚の絵を描くのにかかる時間を指します。カメラがParticleに近い程、絵を大きく描かなければならないので時間がかかります。更に一枚の絵が沢山のLayerで作成されている場合はそのLayerの数の分だけ、更に絵を描く時間がかかります。絵の大きさ(Pixelの数)、描く絵の枚数(Layerの数)なので、難しく言うと、Overdraw =(Layerの数)*(Pixelの数)となります。
  • Tick Time:全部のParticleを一回だけUpdateするのにかかる時間を指すそうです。

今、唐突に思ったんですが、Tick timeってFrame毎に決まっているんでしたっけ。そのFrame数内で終わらない量の処理をTick関数内で実行した時はどうなるんでしたっけ。次のTickは前のTickが実行し終わるまで待機するのか?それとも勝手に実行し始めるんでしょうか?

Tick()関数がThreadならば、多分Particleの位置を変えたりする場合にThread Safeが働くはずです。そうなると後のTick()関数のThreadは前のTick() 関数のThreadが実行を終わるまで待っている事になるんでしょうね。
うん。そうなるとTick Timeの時間が増えると、線形的に全体の計算時間も増えてしまうわけですね。
でも、これってO(n)ですよね。別にそんなに悪くないんじゃん。
ああ、段々分かって来ました。コストを削減するには最もコストがかかってる箇所を理解するだけでは足りなかったんっだ。コストがかかったとしても必要な部分はやらないといけない訳です。その中でこの部分を減らすと指数的(O(nc))にコストが減らせる箇所とそうでない箇所があって、前者は何としても減らせるならば減らせるべきですが、後者は出来れば減らしたい位の箇所になるわけです。
でも何でこのDocument、コストについての考察なのにBig O notation についての考察が全く述べられていないの?
ちょっとパラッと全部読んでみます。
うーん。ここにまとめる価値はないかもです。
結局、私が知りたい、Particle systemを勉強している人全般が知りたい事は、以下の事でしょう。
Emitterを増やすとコストは指数的に増えるのか?

f:id:kazuhironagai77:20210517000407p:plain

その場合でも、一万個のEmitterを追加した場合は影響が凄いでるが、5個や6個のEmitterなら関係ないのか?
Emitter内で使用しているMaterialが使用してるTextureのサイズはコストに影響するのか?

f:id:kazuhironagai77:20210517000429p:plain

生成するParticleの数はコストにどのように影響するのか?

f:id:kazuhironagai77:20210517000448p:plain

これら以外もあるかもしれませんが、私自身があまりParticle Systemについて知らないので取りあえず思い付くのは以上になります。
それらがParticle systemのどの辺の処理に関連しているのか、つまり先程の説明であった、Overdraw、Tick Timeなどの処理に使用されているならば、少なくした方がコストがかからないのかと定性的に理解出来ますし、その後でこの部分はO(n^C)なので絶対コスト少なくすべきとの定量的な考察に入るべきでしょう。
はい。時間が勿体ないのでこのDocumentの勉強は中止します。
4.コストと最適化について
本当は、別なcascadeについての勉強をしようと思ったのですが、先程のcostと最適化についてもっとしっかりしたtutorialはないのかと探したらMaterialの作成についての最適化で直接はparticle systemには関係ないですが、一個見つかりました。
ので、今週はそれを勉強する事にします。
4.1 Shader Performance Optimization - UE4 Materials 101 - Episode 7 [6] を勉強します
Shader Performance Optimization - UE4 Materials 101 - Episode 7 [6] を勉強します。
最初に、Materialのコストを測定する3つの方法を紹介しています。
3つの方法を紹介するというかこの3つ以外にMaterialのコストを測定する方法はないって感じで説明しています。
最初の方法は、Shader Complexityを使用する方法です。

f:id:kazuhironagai77:20210517000551p:plain

以下のようなシーンが見られます。

f:id:kazuhironagai77:20210517000624p:plain

これは大変有名で私も知っています。
しかしこの色は、Instraction countに基づいて表示されていて、Instraction count自体が完璧な評価方法でないので正確ではないそうです。
おお。
これは勉強しがいがあります。
上記のShader ComplexityはShaderのInstractionの数を何となく比較するには都合が良いですが、実際の数字を見る方法もあるそうです。
それが2つ目の方法です。
まず、あるMaterialを開きます。
試しに以下のMaterialを開いて見ます。

f:id:kazuhironagai77:20210517000655p:plain

開くと以下に示した様なStas欄があります。

f:id:kazuhironagai77:20210517000713p:plain

私が参考にしたMaterialにはDefaultではありませんでした。
追加します。

f:id:kazuhironagai77:20210517000733p:plain

以下の表示が現れました。

f:id:kazuhironagai77:20210517000750p:plain

最初のInstructionの意味ですが、Materialに書かれたBPは以下のように変換されて実行されるそうです。

f:id:kazuhironagai77:20210517000809p:plain

このAssembly Instructionに変換された時のInstructionの数を表しているそうです。
ただしこの数と一つのInstructionの実行にかかる時間は同じではなく、あるInstructionは他のInstructionの何倍の時間がかかったりするため単純にこのInstructionの数が減ったから最適化したとは言えないそうです。
Assembly レベルでの話ならば一個の指令を実行する時間はどの指令でもそんなに差はないような気もしますが、どうなんでしょう?
三番目の方法ですが、実際の環境で試す事だそうです。
これは設定するのに大変時間がかかりますが最も正確だそうです。
そりゅあそうです。
そしてこの方法は最も信頼に値しますし。当たり前と言えばそうですが、これ以上正しい方法はないですね。
実際、PCやPS、Xboxで試すとHardwareによってコストが全然違うそうです。
うーん。深い。
Materialのコストは三番目の方法で最終的には最適化しますが、最初の段階では2番目の方法で大体最適化出来ているだろうとの目安にするそうです。
はい。
これが重要になってくるわけですね。

f:id:kazuhironagai77:20210517000832p:plain

この点を理解した上で、4つの最適化のための方法を教えてくれるそうです。
一つ目です。

f:id:kazuhironagai77:20210517000908p:plain

流石にこれは当たり前だろうと思いましたが、良く考えると私のProjectのGameModeやThirdPersonCharacterには結構使用していないコードが残っています。謙虚に受け止めます。
具体例も結構説明していました。
実際に使用しない計算をoffの状態で残しているだけでも影響はあるそうです。
以下にMacro Texture Variationを示しますが、

f:id:kazuhironagai77:20210517000936p:plain

これもMaterialを使用する物体が十分小さい場合は、全部のパターンは要らないですよね。こういう部分を削れと言う事ですね。

2番目の方法です。

f:id:kazuhironagai77:20210517000955p:plain

計算結果がほぼ同じで計算の負担が減る方法があるならばそちらを採用すべきだそうです。
このTutorialではPowerノードの代わりに、3つの単純な足し算、掛け算のノードを使用する事で、結果がほぼ同じでShader Instructionを5つ位減らす方法が紹介されていました。
以下のPowerのノードを

f:id:kazuhironagai77:20210517001014p:plain

以下の様なノードに変換する事で

f:id:kazuhironagai77:20210517001034p:plain

Base Pass ShaderのInstructionの数が115から

f:id:kazuhironagai77:20210517001050p:plain

111に下がっています。

f:id:kazuhironagai77:20210517001106p:plain

3番目の方法です。

f:id:kazuhironagai77:20210517001126p:plain

以下にTutorialに載っていた例を示します。

f:id:kazuhironagai77:20210517001146p:plain

以下の様に改良しました。

f:id:kazuhironagai77:20210517001206p:plain

ただし、この例のBase Pass ShaderのInstructionの数は両方96で変化していませんでした。
あくまでもこんなやり方もあると言う位に聞いておきます。
4番目の方法です。

f:id:kazuhironagai77:20210517001239p:plain

これが知りたかったです。
説明は簡単でした。
Ambient Occlusion、Metallic、Specular、そしてRoughnessは一個のTexture Sampleにまとめてしまえ。と言う事でした。

f:id:kazuhironagai77:20210517001304p:plain

そう言えば、2021-04-11 のブログでGameTexture.comからdownloadしたTextureにMrao.pngというMetalness-Roughness-Ambient OcclusionをまとめたTextureがありましたね。こっちはそれに更にSpecularまで一緒にしたTextureだそうです。
この例のBase Pass ShaderのInstructionの数は102 から100に下がりました。
ただし、このTextureからデータを読み込むのは例え一回にしか数えられなくても最もコストがかかるInstructionなので数字に見えないコスト削減が出来ているそうです。
以上でした。
5.モンスターの配置についての考察
先週、Landscape4のBlock 1にモンスターを2体配置しました。

f:id:kazuhironagai77:20210517001406p:plain

はっきり言うと何も考えないで作成したので色々な問題を内包していそうです。
それについての対策を考えておきます。
5.1 Trigger boxのサイズ
Trigger boxのサイズをBlock 1 の範囲であるNav Mesh Bound Volumeよりも大きくしています。

f:id:kazuhironagai77:20210517001454p:plain

これはplayerが操作するキャラが縄張り内に侵入した途端に、モンスターが発生したらPlayerが対応するのは大変だからそうしたのですが、その結果、その他のblockのTrigger boxと重複する箇所が発生します。
5.2 Monster Territory Numberについて
Monster Territory Number はRPG Game Instance内に作成されたInt型の変数で、Landscape4のどのBlockから戦闘画面に飛んだのかを記録するための変数です。

f:id:kazuhironagai77:20210517001535p:plain

先週、Landscape4に戦闘から戻った時に、どのBlockに属するMonsterを生成すべきなのか指定する必要があったので急遽作成しました。
以下のように使用されています。

f:id:kazuhironagai77:20210517001600p:plain

RPG Game Instance BP内だけで以下の変数が追加されています。早急に整理しないと何が何をしているのか分からなくなります。

f:id:kazuhironagai77:20210517001619p:plain

5.3 Remove Defeated Monster from Landscape 4 () 関数について
Landscape 4 上で配置されているモンスターが戦闘で倒された後、二度と再生されないようにGame Instance内のdata tableから消去する関数です。
この関数、先週は何も考えないで、Block1ならGame Instance内のdata table、Monster Spawn Data Landsape4-1を検索、それ以外ならdata table 、Monster Spawn Data Landsape4から検索と設定しています。

f:id:kazuhironagai77:20210517001650p:plain

もう少しうまく実装出来ないか検討します。
5.4 戦闘画面に移動する場合もOn Actor End Overlap (Trigger box) が呼ばれている事について
先週はこの事が分からなくて、戦闘から戻って来たら倒したモンスターが復活して来て困惑しました。
On Actor End Overlap (Trigger box)が呼ばれると当然ですが、RPG Game Instanceにある変数、Monster Territory Numberは0 にセットされます。
戦闘後、Remove Defeated Monster from Landscape 4 () 関数は、Monster Territory Numberの値に基づいて、どのデータテーブルからモンスターを消去するかを決定するので、Monster Territory Numberが0 にセットされると正しいData tableにaccessする事が出来ません。
先週は、以下に示した様に戦闘画面に移動するために、On Actor End Overlap (Trigger box)が呼ばれた場合は、RPG Game Instanceにある変数、Monster Name in FightingがNone以外である事を利用して、この問題を解決しました。

f:id:kazuhironagai77:20210517001725p:plain

以下の問題点が今の時点でも考えられます。

  • 別なレベルに移動する時、必ずOn Actor End Overlap (Trigger box)は呼ばれるのか?呼ばれなかった場合はどうなるのか?
  • 戦闘画面からBlock1内に戻って来た時に、On Actor Begin Overlap (Trigger box)は呼ばれるのか?呼ばれなかった場合、それぞれのParameterはどうなっているのか?
  • Block1内から移動する時、必ずMonster Name in Fighting 変数の値はNoneになっているのか?
  • Block1内から直接、Block2に移動した時はどうなるのか?

これ以外の問題も発生するかもしれませんが、今の所思い付くのはこれだけでした。
5.5 Territory 1に一端侵入した後に、Territory 1から逃走するとMonsterが消滅しなかった事について
先週のBlogを読み直していたら、Block 1をTerritory 1と読んでいました。
用語の使用を統一しないと、後で混乱を招くのでここできちんと定義する事にします。
Trigger boxの範囲をBlock 1とします。

f:id:kazuhironagai77:20210517001920p:plain

そして、実際にmonsterが徘徊する領域をTerritory 1と呼ぶ事にします。

f:id:kazuhironagai77:20210517001953p:plain

新しい定義に従ってもう一度、問題を言語化すると、「Block 1に一端侵入した後に、Block 1から逃走するとMonsterが消滅しなかった事について」となります。
先週は、このバグの原因が、

  • Trapでモンスターを発生させた時に、モンスターが消滅する前に新しいモンスターを発生した時に起きたバグと同じである。
  • 今回は更に、Monsterの発生や消滅でLoopを使用してるため更に問題が複雑になっている。

の2点にあると考え以下の解決方法を実装しました。
Trapでバグが発生したのは、Monsterのanimationが終了する前に、そのanimationが終了した後に実行してほしいコードが実行される事で起きていました。基本的にPlay Animationノードを使用すると、アニメーションの終了を待たずに次のコードが実行されます。
Trapの時は、Animationが終了するまで、次のコードを実行しない事で解決しました。
今回の問題に関しては、先週は、以下のようにして解決しました。
Landscape4のLevel BPにMonster BPのArray、Monster Territory 1を作成して生成したそれぞれのモンスターを管理します。

f:id:kazuhironagai77:20210517002039p:plain

これで生成したmonsterそれぞれにこのArrayからいつでもアクセス出来ます。
分解、消滅のアニメーションをPlayする場合は、以下の方法で実行出来ます。

f:id:kazuhironagai77:20210517002058p:plain

このAnimationが終了する前まで次のコードを実行させないために、

f:id:kazuhironagai77:20210517002115p:plain

必ず2秒ほど、待つ事にしています。
分解、消滅のAnimationの時間は1.4秒なので2秒間までは全てのMonsterの分解消滅のアニメーションが終了しているはずという考えです。もし何らかの原因でAnimationが2秒以内に終わらなかった場合は、この部分のコードは正常に作動しなくなります。
その後、以下のコードを実行しています。

f:id:kazuhironagai77:20210517002136p:plain

まずDestroy ActorでそれぞれのMonster BPを破壊します。その後で、Monster Territory 1をclearします。
ここで新しいBoolean変数、Monster Cleanを使用する事で、生成したMonsterが全員消滅した事を確認出来る様にしています。
更に、Block 1から出てモンスターが消滅する前にもう一度Block 1に侵入した場合についてです。
先程説明した、Boolean変数、Monster CleanがTrueになるまで、新しいMonsterは生成しません。

f:id:kazuhironagai77:20210517002155p:plain

このやり方で、今の所バグは発生していませんが、論理的な破たんがないか検討すべきです。
5.6 Landscape4、Territory 1における戦闘
Territory 1 で戦闘した後にLandscape 4の沼に戻ってしますバグがありました。

f:id:kazuhironagai77:20210517002250p:plain

これは後で検討します。
更に、戦闘から戻った時に発生したErrorが、On Actor End Overlap (Trigger Box) のDestroy Actorノードの前にIs Validをつける事で解決した理由もはっきり分かりません。

f:id:kazuhironagai77:20210517002310p:plain

これについても検討します。
6. モンスターの配置についての考察-その2
6.1 Trigger boxのサイズについての検討
以下に示した様にBlock 1から出ずにBlock 2に侵入する場合が考えられます。
その場合、Block1からは出ていませんが、Territory 1からは出ている場合があります。
これを条件1とします。

f:id:kazuhironagai77:20210517002347p:plain

Territory 1からも出ていない場合もあります。
これは条件2とします。

f:id:kazuhironagai77:20210517002408p:plain

更に、Block1からは出ない内にBlock2に入り、かつTerritory2にも入っている場合も考えられます。
これは条件3とします。

f:id:kazuhironagai77:20210517002459p:plain

条件1の場合から考えます。
Monster Territory Numberは1から0 に成る前に2 になります。

f:id:kazuhironagai77:20210517002519p:plain

うん。Monster Territory NumberなのにBlockに入った時に値が変化するのか?
うーん。
やっぱりこの方法で管理するためにはTerritoryとBlockのサイズを同じにする必要があるみたいですね。
TerritoryとBlockのサイズを同じにすれば上記の問題を何も考える必要がなくなります。しかし5.1で論じたようにUserには不親切になります。
考えるのが面倒くさいです。
TerritoryとBlockのサイズを同じにして別なBlockは絶対に重ならないようにします。
以下に示した様に、Block 1とTerritory 1のサイズを全く同じにしました。

f:id:kazuhironagai77:20210517002557p:plain

これで解決です。
6.2 Monster Territory Numberについて
6.1でBlock 1とTerritory 1のサイズを同じにしたので、この変数自体が間違った値を示す可能性は無くなりました。
RPG Game Instance BP内に変数が多すぎて整理が出来ない問題については、以下に示した様にLandscape4で使用する変数はCategoryとしてLandscape4を作成する事で区別しました。

f:id:kazuhironagai77:20210517002630p:plain

6.3 Remove Defeated Monster from Landscape 4 () 関数について
この関数をもっと簡単な形に変更出来ないか検討します。
検討した結果、関数を更に作成する事で見た目を簡便にする事が出来る事が判明しました。
しかし、それをやるとその関数が正しく働いているのかの検討をしなければならず、ちょっと大変になるので、以下に示した様に簡単な整理だけやりました。

f:id:kazuhironagai77:20210517002701p:plain

6.4 戦闘画面に移動する場合もOn Actor End Overlap (Trigger box) が呼ばれている事について
5.4で見つけた問題点、以下の事について考察、検討します。

  • 別なレベルに移動する時、必ずOn Actor End Overlap (Trigger box)は呼ばれるのか?呼ばれなかった場合はどうなるのか?
  • 戦闘画面からBlock1内に戻って来た時に、On Actor Begin Overlap (Trigger box)は呼ばれるのか?呼ばれなかった場合、それぞれのParameterはどうなっているのか?
  • Block1内から移動する時、必ずMonster Name in Fighting 変数の値はNoneになっているのか?
  • Block1内から直接、Block2に移動した時はどうなるのか?

<別なレベルに移動する時、必ずOn Actor End Overlap (Trigger box)は呼ばれるのか?呼ばれなかった場合はどうなるのか?>
これが問題になるのは戦闘画面に移動した時だけです。もしOn Actor End Overlap (Trigger box) が呼ばれなかった場合は、Monster Territory Number変数は1のままです。何の問題も生じません。
<戦闘画面からBlock1内に戻って来た時に、On Actor Begin Overlap (Trigger box)は呼ばれるのか?呼ばれなかった場合、それぞれのParameterはどうなっているのか?>
テストしてみます。
On Actor Begin Overlap (Trigger box)は呼ばれませんでした。
しかしまた沼に戻って来ました。

f:id:kazuhironagai77:20210517002829p:plain

2回目の戦闘で沼に移動するバグになりました。
パラメーターに関して言えば、問題になるParameterはMonster Territory Number変数だけでした。On Actor Begin Overlap (Trigger box)は呼ばれない場合でも、この変数は1を保持していて、それが正しい値ですので問題はないです。
<Block1内から移動する時、必ずMonster Name in Fighting 変数の値はNoneになっているのか?>
戦闘が終了してから何時、Monster Name in Fighting 変数の値をNoneにセットしているのか調べました。
そしたらなんと、先程整理していたRemove Defeated Monster…関数の最後で呼ばれていました。
以下に示した様に、この関数の後で、Open Level ノードが呼ばれています。

f:id:kazuhironagai77:20210517002911p:plain

Block1内から移動する時は、Monster Name in Fighting 変数の値は必ずNoneにセットされていると考えて良いと思います。
<Block1内から直接、Block2に移動した時はどうなるのか?>
この条件は起きない設定に変更しましたので検討する必要はなくなりました。
6.5 Territory 1に一端侵入した後に、Territory 1から逃走するとMonsterが消滅しなかった事について
このバグ自体は、先週、既に解決しています。
先週行った解決方法を5.5で示しましたが、このやり方で問題無いのかの検討も5.5でやっているのでここでは特に何も検討しません。
6.6 Landscape4、Territory 1における戦闘
5.6でIs ValidノードをDestroy Actorの前にセットする必要性が分からない点について考察します。

f:id:kazuhironagai77:20210517003011p:plain

先ず、このErrorが何時呼ばれたのかが不明なんです。戦闘から帰って来て気が付きましたが、戦闘画面から帰って来た時にOn Actor End Overlap (Trigger box)が呼ばれる訳がないです。
と言う事は、戦闘が始まる時に呼ばれて、それと同時にLevelが切り替わる為、Landscape4に存在していたActorも消去されるはずです。多分その後で、Destroy Actor()関数が呼ばれる時もあるんじゃないんでしょうか?
その時にErrorになってしまうと思われます。
それが原因ならIs Valid () 関数を今の様にかましておけばErrorは起きません。更に理論的な解釈の間違いもないと成りますので、他に直さなければならない部分も無いはずです。
7. Landscape4で戦闘後、沼にワープするバグの直し
2時間位、原因が分からなくて悩みましたが、結論から言うと直せました。
Landscape4内で戦闘になった時、元の位置をRPG Game Instance BPのThird Person Character Location変数内に保持します。

f:id:kazuhironagai77:20210517003044p:plain

この時に、戦闘開始になった位置を保持するのではなく、X軸に500ずれた位置を保持していました。
このせいで、モンスターに捕まった位置からX軸に500ずれた箇所が地面より下だった場合、戦闘から帰ってきたキャラは地面の下で再生され、落下して消滅してしまいます。これが沼の再生とerrorの原因だったのです。
勿論、モンスターに捕まった位置からX軸に500ずれた箇所が地面より上の時は、Errorは起こりません。つまり一見再現性が無いようにも見えます。
兎に角、直せました。
8.モンスターの出現方法についての再考
8.1 Triggerを追加する。
「7.Landscape4で戦闘後、沼にワープするバグの直し」で何十回もテストしましたが、やはりモンスターはterritoryに侵入する前に出現して欲しいです。
そこで思い付いたのが、Trigger Boxをもう一個足す方法です。以下に図を示します。

f:id:kazuhironagai77:20210517003134p:plain

Trigger1内に侵入した時にTerritory 1のモンスターが発生します。しかし前の様に、Monster Territory Numberは1になりません。0のままです。Monster Territory NumberはTrigger 2に侵入した時に初めて1に変化します。
8.2 Triggerを追加した場合の理論的考察
「6.1 Trigger boxのサイズについての検討」で説明した条件に基づいて具体的に解説します。
条件1の場合です。
Playerの操作するキャラはBlock1内にいながらBlock2に侵入しました。

f:id:kazuhironagai77:20210517003217p:plain

Territory 1から既にでているのでTrigger2 の働きにより、Monster Territory Numberは0 にセットされています。しかしBlock1からは出てないのでTerritory1のモンスターは消滅しません。更にBlock2に侵入したため、Territory2のモンスターも出現しました。
ここでモンスターと戦闘になる事は無いので、Monster Territory Numberは0で正しいです。全部正しく作動しています。
条件2の場合です。
Playerの操作するキャラはTerritory1内にいながらBlock2に侵入しました。

f:id:kazuhironagai77:20210517003239p:plain

Territory1にいるので、Monster Territory Numberは1 にセットされています。しかしBlock2にも入っているので、Territory2のモンスターも出現します。出現しますが彼らはTerriory2から出る事はないので、Playerの操作するキャラと戦闘になる事はありません。Playerの操作するキャラと戦闘になる可能性のあるモンスターはTerritory1内にいるモンスターです。つまりMonster Territory Numberは1が正しいです。
この条件でも正しく作動します。
条件3の場合です。
Playerの操作するキャラはBlock1内にいながらTerritory2に侵入しました。

f:id:kazuhironagai77:20210517003300p:plain

Territory2にいるので、Monster Territory Numberは2 にセットされています。しかしBlock1にも入っているので、Territory1のモンスターも出現します。出現しますが彼らはTerriory1から出る事はないので、Playerの操作するキャラと戦闘になる事はありません。Playerの操作するキャラと戦闘になる可能性のあるモンスターはTerritory2内にいるモンスターです。つまりMonster Territory Numberは2が正しいです。
この条件でも正しく作動します。
はい。
この方法でやれば、戦闘後の混乱を起こさないでMonsterを遠くから発生させる事が出来ます。
8.3 実際の作成
よく考えたらまだ、Block1しか作成しておらず、上記の条件でBlock1だけで上手く作動出来るかの検討が先に必要でした。
つまり、

  • Block1に侵入して直ぐにBlock1から退去した場合、モンスターは消滅するのか?
  • Block1に侵入して直ぐにBlock1から退去して、更にもう一度、Block1に侵入した場合、Monsterは消滅してから生成されるのか?
  • 戦闘で倒したモンスターは二度と再生されないようになっているのか?

の検討を忘れていました。(先週のBlogの4でやった部分です。)

これが出来てからBlock同士が重なった場合の検討が必要になります。Blockを増やすのは後でやります。今週はBlock1だけで上手く作動出来るように実装します。
Block1に侵入した場合です。

f:id:kazuhironagai77:20210517003421p:plain

Monsterが生成されます。
Block1に侵入して直ぐにBlock1から退去して、更にもう一度、Block1に侵入した場合、Monster Clean変数がTrueになるまで待ちます。のでMonsterは消滅してから生成されるはずです。
Block1から退去した場合です。

f:id:kazuhironagai77:20210517003444p:plain

実装している内容は先週のBlogの4でやった事と同じですのでコードの説明は省きます。
今度はTerritory1に侵入、退出した場合です。

f:id:kazuhironagai77:20210517003540p:plain

新しいTriggerを追加しようとしたら、Nav Mesh Bounds Volume もOverlapの機能を持っていたのでNavをそのまま使用しています。
ここではMonster Territory Numberを変更しています。
8.4 テスト
テストしてみます。
Block1に侵入します。

f:id:kazuhironagai77:20210517003607p:plain

f:id:kazuhironagai77:20210517003615p:plain

Monsterが生成されました。
Block1から退出します。

f:id:kazuhironagai77:20210517003633p:plain

f:id:kazuhironagai77:20210517003641p:plain

Monsterは分解消滅します。
Block1に侵入して直ぐにBlock1から退去して、更にもう一度、Block1に侵入します。

f:id:kazuhironagai77:20210517003707p:plain

f:id:kazuhironagai77:20210517003720p:plain

Monsterが消滅してから新しいモンスターが生成されました。
ここまでは出来ています。
今度は戦闘を試してみます。
Monsterを一体倒して戦闘から戻ってきたら、別のMonsterもいません。

f:id:kazuhironagai77:20210517003744p:plain

何故でしょう?
一回、Block1から退出して侵入し直したら、Monsterが2体出て来ました。

f:id:kazuhironagai77:20210517003802p:plain

出来ていませんね。
直します。
調べたら以下のNavはOverlapしてもEventを発信していなかったです。

f:id:kazuhironagai77:20210517003821p:plain

やっぱりTrigger boxが必要でした。
Trigger box_ Territory1を作成します。

f:id:kazuhironagai77:20210517003839p:plain

テストします。

f:id:kazuhironagai77:20210517003858p:plain

戦闘します。
一体倒して戻ってきたら、直ぐに残りの一体と戦闘になりました。
それを倒して戻ってきたら今度は、モンスターはいなくなりました。
と言う事は出来ています。
これで完成とします。
9.Landscape4で戦闘しまくってバグ出しをする。
今週はもう疲れたのでやりません。
来週、Block1のモンスターの数を増やして試します。来週は更にBlockの数も増やします。
10.魔法陣の改良
これも来週やります。
11.まとめと感想
今週は以下の事をやりました。

  • Niagara:Eventの使用方法の勉強
  • Cascade:Costについて勉強しようとしたら良いTutorialが見つからなかったので代わりにMaterialのコストについて勉強
  • Block1のモンスターについての考察
  • Landscape4における戦闘後に沼に戻ってしますバグの直し
  • Block1のモンスターの出現の実装

今週は勉強している途中でぎっくり背中になってしまって途中、集中力が落ちてしまいました。
来週は以下の事をやる予定です。

  • NiagaraCascadeの勉強
  • Block1にもっとモンスターを出現させる
  • Block2,3を作成する時に簡単に出来る様にBlock1をActor化する。
  • Block2,3を作成する。
  • 魔法陣の改良

などを考えています。
12.参考文献(Reference)

[1] Epic Games. (n.d.-c). Niagara Visual Effects. Unreal Engine Documentation. Retrieved May 16, 2021, from https://docs.unrealengine.com/en-US/RenderingAndGraphics/Niagara/index.html

[2] Epic Games. (n.d.-a). Events and Event Handlers Overview. Unreal Engine Documentation. Retrieved May 16, 2021, from https://docs.unrealengine.com/en-US/RenderingAndGraphics/Niagara/EventHandlerOverview/index.html

[3] Hrishikesh Andurlekar. (2020, May 18). UE4 4.25 Niagara Event Handler Simplest Example [Video]. YouTube. https://www.youtube.com/watch?v=wJ5o8mxeyXY

[4] Epic Games. (n.d.-d). VFX Optimization Guide. Unreal Engine Documentation. Retrieved May 16, 2021, from https://docs.unrealengine.com/en-US/RenderingAndGraphics/ParticleSystems/Optimization/index.html

[5] Epic Games. (n.d.-b). Matinee and Cinematics. Unreal Engine Documentation. Retrieved May 16, 2021, from https://docs.unrealengine.com/en-US/AnimatingObjects/Matinee/index.html

[6] Ben Cloward. (2020, January 2). Shader Performance Optimization - UE4 Materials 101 - Episode 7 [Video]. YouTube. https://www.youtube.com/watch?v=D8E47BJOE6E