UE4の勉強記録

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

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する 村人用のAIの開発2

f:id:kazuhironagai77:20211010202142p:plain

<前文>

前文には日本人が誤解しているアメリカ、特に日本文化とキリスト教文化の違いから生じる誤解とその解決法について気が付く範囲ですが書いておきます。

焚書を悪い事と思っていない西欧文化

何でも欧米の文化が日本より上と思っている日本人って結構います。私が悲しいのは英語の勉強が好きな人達や欧米に留学した人達にそういう傾向が多い事です。実際は、西洋文化に内側から触れると日本より良い部分も多い反面、日本より全然駄目な部分も沢山あります。

アメリカで十年暮らした私が欧米の文化の中で日本の文化と比べて最も駄目だと思う部分について今回は語ります。

それは欧米人が焚書を悪い事と思っていない事です。

彼等は簡単に本を燃やします。単に燃やすだけじゃなくてその本に書かれた事実もついでに無かった事にします。

日本人にとって一番簡単な例が教会の絵です。昔は裸で書かれていた天使の絵を、後からふしだらと言う理由で服を着せたり、人物ごと消したりします。

アステカ文明とか西洋に完全に滅ぼされた文明の記録なんか全て燃やされてしまいました。残っているのが彼らが生贄を捧げていた残虐な民族であるというキリスト教にとって有利かつCheapな情報だけでアステカ文明という人類の文明にとって欠かせない遺産が現在に全く伝わってない結果になってしまっています。

当時の宣教師たちは大変な知識階級だったわけで「こんな大切な人類の遺産を消す訳にはいかない。」と思って例えそれが大きな罪に問われる事になっても、燃やす前にどこかにコピーを隠したりする人が一人位はいるべきだろうと、日本人なら感じますが実際は当時の宣教師たちの間には全くいなかったわけです。

これが日本の話になると、例えば、私の家の近所には、高野長英が東北に逃げる時に幕府に内緒で匿った家があるんです。

その当時そんな事したら打ち首獄門な訳ですよ。そしてその時は村社会だから村の人はみんなその事を知っている訳です。知らないフリしているけど知っている訳です。

村全体が罰せられるかも知れないんですが、みんな黙っていたんです。

そのせいかどうか知りませんが、未だにその家の人達は、高野長英が東北に逃げるのを助けた○○さんって影で言われています。

でもそういう人達がいたお陰で日本は西洋の科学技術を他のアジアの国と違って素早く吸収する事が出来た訳です。

所が、西洋人にはそういう文化はないんです。本なんか聖書以外は、自分達の直接の利益にならなければ燃やしたって全然OKなんです。

これが西洋人の本に対する普通の感覚なんです。

これを理解した上でないと、なんでユダヤ人があんなにホロコーストに否定的な意見に対して激怒して攻撃しているくるのかも理解出来ないです。

ユダヤ人がああやって怒っていないとホロコースト関連の資料なんか全部燃やされて、西洋社会ではあっという間に無かった事にされてしまうからです。

日本の歴史の例で言えば、戦国時代の日本人奴隷の存在や、長崎の原爆がカソリック教会に落とされた事など、現在はほとんどなかった事にされてしまっています。

そして私が危惧しているのがKindle独占状態における電子書籍化です。電子書籍に関しては私は大賛成です。しかし西洋社会では書籍はちり紙のように軽く扱われます。いつ日本語の本が発禁にされてもオカシクありません。

こういう事実を理解しておけば、日本語の本を西洋人に管理させてはならない。と言う事と、いつ発禁処分にされても大丈夫なようにBlock Chain技術で管理しなければならないと言う結論に自ずと達します。

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

<本文>

1.今週の予定

今週は先週、終わらなかった事を中心にやっていきます。

  • Niagara : 先週の復習とExample Contentsの勉強
  • Material : 先週の復習など
  • AIの復習の続き
  • World CompositionによるMap1の作成
  • Loading Screenの勉強の続き(Moduleについて)

2.Niagara : 先週の復習とExample Contentsの勉強

2.1 Transient Physics Drag変数についての補足

先週、Local Name SpaceとTransient Name Space の違いについて以下の2つの解説を見せました。

このTransientの解説は良いんですが、

f:id:kazuhironagai77:20211010202254p:plain

Localの方です。

f:id:kazuhironagai77:20211010202309p:plain

ここでTransient Valueと言っているのはLocal Valueの間違いですよね。

後、最初の文でSingle Module内でしか使用出来ないと言っているのだから、Frame 間やEmitterからParticle、Spawn Section からUpdate Sectionへの変数の値のパスは勿論出来ません。2番目の文の説明いるんでしょうか?

このLocalの説明は間違ってTransientの説明を載せていて、それをCheckした人がAny ModuleからSingle Moduleの所だけ直した可能性はないでしょうか?

何かとても不自然な文章です。

2.2 Bugs in UE4.27 Niagara Tutorial | Download Files [1] で新しく学んだModuleなどを復習する

<Add Velocity From Point Module

まず解説から読んでいきます。

f:id:kazuhironagai77:20211010202332p:plain

最初の文はこのModuleの機能を説明していると思われます。

2番目のUses the vector…が何を言っているのか分かりませんね。

UsesとSが付いていると言う事は命令形じゃなくて「このModuleは」と言う主語が隠れていると言う事でしょうか?となると「このModuleはVelocity Vectorを計算するのにVelocity originとParticles PositionからのVectorを使用します。」と言う事になるんでしょうか?

Scriptを開いて確認してみましょう。

Map GetノードにVelocity originとParticles Positionらしい変数を見つけました。

f:id:kazuhironagai77:20211010202410p:plain

Default Positionの方はParticles Position変数の値をパスされていました。

f:id:kazuhironagai77:20211010202447p:plain

と言う事はVelocity originとParticles Positionに間違いないですね。

ただVelocity originって具体的に何を指しているのか良く分からないですね。

調べたらDefault Position変数、Velocity origin変数共にSelectionでどんな値を代入するのかを指定されていました。

f:id:kazuhironagai77:20211010202501p:plain

Dynamic Inputなので当たり前だったんですが気が付かなかったです。

これを見るとVelocity origin変数はSimulation Positionをパスされています。

Simulation Positionを調べます。

<<Simulation Position>>

Engine.Owner.Positionの値かLocal Spaceにチェックがある場合は0を返すそうです。

f:id:kazuhironagai77:20211010202520p:plain

あれ、これ前にも使用しましたっけ。

ぱっと確認したら2021-07-11のBlogと2021-09-06そして2021-09-12のBlog内でこのDynamic Inputについて述べています。2021-07-11のBlogではSimulation Position をRibbon の作成に使用しています。2021-09-06、そして2021-09-12のBlogではBuilding advanced effects in Niagara | Unreal Engine [2]内で使用されていました。

Engine.Owner.Positionの値を使用していますが、この値はEmitterの原点を示しているはずです。

以下の図Curl Noise Force Moduleを切った状態ですが、Emitterの原点からそれぞれのParticleのPositionが放射状に広がっています。これは繋げたVectorを初速度のVectorとして使用しているからです。

f:id:kazuhironagai77:20211010202537p:plain

またScriptを見てもVelocityの値にVelocity origin変数とParticles Position変数から計算したNormalized Vector Between Positionsと(ifノードに繋がっている)Velocity Strength変数の値を掛けたものをセットしています。

f:id:kazuhironagai77:20211010202558p:plain

f:id:kazuhironagai77:20211010202604p:plain

これも先程の訳と同じ事を表しています。

f:id:kazuhironagai77:20211010202619p:plain

はい。分かりました。

このAdd Velocity From Point ModuleはそれぞれのParticleに速度を与えるModuleです。速度の方向はEmitterの原点とParticleの発生位置を繋げたVectorから計算します。

その後の解説で

f:id:kazuhironagai77:20211010202633p:plain

もしEmitterの原点とParticleの発生位置が同じ時はどうするのか?とか、

最も正確な結果がほしい時はLocation Moduleの下にこのModuleを配置しろとか説明されています。

f:id:kazuhironagai77:20211010202648p:plain

完全に理解しました。

Curl Noise Force Module

Curl Noiseは流体の動きを表現するのに便利なNoiseです。2021-08-23のBlogでCurl Noiseについて調べています。その時に以下の仮説を述べています。

f:id:kazuhironagai77:20211010202707p:plain

Noise Strengthがベクトル場の大きさを表して、Noise Frequencyがベクトル場の複雑さを表してると言う仮説です。

今までこれを証明する方法がありませんでしたが、前回Curl Noise Forceを追加したParticleを平面上に飛ばすやり方を教わりました。この技術を使用したらNoise Frequencyとベクトル場の複雑さの関係の確認は出来るはずです。それを試してみます。

<<Velocity変数>>

その前にCurl Noise Forceを追加したParticleを平面上に飛ばすやり方で使用しているVelocity変数について復習します。

これです。

f:id:kazuhironagai77:20211010202725p:plain

Velocity 変数そのものについては先程のAdd Velocity From Point Module内でも出て来ましたがParticleの速度を管理する変数です。

今回はその変数を直接、Particle Update SectionにModuleとして追加しています。

そして以下の計算を行っています。

f:id:kazuhironagai77:20211010202739p:plain

Velocity = Velocity * (1,1,0)

をです。

このやり方は覚えておきます。色々な個所で使用出来るはずです。

<<Noise Frequencyとベクトル場の複雑さの関係>>

Noise Frequencyが20の時です。

f:id:kazuhironagai77:20211010202812p:plain

Noise Frequencyが100の時です。

f:id:kazuhironagai77:20211010202825p:plain

うねりが全く無くなってしまいました。

これはCurl NoiseでHigh Frequencyの時に黒と白の部分が細かくなっていくのに一致した結果と思われます

f:id:kazuhironagai77:20211010202839p:plain

Noise Frequencyが100の時、particleは回転しているはずです。ただしその渦が上記のHigh Frequencyの時のように非常に小さくなってパッと見には回転してないように見えていると考えられます。

Noise Frequencyが10の時です。

f:id:kazuhironagai77:20211010202854p:plain

Particleの動きがNoise Frequencyが20の時よりも大きいです。これもCurl Noiseでlow Frequencyの時に黒と白の部分が大きくなっていくのに一致した結果となっています。

この結果から推測するとCurl Noise Force ModuleのDynamic InputであるNoise FrequencyはCurl Noiseでいう所のFrequencyと同じであると言えます。

<Spawn Particle From Other Emitter Module

このModuleは使い方の復習をします。

f:id:kazuhironagai77:20211010202912p:plain

このModule、何回か使用したはずですが、Blogの検索に引っかかりません。

仕方ないので先週のBlogの記録を持っていきました。

f:id:kazuhironagai77:20211010202926p:plain

Sample Particles From Other Emitter ModuleをParticle Spawn Sectionに追加する事がPointですね。

RibbonのRendering方法の復習>

あれだけ勉強したRibbonのRenderingの方法を忘れてしまっていました。復習します。

<<Ribbonの理論>>

まずRibbon Renderingの理論ですが、2021-07-04のBlogによくまとまっています。

簡単にまとめると

Ribbonは以下の様に生成されます。

f:id:kazuhironagai77:20211010202947p:plain

緑の菱型の数がSpawn Rateで指定した数に当たります。

f:id:kazuhironagai77:20211010203000p:plain

それぞれの緑の菱型は

f:id:kazuhironagai77:20211010203015p:plain

Lifetimeで指定した時間だけ存在します。

後、Ribbonの幅はParticle Spawn Section のInitialize Ribbon ModuleのRibbon Width で指定します。

f:id:kazuhironagai77:20211010203030p:plain

<<Ribbonの作り方>>

前のBlogにおけるRibbonの作成方法も読んだんですが分かりにくい。

私が新しく「Ribbonの作り方Tutorial」をここに作ります。

Niagara SystemからEmpty Templateを追加して新しいEffectを作成します。

f:id:kazuhironagai77:20211010203055p:plain

Emitter Update SectionにSpawn Rateを追加します。

f:id:kazuhironagai77:20211010203108p:plain

Particle Update SectionにGravity Forceを追加します。

f:id:kazuhironagai77:20211010203122p:plain

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

f:id:kazuhironagai77:20211010203136p:plain

このSpriteをRibbonに変換します。

まずRender sectionのSprite Render Moduleを消してRibbon Renderer Moduleを追加します。

f:id:kazuhironagai77:20211010203149p:plain

Ribbon Renderer ModuleのMaterialにDefault Ribbon Materialを追加します。

f:id:kazuhironagai77:20211010203202p:plain

こんな感じになります。

f:id:kazuhironagai77:20211010203216p:plain

次にParticle Spawn SectionにInitialize Ribbon Moduleを追加します。Initialize Ribbon ModuleはInitialize Particleの後にセットします。

f:id:kazuhironagai77:20211010203237p:plain

こんな感じです。

f:id:kazuhironagai77:20211010203251p:plain

Ribbon出来ました。

少しだけ微調整します。

Particle Spawn SectionのInitialize Ribbon ModuleのRibbon Widthの値を50に変更して

f:id:kazuhironagai77:20211010203305p:plain

Ribbon の幅を太くしました。

f:id:kazuhironagai77:20211010203319p:plain

更にInitialize Ribbon ModuleのLife timeの値を1.5にしてRibbonを長くしました。

f:id:kazuhironagai77:20211010203333p:plain

f:id:kazuhironagai77:20211010203339p:plain

以上です。

一つ疑問ですがParticle Spawn SectionのInitialize Particle Moduleは要らない気がしています。

f:id:kazuhironagai77:20211010203352p:plain

試しに外して見ましたが、Effectに変化は見られなかったです。

<Scale Ribbon Width Module

このModule自体よりもRibbonのサイズを管理している変数は何かに興味があります。

Initialize Ribbon ModuleのDynamic InputであるRibbon Widthの値はParticle のRibbon Width変数にパスされていました。

f:id:kazuhironagai77:20211010203410p:plain

と言う事はParticle のRibbon WidthがRibbonのサイズを管理する変数と言う事なんでしょうか?

そのようですね。

Scale Ribbon Width Module内でもRibbonのサイズを変更する時は、Ribbon Widthの値を変更しています。

f:id:kazuhironagai77:20211010203424p:plain

以上でしょうか?

2.3 Disintegration in UE5 Niagara Tutorial | Download Files [3]を勉強する

Disintegration Skeletal Mesh FX in U5 Niagara Tutorial [4] をやろうとしたら、

f:id:kazuhironagai77:20211010203445p:plain

このTutorialで使用しているMaterialはDisintegration in UE5 Niagara Tutorial | Download Files [3]で作成したヤツだからそっちを参考にしてと言っていました。

なのでDisintegration in UE5 Niagara Tutorial | Download Files [3]を勉強する事にします。

f:id:kazuhironagai77:20211010203459p:plain

どっちもBuilding advanced effects in Niagara | Unreal Engine [4] の派生ですね。

f:id:kazuhironagai77:20211010203513p:plain

ParticleをCurl Noise で飛ばすかParticleにSpriteを貼り付けて炎のEffectを出している違いだけです。

ただCGHOW氏の方はActorの足がParticle化していないんです。こっちのやり方の方が精錬されています。

Content ExampleのNiagara_ Advancedにも同様のTechniqueが紹介されています。

f:id:kazuhironagai77:20211010203527p:plain

こっちは一回NSの実装を見たけど途中で理解出来ない部分があり、中止しています。

そう言えばUE5で制作中のGame、Black Myth: WukongのTrailerでも似たようなEffectが使用されていました。

f:id:kazuhironagai77:20211010203541p:plain

Black Myth: Wukong - Official Unreal Engine 5 Gameplay Trailer [6] より

Meshがparticle化するEffectはかなり基本的な技術のようです。

良し。今回はこの技術の習得を目指して勉強します。

前、Building advanced effects in Niagara | Unreal Engine [4]を勉強した時は、CGHOW氏のTutorialをやっても再現も出来なければやっている内容の意味も分からないLevelだったので、CGHOW氏のTutorialと比較してBuilding advanced effects in Niagara | Unreal Engine [4]がどれだけ分かり易いがが焦点でした。

のでMeshが消えてその代りにParticleをMeshに沿って生成させる技術の理解はニの次でした。実際、どうやってそれを実現させたのかは分かっていません。

今回は、その辺の技術に注目して勉強しようと思います。

  • Meshが消える技術
  • ParticleMeshに沿って発生させる技術
  • 発生したParticleを動かす技術

などについてです。

2.4 Building advanced effects in Niagara | Unreal Engine [4] の復習

Disintegration in UE5 Niagara Tutorial | Download Files [3]を勉強する前にBuilding advanced effects in Niagara | Unreal Engine [4]  の復習をやっておきます。特に

  • Meshが消える技術
  • ParticleMeshに沿って発生させる技術
  • 発生したParticleを動かす技術

についてどうやったのかを復習しておきます。

調べるとBuilding advanced effects in Niagara | Unreal Engine [4]  の勉強は2021-09-06のBlogから開始しています。

<ParticleをMeshに沿って発生させる技術>

Initialize Mesh Reproduction Sprite Moduleを使用しています。

f:id:kazuhironagai77:20211010203706p:plain

ただこのModuleが指定されたMeshに沿ってSpriteを発生する以外の機能については分かっていないと書かれています。

Meshに沿ってSpriteを発生させるModuleは以下に示したように沢山あるじゃないの?

f:id:kazuhironagai77:20211010203720p:plain

と書かれていました。

この辺は今回の勉強でも注目します。今回、勉強するTutorialでもInitialize Mesh Reproduction Sprite Moduleを使用するのか?するならばその理由は何なのか?などが注目するPointになるでしょう。

<発生したParticleをMeshの動きに追随させる技術>

Particleを発生させる事が出来てもそのParticleはMeshの動きには追随しません。

f:id:kazuhironagai77:20211010203745p:plain

それを直すために以下の事をしています。

f:id:kazuhironagai77:20211010203758p:plain

今回のTutorialはStatic Meshに対してなのでこの技術が使用されるかどうかは分かりません。その次に行うDisintegration in UE5 Niagara Tutorial | Download Files [3]では注目すべき個所です。

<発生するParticleの色をMeshのMaterialの色と同じにする>

これは以下に示したように出来なかったんです。

f:id:kazuhironagai77:20211010203816p:plain

最新のCrunchは沢山のMaterialを使用していてTutorialで使用されているCrunchは一個のMaterialしか使用していませんでした。Meshが一個のMaterialしか使用していない時のやり方はTutorialから理解出来ましたが、Meshが複数個のMaterialを使用していた場合のやり方が分からなかったです。

今回のTutorialはMeshにあるMaterialと同じ色を発生する必要はないのでこの技術は謎のままです。

<Spriteが四角な事、Spriteが常にカメラを向いている事>

うーん。こんな事も勉強していたんですね。

f:id:kazuhironagai77:20211010203835p:plain

すっかり忘れていました。

カメラの向きは以下の方法で直します。

f:id:kazuhironagai77:20211010203847p:plain

ただこの方法で直せるのは以下の理由があるからだそうです。

f:id:kazuhironagai77:20211010203900p:plain

成程と書いていますが、その理由が書いていません。

今考えると、Custom Facing Vectorが一個一個のNormal Vectorを指しているはずです。Meshは動きますが一個一個のNormal VectorはUpdate Mesh Reproduction Sprite Moduleで常に最新の状態にUpdateされます。

多分、こう考えて成程と書いたんだと思います。

Spriteを丸くするのは以下の方法でやっています。

f:id:kazuhironagai77:20211010203913p:plain

PointはRadial Gradient Exponential ノードを使用してる所です。

f:id:kazuhironagai77:20211010203926p:plain

ここからは2021-09-12のBlogになります。

CrunchがObjectに触ったらCrunchのMaterialが消える方法>

この部分はあんまり丁寧に書かれていなくて良く分からない部分もありました。

分かった範囲で書くと

以下の実装をMaterialのOpacity Maskに追加すると

f:id:kazuhironagai77:20211010203946p:plain

以下の様にParticleが発生する箇所のMeshのMaterialが消えます。

f:id:kazuhironagai77:20211010204000p:plain

この部分の実装に関しては2021-09-19のブログで詳しく解説されています。

簡単に説明するとDitherTemoralAAノードはMaterialを透明にするノードです。

例えば以下の実装だと

f:id:kazuhironagai77:20211010204013p:plain

こんな感じにMaterialを消します。

f:id:kazuhironagai77:20211010204025p:plain

このTutorialで使用した実装を元にした以下の例では

f:id:kazuhironagai77:20211010204038p:plain

Subtract ノードとDivideノードの値によって以下のような変化を示します。

f:id:kazuhironagai77:20211010204052p:plain

f:id:kazuhironagai77:20211010204059p:plain

これでMaterialを消している訳です。

ただしこの方法だと発生したParticleはバラバラになって移動しません。

そのためには以下のような新しいModuleを作成して

f:id:kazuhironagai77:20211010204113p:plain

色々やっているんですが何をやっているのかが良く分かりません。

実際の記録でも

f:id:kazuhironagai77:20211010204128p:plain

と書いてありました。

この辺が何をやっているのかを理論のレベルから理解するのも今回の課題にします。

Building advanced effects in Niagara | Unreal Engine [4] の復習まとめ

復習した事で以下のPointで重要な情報が得られました。

<<ParticleをMeshに沿って発生させる方法>>

Building advanced effects in Niagara | Unreal Engine [4]ではParticle Spawn SectionでInitialize Mesh Reproduction Sprite Moduleを使用する事で可能にしています。

f:id:kazuhironagai77:20211010204153p:plain

<<Meshを消す方法>>

Material内でDither Temporal AA ノードを使用する事で可能にしています。

f:id:kazuhironagai77:20211010204211p:plain

<<発生したParticleを動かす方法>>

この部分はどうやっているのか分かっていません。

2.5 Disintegration in UE5 Niagara Tutorial | Download Files [3]の勉強の続き

ぱっと一回見ましたが結構違う方法でやっています。

取りあえず今週はやれる所までやってみて、検討は来週以降します。

Fountain Templateを追加したNiagara Systemを作成しました。

f:id:kazuhironagai77:20211010204234p:plain

要らないModuleを全部消した後、Particle がMeshに沿って発生するためにSample Static Mesh ModuleとStatic Mesh Location Moduleを使用しました。

f:id:kazuhironagai77:20211010204308p:plain

Sample Static Mesh ModuleのPreview MeshとDefault MeshにはSM_Chairをセットしました。

f:id:kazuhironagai77:20211010204328p:plain

こんな感じです。

f:id:kazuhironagai77:20211010204341p:plain

Materialを作成します。

f:id:kazuhironagai77:20211010204354p:plain

SM_Chairに適応しました。

f:id:kazuhironagai77:20211010204408p:plain

Emitter Properties ModuleのSim TargetをGPUに変更してSpawn Rateを10000に変更しました。

f:id:kazuhironagai77:20211010204420p:plain

イスが見えます。

f:id:kazuhironagai77:20211010204433p:plain

このParticle がMeshのMaterialを侵食して発生するようにします。

イスをLevel上に配置します。

f:id:kazuhironagai77:20211010204448p:plain

Materialに以下の実装をします。

f:id:kazuhironagai77:20211010204500p:plain

上記のMaterialのInstanceを作成してそれをイスのMaterialに適応します。

適切な値を選択すると以下の様にイスのMaterialがある部分だけ消滅します。

f:id:kazuhironagai77:20211010204514p:plain

Materialを消すのにSphere Maskノードを使用しています。後でこのノードについては調べます。

今度はこの球に境界線を追加します。

以下の実装をMaterialに追加しました。

f:id:kazuhironagai77:20211010204534p:plain

3ColorBlendノードの機能が良く分かりません。後で調べます。

こんな結果になりました。

f:id:kazuhironagai77:20211010204548p:plain

このMaterialを使用すると以下の様に椅子の消えた部分との境目に境界線が現れました。

f:id:kazuhironagai77:20211010204602p:plain

今度は消えた部分からParticleを発生させます。

まず以下の先程作成したModuleを消します。

f:id:kazuhironagai77:20211010204618p:plain

えー。何で?

折角新しいModuleを習ったと思ったらもう消すそうです。

理由がColorがVertexの Colorを使用しているからだそうです。MaterialのColorを使用したいそうです。

以下の部分がそうみたいです。

f:id:kazuhironagai77:20211010204630p:plain

f:id:kazuhironagai77:20211010204636p:plain

f:id:kazuhironagai77:20211010204643p:plain

これだけ見るとVertexのColorの値をPassしているのかどうかは分かりませんね。これも後で検討します。

消したSample Static Mesh ModuleとStatic Mesh Location Moduleの代わりに以下のModuleをScratch Pad Moduleを使用して作成します。

f:id:kazuhironagai77:20211010204659p:plain

Dynamic Material Parameterが何をしているのか不明です。

この変数は何を管理しているんでしょうか? 

UV値をパスしていると言う事はTexture関連の変数なんでしょうか?

今度は先程作成したMaterialをDuplicateします。新しい方のMaterialの名前をM_ParticleBodyと名付けます。そして以下のコードを追加します。

f:id:kazuhironagai77:20211010204715p:plain

先程のScratch Moduleで使用したDynamic Material ParameterはこのMaterial内で使用しているDynamic Parameterとlinkしているんでしょうか?

そうすると理屈が通りそうですね。

この部分の意味は分かりましたがこのMaterialをどうすんでしょうか?

Render SectionのSprite Renderer ModuleのMaterialにセットしました。

f:id:kazuhironagai77:20211010204729p:plain

f:id:kazuhironagai77:20211010204734p:plain

うーん。これってどういう事?

これってParticleをMeshがある部分だけ生成すると言う事でしょうか?

何も生成していません。

f:id:kazuhironagai77:20211010204748p:plain

Particleが生成する箇所を逆にするために1-xノードを消しました。

f:id:kazuhironagai77:20211010204801p:plain

こんな感じです。

f:id:kazuhironagai77:20211010204814p:plain

そしてNSそのものをMeshに重ねています。

f:id:kazuhironagai77:20211010204826p:plain

所が私のNSは

f:id:kazuhironagai77:20211010204841p:plain

全部Particleになっています。しかも移動してくれません。がっちりCenterに位置しています。

理由が分かりました。

このMeshが消える球が原点に位置しています。NSもMeshも原点に置かないと消滅したりParticleになったりする位置が変わってしまいます。

両方を原点に設置したら以下の様になりました。

f:id:kazuhironagai77:20211010204856p:plain

そう言う訳で出来てはいました。この原点から移動出来ない理由は後で調べます。

今度はParticleの位置とMeshの位置を手動でコントロール出来るようにします。

Material Functionを作成します。

f:id:kazuhironagai77:20211010204911p:plain

ここにFloat型のParameterであるRadiusを作成します。

f:id:kazuhironagai77:20211010204923p:plain

これを先程作成した二つのMaterialのRadiusと交換します。

f:id:kazuhironagai77:20211010204936p:plain

f:id:kazuhironagai77:20211010204941p:plain

これでMaterial Functionを使用してMeshとParticleの位置が自由に移動出来るようになりました。

今度はParticleに速度を与えて動かします。

Particle Update SectionにCurl Noise Force Moduleを追加します。

f:id:kazuhironagai77:20211010204954p:plain

値を色々微調整して

f:id:kazuhironagai77:20211010205250g:plain

今度は境界線の部分だけParticleが発生するようにします。

流石に疲れて集中力が切れてきました。残りは、来週やる事にします。

3.Material : 先週の復習など

今週もBen Cloward氏のShader Graph Basics [7]の勉強をしていきます。

先週のBlogのMaterialの箇所を読み直しましたが特に復習しなければならない箇所はないように思えます。

先週のBlogを読み返した感想としては習った基礎の応用方法を忘れないようにしたいです。

3.1 Input Vectors - Shader Graph Basics - Episode 9 [8] を勉強する

正直、集中力が切れてしまっているのでゆっくりやります。

最初の疑問ですがInput Vectorって何を指しているんでしょうか?

これが調べたんですが良く分からない。ならこのTutorialを見た方が早いと兎に角、最初から見る事にしました。

Tutorialでは以下の3つがInput Vectorとして紹介されていました。

Surface Normal

まあ当然ですね。Normal Vectorの事ですね。

既にNormalization(正規化)された状態で保存されています。

<Camera Vector

カメラから物体に当たるまでの距離と角度を保存しているVectorだそうです。このVectorはPixel一個一個が保持しているんでしょうか?

Normalization (正規化)はされていないそうです。

<Light Vector

光源からその光が当たる物質との角度と距離を示すVectorです。

Normalization (正規化)はされていないそうです。

UEではlightの計算は後で行う(Deferred)からMaterial内で使用する事は出来ないそうです。このRenderingの方法をDeferred Renderingと言うそうです。

<Vertex Normal WSノードなどについて>

Vertex Normal WSノードは公式のDocumentであるCoordinates Expressions [9] には

f:id:kazuhironagai77:20211010205519p:plain

と説明されています。

TutorialではWorld Spaceについては後でしっかり解説するので今は計算するVectorが同じSpaceにいる事だけを確認しておけば大丈夫といっています。

そこは当然と思ったんですが、SpaceにはWorld Spaceの他にObject Space、Tangent Spaceがあるそうです。

World SpaceってWorld Coordinateと同じと思っていましたが違うのかもしれません。

Vertex Normal WSを使用して以下の実装を組みました。

f:id:kazuhironagai77:20211010205532p:plain

こんな結果になりました。

f:id:kazuhironagai77:20211010205547p:plain

これだけではLightの影響が分からないので以下の場合も試しました。

f:id:kazuhironagai77:20211010205559p:plain

こんな感じです。

f:id:kazuhironagai77:20211010205614p:plain

思ったより明るいですね。(0,1,0)と垂直になる球の中央が白いのはわかりますが、先週の例のような黒さは出てないです。

f:id:kazuhironagai77:20211010205631p:plain

設定をUnlitにしたら以下の様になりました。設定の問題でした。

f:id:kazuhironagai77:20211010205650p:plain

Tutorialによると以下の実装でも同じ効果が得られるそうです。

f:id:kazuhironagai77:20211010205704p:plain

確かに計算結果は同じですね。

<Camera Vectorなどについて>

まずCamera VectorとVertex Normal WSで以下の様にDot Productを実装します。

f:id:kazuhironagai77:20211010205722p:plain

結果です。

f:id:kazuhironagai77:20211010205735p:plain

今度はCamera Vector はNormalize(正規化)されていないのでNormalizeノードを追加します。

f:id:kazuhironagai77:20211010205748p:plain

こんな結果になりました。

f:id:kazuhironagai77:20211010205800p:plain

あれ、同じ?

実はUEのCamera Vectorは最初からNormalize(正規化)されているそうです。

今度はPowerノードを実装します。

f:id:kazuhironagai77:20211010205812p:plain

Power = 2の時です。(Powerとは乗数の事です。)

f:id:kazuhironagai77:20211010205826p:plain

Power = 3の時です。

f:id:kazuhironagai77:20211010205838p:plain

Power = 5の時です。

f:id:kazuhironagai77:20211010205851p:plain

One Minusを使用して白黒を逆にする事も出来ます。

f:id:kazuhironagai77:20211010205921p:plain

こんな感じです。Fresnel Reflectionと同じ効果になります。

f:id:kazuhironagai77:20211010205933p:plain

Normalize(正規化)されていないCamera Vectorを使用したい時もあります。

例えばCameraから写っているActorへの距離を知りたいときです。

そんな時は以下の実装でNormalize(正規化)されていないCamera Vectorを得る事が出来るそうです。

f:id:kazuhironagai77:20211010205946p:plain

うん。と言う事は以下の関係が成り立つと考えられるので

f:id:kazuhironagai77:20211010205959p:plain

Absolute World PositionはActorの位置を指していると思われます。

この実装を使用してCameraから写っているActorへの距離を計算するには以下のようになります。

f:id:kazuhironagai77:20211010210012p:plain

しかし実際の実装を見ると以下の様にその後にSubtractノードとDivideノードが追加されています。

f:id:kazuhironagai77:20211010210025p:plain

これらの働きは何なのでしょうか?

まずSubtractですが、上記の例だと500 cm つまり5mの距離内にあるActorは全て真っ黒(0)になると言う意味です。Offsetと同じです。

次のDivideですが、5000cmになっています。つまり50mです。この50mの間で真っ暗から真っ白、0から1に変化すると言う事です。50m以上離れたActorは真っ白になります。Mask Lengthと同じです。

と解説されていました。

これは分かり易いです。この説明でやっと意味が分かりました。

Tutorialでやっていたのと同様のテストも試してみます。

カメラに近い時です。

f:id:kazuhironagai77:20211010210039p:plain

球は真っ黒です。

球をカメラから放すと色が薄くなりました。

f:id:kazuhironagai77:20211010210051p:plain

球をもっと放すと白くなりました。

f:id:kazuhironagai77:20211010210105p:plain

<Light Vectorノードについて>

以下に示したようにLight VectorノードとVertex Normal WS ノードでDotした値をBase Colorに繋ぐとErrorになります。

f:id:kazuhironagai77:20211010210123p:plain

以下の様な説明がされていました。

f:id:kazuhironagai77:20211010210141p:plain

UEではDeferred Rendering方式を採用しているのでLightの計算はMaterialの計算をした後で行うそうです。なのでこのErrorの説明はMaterial内でLight Vectorを使用する事は出来ないと言っている訳だそうです。

ウオー。

こういう事を知りたかった。

と言う事はLight FunctionやDeferred Decal MaterialはLightのRenderingが終わった後でやる特殊なMaterialと言う事も同時に理解出来ました。

Light VectorノードとVertex Normal WS ノードでDotした値をBase Colorに繋ぐのは単にDiffuse Lightingの計算でPreviewをLitで見れば、Diffuse Lightingは既にEngine側で計算されているのも分かります。

f:id:kazuhironagai77:20211010210205p:plain

成程。

後、このDiffuse Lightingの計算はG-buffer内で行われるとありました。

そう言えば、Disintegration in UE5 Niagara Tutorial | Download Files [3]で、CGHOW氏が「○○さんはUEで同じTechniqueを作成していますが、彼はG-bufferを使用して作成しました。私のNSはG-bufferは使用していません。」と言っていた気がします。

G-bufferやDeferred RenderingそのものについてはDeferred Shading [10] で詳しく解説されています。これは後で勉強します。

Tutorialはこの後はUnityの場合の解説でした。

同じ事をUnityでやってるだけですが結構実装方法に違いがあって面白いです。

UEでVertex Normal WSノードにMaskをしてDot Productと同じ効果を出すやり方をUnityでやっています。

f:id:kazuhironagai77:20211010210224p:plain

この部分です。

f:id:kazuhironagai77:20211010210238p:plain

この計算が以下と同じと言う奴です。

f:id:kazuhironagai77:20211010210251p:plain

所がUnityの場合はG(1)が上下の軸を表してるだそうです。

だから以下のようなImageになるそうです。

f:id:kazuhironagai77:20211010210304p:plain

Unityの公式のDocumentのTransforms [11] には以下の説明図が載っていました。

f:id:kazuhironagai77:20211010210317p:plain

そう言えば、昔UE4の軸がオカシイと何回も計算した記憶があります。

調べたら2019-05-05のBlogでした。

f:id:kazuhironagai77:20211010210338p:plain

z軸の回転方向が一般の場合と逆だ。と書いてありました。Unityの軸とは関係なかったです。

次はUnityにおけるFresnel Reflectionの実装です。

f:id:kazuhironagai77:20211010210356p:plain

ここで大切なのがUEにおけるCamera VectorとUnityのCamera Directionは向きが逆で、Unityの場合はNegateノードを使用する必要がある事だそうです。

UnityにおけるCameraから写っているActorへの距離の計算です。

f:id:kazuhironagai77:20211010210407p:plain

ここでもCamera PositionとPositionからNormalize していないCamera Vectorを計算して使用しています。Camera VectorがNormalizeしてないと言った最初のコメントは何だったんでしょうか?

後、Unityでは単位がmだそうです。上記のSubtractで9.5が代入されていますが、9.5mの事だそうです。

3.2 View, World, Object, & Tangent Space - Shader Graph Basics - Episode 10 [12] を勉強する

知らなかった事を記録しておきます。

View SpaceはZ軸がカメラの見ている方向になっています。

f:id:kazuhironagai77:20211010210427p:plain

Tangent Spaceについてです。

Vertices一つ一つが持つCoordinateの事のようです。

f:id:kazuhironagai77:20211010210440p:plain

赤がそのVertexのNormal、青と緑がUVを表しているそうです。

ここからはCoordinateが違うVector同士の計算方法についてです。

<Spherical Coordinate

Spherical Coordinateは別名をMat Cap Reflectionsと言うそうです。

Spherical Coordinate用のTextureを手に入れます。

Tutorialと同じモノにしました。

f:id:kazuhironagai77:20211010210504p:plain

このTextureをそのまま使用すると

f:id:kazuhironagai77:20211010210529p:plain

こうなります。

f:id:kazuhironagai77:20211010210552p:plain

Vertex Normal WSを直接繋げた場合は

f:id:kazuhironagai77:20211010210606p:plain

こうなります。

f:id:kazuhironagai77:20211010210620p:plain

この二つを上手く使ってTextureが球の反射をしているかのように表現します。

以下の様に実装します。

f:id:kazuhironagai77:20211010210636p:plain

結果です。

f:id:kazuhironagai77:20211010210649p:plain

ここで使用した重要なノードであるTransform Vectorノードについて解説します。

f:id:kazuhironagai77:20211010210702p:plain

以下の様にセットする事でWorld SpaceをView Spaceに変換します。

f:id:kazuhironagai77:20211010210719p:plain

それがどういう事かを示すために以下の実装で試すと

f:id:kazuhironagai77:20211010210733p:plain

以下の結果が表示されます。

f:id:kazuhironagai77:20211010210804p:plain

Vertex Normal WSのみとの違いは幾らObjectを動かしても色が変化しない事です。

この値をTexture Coordinateとして使用するそうです。

そのまま使用した場合は

f:id:kazuhironagai77:20211010210825p:plain

こうなります。

f:id:kazuhironagai77:20211010210839p:plain

4つのTextureが表示されていると言う事はUVの両方を0.5倍すれば1個のTextureが表示されるはずです。

f:id:kazuhironagai77:20211010210857p:plain

Vが‐0.5なのは向きを上下逆にするためだそうです。UEのCoordinateのY軸が下に向かって+だからでしょうか?

結果です。

f:id:kazuhironagai77:20211010210911p:plain

Textureの中心がずれています。UV両方に0.5を足します。

f:id:kazuhironagai77:20211010210923p:plain

結果です。

f:id:kazuhironagai77:20211010210936p:plain

<Tangent Space to World Space

以下の実装をします。

f:id:kazuhironagai77:20211010210953p:plain

こんな感じです。

f:id:kazuhironagai77:20211010211009p:plain

ここに苔を生やします。

もし単純に苔のTextureを使用すると

f:id:kazuhironagai77:20211010211035p:plain

以下のようになり、全然作成したいImageと違うものになります。

f:id:kazuhironagai77:20211010211050p:plain

今度はLerpを使用してBlendします。

f:id:kazuhironagai77:20211010211102p:plain

半々でBlendします。

f:id:kazuhironagai77:20211010211118p:plain

緑がかった石になりました。

今度はVertex Normal WSを利用します。以下の実装をすると

f:id:kazuhironagai77:20211010211131p:plain

以下の様になります。

f:id:kazuhironagai77:20211010211144p:plain

これをLerpに使用すると以下の様な実装になり

f:id:kazuhironagai77:20211010211157p:plain

こんな結果になります。

f:id:kazuhironagai77:20211010211212p:plain

前よりはかなりマシですがそれでも変です。

何が変かと言うと岩のゴツゴツが苔の生えている部分にはないんです。

じゃあ、岩のNormalをLerpのAlphaに使用したらいいんじゃないでしょうか?

所が、岩のNormal MapはTangent SpaceなのでWorld Spaceとは繋げないんです。

はい。

Transform Vector ノードを使用します。

以下の様にです。

f:id:kazuhironagai77:20211010211226p:plain

Tutorialによると岩のTextureをTransform Vectorノードを使用してWorld Spaceに変更した後は必ずSaturateをして‐の値を消す必要があるそうです。

後は一緒です。

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

f:id:kazuhironagai77:20211010211239p:plain

このやり方の凄い所がTutorialで説明されていました。

このMaterialを使用した岩を下に示します。

f:id:kazuhironagai77:20211010211253p:plain

この岩を回転させると

f:id:kazuhironagai77:20211010211307p:plain

それでも苔は岩の上だけに生えています。

はあー。凄いです。

これならこのMaterialを使用すれば岩を色々な角度に配置するだけで凄いリアルな岩山が作成出来ます。

私のRPGのこの岩山のMaterialに使用したいです。

f:id:kazuhironagai77:20211010211323p:plain

99%はこのMaterialの方が良いですが、極稀に岩を転がす場合が存在します。

その時に苔が常に上に移動したら変です。その場合の対策をこれからやります。

Transform VectorノードのDestinationの値をWorld SpaceからLocal Spaceに変更します。

f:id:kazuhironagai77:20211010211337p:plain

以上です。

Object SpaceはどうやらUEでいうLocal Spaceの事だったようです。

ここでUEは終でUnityのMaterialに変わります。

やっている内容は全く同じなので特に記録したい事はなかったです。

以下にUnityで作成した苔の生えた岩のPreviewを示します。

f:id:kazuhironagai77:20211010211350p:plain

f:id:kazuhironagai77:20211010211356p:plain

言ってはアレですがUEに比べるとかなりショボいです。

これは使用しているTextureの差が大きいように思えます。

Quixelの超高品質のTextureがそのまま使用出来るUEはGraphicsに関してはUnityを完全に制圧していますね。

もう一個Tutorialをやる予定だったですが、結構疲れてしまいましたので、ここでMaterialの勉強は終了します。

Ben Cloward氏のTutorialは質が高くてかつ親切で分かり易いです。来週もこのTutorialで勉強します。

4.AIの復習の続き

4.1 先週の復習

先週、NPCにAIを追加してもっと人間らしい動きを再現しようとしたのですが、UE4_AIのやり方をほとんど全部忘れていました。のでMonsterのAIを作成した時のBlogを見直してUE4_AIの復習をする事にしました。

Blogを読み直すと、何を言っているのか分からない箇所が多々ありました。これは半分は私のUE4_AIの知識がその当時程無くなってしまったためですが、残り半分はBlogの書き方が悪いからです。

そこでUE4_AIについて書かれた箇所を、もう一回まとめ直す事にしました。

以下の様にまとめ直す事にしました。

<基礎編>

  • UE4_AIは単なるIF節!
  • UE4_AIを構成する4つのクラスとその繋ぎ方
  • Task BPService BPそしてDecorator BPについて
  • Task BPAbort関数、Execute 関数、そしてTick関数
  • Task BPFinish Execute ノードとFinish Abortノード
  • SequenceSelector
  • Debugのやり方
  • Observer Abortの機能について

<応用編>

  • AI Perceptionについて
  • EOSについて

しかし量が多すぎたのでまとめきらないで先週は終わってしまいました。今週はその続きをやります。

番号が先週と全く変わってしまうと、後から見た時に訳分からなくなるので、番号を先週の続きから始める事にします。

5.AIの復習の続き Part 2

先週、途中までやったUE4_AIのまとめの続きを行います。

番号が先週と全く変わってしまうと、後から見た時に訳分からなくなるので、番号を先週の続きから始める事にします。

5.6 Task BPService BPそしてDecorator BPについて

そうだ。思い出しました。今までUE4のAIは単なるIF節です。しかしBehavior Treeシステムを採用しているので単なるIF節を書くのに4つのクラスを使用する必要があるんです。

と説明していたら、Behavior Treeを作成するのにTask BPと言うクラスが必要だと判明して、整合性が取れなくなってしまったんです。

実際、Behavior Treeを作成するためにはTask BPだけでなくService BPとDecorator BPも必要です。

しかもこのService BPとDecorator BPの機能について全く覚えていません。その復習をまずやります。

分かりました。

2021-02-01のBlogでTaskですべき内容とServiceですべき内容について熱く語っていますが、

f:id:kazuhironagai77:20211010211514p:plain

UE_AIを通常のAIに当てはめて考えるから色々矛盾が出て来る訳で、単なるIF節と考えれば何の問題もありません。

f:id:kazuhironagai77:20211010212042p:plain

If節の“何かの条件を指定“する為のクラスが、Decorator BPです。

そして“実行する内容”を指定するのが、Task BPとService BPです。

Task BPとService BP の違いはBehavior Treeの最後のノードで実行する内容を指定する場合はTaskノードが使用され、Behavior Treeの途中で実行する内容を指定する場合はService BPが使用されます。

先週作成したテスト用のBehavior TreeだとSequenceノード内に“実行する内容”を指定する場合はService BPが使用されます。

f:id:kazuhironagai77:20211010212109p:plain

では試しにService BPを作成して試してみましょう。

<Service BP

Behavior TreeのNew ServiceからBTService_BlueprintBaseを選択します。

f:id:kazuhironagai77:20211010212131p:plain

するとBTService_BlueprintBaseが開きます。

f:id:kazuhironagai77:20211010212512p:plain

Browseボタンを押してこのクラスが作成された箇所に飛びます。

f:id:kazuhironagai77:20211010212527p:plain

名前を変更します。FirstServiceとします。

名前を変更したらFirstServiceのBP内に戻ります。

そこに以下のコードを追加します。

f:id:kazuhironagai77:20211010212540p:plain

今度はBehavior Treeに戻り、Sequenceノードに先程作成したFirst Serviceを追加します。

f:id:kazuhironagai77:20211010212554p:plain

こんな感じになります。

f:id:kazuhironagai77:20211010212610p:plain

実行してみます。

f:id:kazuhironagai77:20211010212624p:plain

First Serviceの内容が実行されました。

では最後のif 節の条件を指定するDecorator BPについて解説します。

<Decorator BP

実はDecorator BPはService BPやTask BPと比較すると使用方法を理解するのが比較的難しいんです。

その理由ですが、IF節の条件は以下に示したように

X = 0

「Xのような変数の値が0の時は…」と言う指定の仕方をするからです。

そしてUEのBehavior Treeは変数の値を保持する事が出来ないんです。だからBehavior TreeはBlackboard クラスのInstanceを利用して値を保持します。このBlackboardクラスの使用方法を先に理解する必要があるんです。

と言う訳でDecorator BPの使い方を理解する前にBlackboard クラスの使用方法を勉強します。

<Blackboard クラスの使用方法>

今まで何回もBlackboardクラスはBehavior Treeのために変数の値を保持しますと言いました。

どうやって保持するのでしょうか?

それをここで解説します。

まずBlackboardクラスを開きます。すると以下の様になっています。

f:id:kazuhironagai77:20211010212708p:plain

以下に示した様にNew Keyを押すと変数が作成出来ます。

f:id:kazuhironagai77:20211010212723p:plain

今回は一番簡単そうなBoolean型を選択します。名前はFirstBB_Variableとしました。

f:id:kazuhironagai77:20211010212736p:plain

以上です。

え。

いやいや、作成したBoolean型である変数に値をセットしていないじゃないですか?

Behavior Treeの変数の値を保持するのがBlackboardクラスの役目なのにBlackboardに作成した変数の値を指定しないのってオカシイじゃないですか?

はい。

その通りです。

しかしBlackboardクラスの役目はあくまで変数の値を保持する事なのでBlackboardクラスからはBlackboardクラス内の変数の値ですら指定する事は出来ないんです。

それが出来るのはBehavior Treeなんです。

<<Behavior TreeからBlackboardクラスの変数の値を指定する>>

もう色々なクラスを行ったり来たりして頭が大分こんがらがって来てると思います。

今の状況を簡単に整理します。

Behavior Treeの使用方法を理解するためには3つのクラスの使用方法を勉強する必要がありました。その内の2つTask BPとService BPについて勉強しました。残りのDecorator BPの使用方法を理解するためには、Blackboardクラスの使用方法を理解する必要があり、Blackboardクラスにおける変数の値を指定するためには、Behavior Treeから指定する必要があると分かりました。

Behavior Treeの使用方法を理解するためにはBehavior Treeを使用する必要があると言う地獄のような状況です。

はい。

でも大丈夫です。

Blackboardクラスにおける変数の値を指定するためにBehavior Treeで使用するのは、今まで勉強したTask BPとService BPのどちからで可能だからです。

現在、Behavior Treeは以下の様になってます。

f:id:kazuhironagai77:20211010212757p:plain

First TaskにDecorator BPを追加して、先程Blackboardクラスで作成したBoolean型である変数、FirstBB_VariableがTrueだったらFirst Taskを実行するように設定したいです。

そうなるとBlackboardクラスのFirstBB_Variable変数の値は、その前のSequenceにあるService、FirstService内で指定するのがよさそうです。

FirstServiceのBPを開きます。

最後のノードであるPrint StringにBlackboardクラスに値をセットするためのノードを繋げます。

そのノードの名前は、Set blackboard Value…です。そのものズバリの名前ですね。

f:id:kazuhironagai77:20211010212811p:plain

今回はBlackboardのBoolean変数に値をセットするのでSet blackboard Value as Boolを選択します。

すると以下の様なノードが表示されます。

f:id:kazuhironagai77:20211010212823p:plain

ここでValueの意味は直ぐに分かります。この値がBlackboardクラスのFirstBB_Variable変数にセットされるはずです。Trueにしておきます。

残りのKeyですが、これは何を指定するでしょうか?

はい。

ちなみにKeyを指定しないとErrorになります。

f:id:kazuhironagai77:20211010212839p:plain

それでは答えから先に言います。

Blackboard内の変数は今はBoolean型が一つしかありませんが、後々、2つ3つと増えていきます。この時にどのBoolean変数を指しているのかを指定する必要があります。

Blackboard内のどのBoolean変数の値をセットするのかをKeyで指定する必要があります。

ただしこのやり方がまたちょっと複雑になります。

それはServiceクラスであるFirstServiceは直接はBlackboardクラスとやり取りする事が出来ないからです。Behavior Treeを通してしかやり取り出来ません。

ではそのやり方を指定します。

まずService BP内で新しい変数を作成します。取りあえず名前はFirstBB_Keyにしました。

f:id:kazuhironagai77:20211010212854p:plain

目をクリックしてPublicにします。なぜこれをしないといけないのかについては後で説明します。

この変数のタイプをBlackboard Keyにします。

f:id:kazuhironagai77:20211010212909p:plain

この作成した変数、FirstBB_KeyをSet Blackboard Value as BoolのKeyに繋げます。

f:id:kazuhironagai77:20211010212937p:plain

はい出来ました。

ここでCompileするとErrorも消えます。

いやいや、ちょっと待って下さい。まだBlackboardクラスのFirstBB_Variable変数にこの値をセットして下さいって指定していませんよね。

はい。

それをこれからやるんです。

正しそれはService BPであるFirstServiceでは行いません。Behavior Tree内で行います。

Behavior Classに戻ってFirst Serviceを選択すると、以下に示したように先程作成した変数であるFirst BB Keyが表示されています。

f:id:kazuhironagai77:20211010212955p:plain

ここにBlackboardクラスのBoolean型の変数であるFirst BB Keyをセットします。

f:id:kazuhironagai77:20211010213008p:plain

これでやっとBlackboardクラスに作成した変数に値をセット出来ました。

最後にFirst Service内の変数であるFirstBB_KeyをPublicにしないといけないのかについて説明します。

f:id:kazuhironagai77:20211010213022p:plain

Privateに戻しました。

f:id:kazuhironagai77:20211010213035p:plain

するとBehavior TreeからFirst Serviceを選択してもFirstBB_Keyが表示されません。

f:id:kazuhironagai77:20211010213047p:plain

なのでFirst Service内の変数であるFirstBB_KeyはPublicにする必要があります。

<Decorator BP の使い方 Part 2

はい。とうとうBlackboard クラスの変数、FirstBB_Keyの値がセットされました。

なのでDecorator BPで

If( FirstBB_Key == True)

を実装します。

実はDecoratorには既にBlackboardの値に対応しているモノがあります。

f:id:kazuhironagai77:20211010213124p:plain

Blackboardです。折角あるのでこれをそのまま使用する事にします。

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

f:id:kazuhironagai77:20211010213138p:plain

そしてBlackboard Based ConditionのBlackboard KeyにFirstBB_Variableをセットします。

f:id:kazuhironagai77:20211010213150p:plain

これだけです。

テストしてみます。

f:id:kazuhironagai77:20211010213203p:plain

Blackboard Based ConditionがTrueなのでFirst Taskが実行されました。

今度は、First Service 内のSet Blackboard Value as BoolのvalueをFalseにします。

f:id:kazuhironagai77:20211010213217p:plain

今度はFirst Taskは実行されません。

f:id:kazuhironagai77:20211010213255p:plain

はい。やっとUEのAIにおける

f:id:kazuhironagai77:20211010213308p:plain

を曲りなりにも作成出来ました。

しかし実は、工程を分かり易くするために、やってはいけない事とか、もっと詳しく知らないといけない事とかを全部省いてここまでやりました。

その補正をこれからやっていきます。

5.7 SequenceSelectorについてとTask BPFinish Execute ノードについて

先週、SequenceノードとSelectorノードについての違いを解説しないまま、先に進んでしまいました。

f:id:kazuhironagai77:20211010213327p:plain

のでSequenceノードとSelectorノードについての違いから始めたいと思います。

SequenceノードとSelectorノードについての違いは以下の様に説明されています。

f:id:kazuhironagai77:20211010213346p:plain

このChildのノードが成功とか失敗とはどういう意味なのかを含めてSelectorとSequenceの違いを解説します。

先程作成したBehavior Treeを以下に示したように簡単にします。

f:id:kazuhironagai77:20211010213407p:plain

First Taskの中身も以下の様に変更します。

f:id:kazuhironagai77:20211010213420p:plain

Taskを後3つ程増やします。

First TaskをDuplicateしました。

f:id:kazuhironagai77:20211010213433p:plain

Printする内容を少しだけ変更します。

f:id:kazuhironagai77:20211010213446p:plain

f:id:kazuhironagai77:20211010213451p:plain

f:id:kazuhironagai77:20211010213458p:plain

これを以下の様に接続します。

f:id:kazuhironagai77:20211010213514p:plain

ここで結果を予測します。

f:id:kazuhironagai77:20211010213529p:plain

SequenceはChildrenの内一つが失敗を返すまで左から右へとそれぞれのChildを実行するとあります。

そこから予測すると

First Task、First Task 1、First Task 2、First Task 3とPrintされると予測されます。

実行すると

f:id:kazuhironagai77:20211010213544p:plain

First TaskしかPrintされません。

え。じゃ最初のTaskが失敗を返してるの?

と思うかもしれません。

もしそうならSequenceノードの代わりにSelectorノードを使用したら、先程の予想のようにFirst Task、First Task 1、First Task 2、First Task 3が返されるはずです。

f:id:kazuhironagai77:20211010213558p:plain

なぜなら、SelectorノードははSequenceノードとは逆にChildrenが成功を返すまで次のChildを実行するからです。

以下の実装で試してみます。

f:id:kazuhironagai77:20211010213612p:plain

ところが、今回もFirst Taskしか実行されません。

f:id:kazuhironagai77:20211010213626p:plain

何でしょう?

実は、First Taskは成功(success)も失敗(fail) も返していないんです。そればかりか、First Taskの実行は終了すらしていません。

Finish Execute ノードについて

Taskの実行を終了させるためにはFinish ExecuteノードをEvent Receive Executeの最後につける必要があります。

f:id:kazuhironagai77:20211010213646p:plain

ただし、これはEvent Receive Executeで始まっているからで、

f:id:kazuhironagai77:20211010213701p:plain

他のEventの場合は、別なFinish ノードを使用する必要がありますが、それについては又後で説明します。

First TaskにだけFinish Executeノードを追加してSuccessをチェックします。

更に、Sequenceノードに戻します。

f:id:kazuhironagai77:20211010213715p:plain

これでテストします。

今度も結果を先に予測します。

まずFirst Taskが実行されますので、First TaskがPrintされます。その後Finish Executeノードが実行されSuccessが返されます。SequenceはFailが返るまで次々とChildrenを実行するので次のFirst Task1が実行されます。しかしFirst Task1にはFinish Executeノードが繋がっていないので、そこでずっと実行した状態になります。

のでFirst Task、First Task 1がprintされます。

f:id:kazuhironagai77:20211010213731p:plain

想像した通りの結果になりました。

では今度は、全部のTaskにFinish ExecuteノードをつけてしかもSuccessにチェックを入れたとします。

結果はどうなるでしょうか?

今までの理論から考えれば、First Task、First Task 1、First Task 2、First Task 3とPrintされるはずです。

試してみましょう。

f:id:kazuhironagai77:20211010213747p:plain

何と、First Task、First Task 1、First Task 2、First Task 3を繰り返してPrintしています。

はい。実はRootノードからの実行の指令は、Tick関数のように何度も何度も送られて来ていたんです。

それが今では、Task内でFinish Executeノードを使用していなかったためTask内の実行がいつまでたっても終わっていない状態になっていたんです。

この節のまとめ

SequenceノードはそのChildがFailを返すまで、SelectorノードはそのChildがSuccessを返すまで左から右へと次々にChildを実行していきます。

正しTaskがSuccessやFailをその親ノードに返すためにはFinish Executeノードを使用する必要があります。

Finish Executeノードが使用されていないTaskはずっと実行中の状態になり、そこでAIの作業は停止してしまいます。

5.9 Task BPAbort関数、Execute 関数、そしてTick関数とFinish Abortノードについて

Task BPを開くとReceive ExecuteだけじゃなくてReceive AbortとかReceive Tickとかがあります。

f:id:kazuhironagai77:20211010213810p:plain

これらはReceive Executeとどう違うのでしょうか?

まずTick関数から説明します。

<Event Receive Tickノードについて>

Receive Tickノードの機能について説明するためにBehavior Treeを以下の様に変更します。

f:id:kazuhironagai77:20211010213828p:plain

ここでFirst Task BPの中身を以下の様に変更します。

f:id:kazuhironagai77:20211010213845p:plain

Finish ExecuteノードがないのでこのTaskはずっと実行されます。

Executeノードの時は、一回だけ実行されてその後は何も起きませんでしたが、Tick関数だったらどうでしょうか?

このTaskが実行されている限り、ずっと実行されるはずです。

試してみましょう。

f:id:kazuhironagai77:20211010213900p:plain

予想通りの結果になりました。

今度はEvent Receive Abortノードについてです。

<Event Receive Abortノードについて>

今度はEvent Receive Abortノードについてです。

このノードはこのTaskを実行中に何らかの事情で、このTaskの実行が中止になったら実行されます。

以下の実装をTaskに組みます。

f:id:kazuhironagai77:20211010213916p:plain

Playを押してゲームを開始しても何も表示されません。

f:id:kazuhironagai77:20211010213934p:plain

Escキーを押してゲームを中止します。

するとPrintされました。

f:id:kazuhironagai77:20211010213948p:plain

この機能は後で勉強するDecoratorクラスのObserver abortsに関係してくるのでその時に更に詳しく説明します。

f:id:kazuhironagai77:20211010214002p:plain

5.10 Debugのやり方

UE_AIのDebugのやり方ですが覚えていません。

これが出来ないと次のObserver abortsの検証も出来ないので、一端、勉強し直します。

2021-02-01のブログにDebugのやり方が簡潔にまとめられていました。

もうそのままやればDebug出来ます。

一応、まとめの練習としてここにもDebugのやり方をまとめておきます。

まずBehavior Treeを開いたままPlayボタンを押してゲームを始めます。

都合の良い所でPauseをします。

Behavior Treeに戻ると以下の図のように今どのTaskが実行されているのかが記されています。

f:id:kazuhironagai77:20211010214020p:plain

記されていない時は以下の赤く囲った箇所に調べたいAI Controllerの名前をセットして下さい。

f:id:kazuhironagai77:20211010214036p:plain

そうすれば表示されます。

この状態でBack:IntoかBack:Overをクリックすると

f:id:kazuhironagai77:20211010214049p:plain

一手前にどんなTaskが実行されたとかが一目瞭然になります。

f:id:kazuhironagai77:20211010214101p:plain

更に、10手位、遡って

f:id:kazuhironagai77:20211010214206p:plain

を使用するとどんな手順でTaskが実行されているのかが一手ずつ順番に示されます。

以上です。

Debugにはもう一つ、以下に示した方法があります。

f:id:kazuhironagai77:20211010214222p:plain

どちらかと言えばこっちの方が大切なんですが、今回はNav Mesh Bound Volumeの説明まで行かないのでこのDebugについては応用編を作成する時に説明する事にします。

5.11 Observer Abortの機能について

基礎編の最後にDecorator BPのObserver abortsのNone、Self、Lower Priority、そしてBothについて解説します。

f:id:kazuhironagai77:20211010214242p:plain

公式のDocumentであるBehavior Tree Node Reference: Decorators [13] によると

f:id:kazuhironagai77:20211010214255p:plain

と説明されていますが実際にどういう事なのかを解説します。

2021-02-07のブログに詳しいテストのやり方が説明されていました。

f:id:kazuhironagai77:20211010214311p:plain

ただこのテストと同様の物を作成するとなるとAI ControllerからBlackboard の変数の値をセットする方法を使う必要があります。

ので今週は口だけで説明します。

<None

以下の図でPrintString2が実行されている時に、Blackboard Based Conditionの条件を満たした場合、このBehavior Treeは

f:id:kazuhironagai77:20211010214327p:plain

PrintString3を実行します。そしてPrintString3の実行が終わった後でSelectorに戻り、Blackboard Based Conditionの条件をチェックして満たしている事を確認します。そしてPrintStringを実行します。

f:id:kazuhironagai77:20211010214341p:plain

<Self

以下の図でPrintStringが実行されている時に、Blackboard Based Conditionの条件を満たさなくなった場合、このBehavior Treeは

f:id:kazuhironagai77:20211010214359p:plain

PrintString1の実行をAbortしてSelectorに戻り、Print String 2を実行します。その後、Print String 3を実行します。

f:id:kazuhironagai77:20211010214413p:plain

<Lower Priority

PrintString2が実行されている時に、Blackboard Based Conditionの条件を満たした場合、このBehavior Treeは

f:id:kazuhironagai77:20211010214429p:plain

Print String 3の実行をAbortしてSelectorに戻り、Print Stringを実行します。その後Print String1を実行します。

f:id:kazuhironagai77:20211010214443p:plain

<Both

BothはSelfとLower Priorityの混合です。

以下の例はLower Priorityと同じ行動をしています。

f:id:kazuhironagai77:20211010214458p:plain

f:id:kazuhironagai77:20211010214507p:plain

以下の例ではSelfと同じ挙動を示してます。

f:id:kazuhironagai77:20211010214521p:plain

f:id:kazuhironagai77:20211010214528p:plain

このテスト用のBehavior Treeの作成方法とDebugによるDecoratorの結果が変わった時のBehavior treeの挙動の可視化も来週やります。

6.World CompositionによるMap1の作成

6.1 Actorの追加

今週は先週作成したWorld Compositionを使用したMap1に岩や家などを追加します。

f:id:kazuhironagai77:20211010214556p:plain

本来ならばMap1にあるActorをそのまま使用したいのですが、方法が分かりません。

色々考えましたが、前にImportしたMap1を最初の状態に戻して、World Compositionを使用可能にして新しくLandscapeを作成した方が速い気がします。

しかもこのやり方が上手く行けば、Ch4_3のMap1を修正する時も、新しいMapを作成してその上でMap1を全部作り直すのではなく、元のMap1をほとんどそのまま使用出来ます。

この方法を試してみます。

Sub Levelに移したActorなどを全部Persistent Levelに移します。

f:id:kazuhironagai77:20211010214612p:plain

f:id:kazuhironagai77:20211010214617p:plain

Sub Levelを消します。

上手く出来たみたいです。

f:id:kazuhironagai77:20211010214633p:plain

今度は、Landscapeを全部消します。

f:id:kazuhironagai77:20211010214647p:plain

そしてWorld CompositionにCheckを入れます。

f:id:kazuhironagai77:20211010214700p:plain

消したはずのSub LevelのDataがまだ残っているみたいであーだこーだ言っていますが、無視して先に進みます。

Sub Level、Level1_1を作成します。

先週、Landscapeの作成に使用したDataを見ると以下のようになっています。

f:id:kazuhironagai77:20211010214721p:plain

同じ設定にしてLevel1_1 にLandscapeを作成します。

f:id:kazuhironagai77:20211010214745p:plain

Landscapeの位置を自由に決められません。

仕方ないので最も近い場所に配置しました。

f:id:kazuhironagai77:20211010214804p:plain

先週と同じように36個のSub Levelを追加しました。

f:id:kazuhironagai77:20211010214844p:plain

Map1のHeight mapを追加します。

f:id:kazuhironagai77:20211010214911p:plain

ここでLandscapeを移動させたいのですが何をやっても移動してくれません。

f:id:kazuhironagai77:20211010214927p:plain

仕方ないのでPersistent LevelにあるActorを全選択してLandscape側に移動させました。

f:id:kazuhironagai77:20211010214940p:plain

これでもまだズレていますね。

微調整しました。

f:id:kazuhironagai77:20211010214953p:plain

こんな感じです。

f:id:kazuhironagai77:20211010215007p:plain

移動した時に失敗したのか最初の神殿が二つ出来ていました。

f:id:kazuhironagai77:20211010215023p:plain

これはこれで面白いです。

どれくらいLandscapeから離れるとUnloadされるのかを設定します。

10,000にセットしました。名前はMap1_10Kとしました。

作成したLandscapeを全部選択してAssign LayerからMap1_10Kを選択します。

f:id:kazuhironagai77:20211010215044p:plain

f:id:kazuhironagai77:20211010215053p:plain

テストします。

f:id:kazuhironagai77:20211010215122p:plain

左奥に山が現れました。出来ています。

f:id:kazuhironagai77:20211010215139p:plain

何なんでしょか?このLandscapeの残りは?

f:id:kazuhironagai77:20211010215201p:plain

前に作成したLandscapeのDataが残っているでしょうか?

分からないのでほって置きます。

本番ではこんなへんなモノは現れない事を祈ります。

LandscapeにPaintします。

Landscapeを選択してLandscape MaterialにMap1_Instをセットします。

f:id:kazuhironagai77:20211010215237p:plain

Landscape Streaming Proxyにも同様にMap1_Instをセットします。

f:id:kazuhironagai77:20211010215252p:plain

こんな感じです。

f:id:kazuhironagai77:20211010215306p:plain

PaintのTarget Layersを以下のようにセットします。

f:id:kazuhironagai77:20211010215322p:plain

以下の様な感じでPaintしました。

f:id:kazuhironagai77:20211010215336p:plain

こんな感じです。

f:id:kazuhironagai77:20211010215351p:plain

後、Mapに残っていた前のSub Levelのdataを消しました。

f:id:kazuhironagai77:20211010215412p:plain

するとPlay時に現れる変なLandscapeも消滅しました。

はい。

これで準備が出来ました。

ここからActorをそれぞれのSub Levelに紐づけします。

以下の様にActorが乗っているLandscapeに紐づけしました。

f:id:kazuhironagai77:20211010215425p:plain

36枚のSub Level全てやりました。

テストします。

f:id:kazuhironagai77:20211010215441p:plain

何故か夜に?

Good Skyが5つに増えていました。

f:id:kazuhironagai77:20211010215457p:plain

消します。

もう一回テストします。

今度は昼になったり夜になったりしました。

f:id:kazuhironagai77:20211010215510p:plain

空を見上げるだけで山が消えたり現れたりします。

かなり変です。

f:id:kazuhironagai77:20211010215523p:plain

でも一応は出来ました。

6.2 Map1のLandscapeをWorld Compositionに変更する事について

Sub Level内のLandscapeがある一定の場所にしか配置出来ないのが難点です。今回は全部のActorの位置を移動させましたが、そんな事をすれば、色々な新しい問題が発生してその直しにもっと時間がかかってしまいます。

Map1のLandscapeは4x9だから中心にLandscapeがあるとずれます。5x9で新しいLandscapeを作ってしまうのはどうでしょうか?

f:id:kazuhironagai77:20211010215544p:plain

Map1のHeight Mapの高さは253pixelです。4で割ると63.25なので326か327pixelの大きさに改良した新しいHeight Mapを作成してそれをImportするのはどうでしょうか?

試しにMap1のHeight Mapを5x9のLandscapeに広げて見ました。

f:id:kazuhironagai77:20211010215557p:plain

出来る事は出来るみたいです。

Ch4_3のLandscapeの位置を確認してみます。

f:id:kazuhironagai77:20211010215610p:plain

これは上から2段目の5番目のComponentが真ん中にくるようになっていますね。

そういうつもりで最初からLandscapeを作成したら動かす必要も無かったかもしれませんね。

兎に角、World CompositionはUE4までにしかない機能なのでそんなに頑張って勉強する価値は無いので適当に済ましておきます。

6.3 Unreal Engine 5 | Open World Tutorial Using World Partition [14] の勉強

よくよく考えたらNiagaraのような小さいEffectならともかく、Landscapeのような大きなものが今の私のPCでUE5が完全に動く訳ないので見るだけにします。

こんな風にUE5だと変わるのかと言う事を理解すればOKにします。

軽く一回だけ見ましたが、これなら私のPCでも出来そうなので試してみます。

まず一番最初にしなければならない事は、World Partitionを使用可能にする事です。

Project SettingからWorld PartitionのEnable World Partitionにチェックを入れます。

f:id:kazuhironagai77:20211010215636p:plain

TutorialではWorld SettingにあるWorld Partition SetupのEnable World PartitionからはCheckが出来ないと言っていましたが、それも試してみました。

f:id:kazuhironagai77:20211010215649p:plain

出来ませんでした。

ProjectをRestartしました。

新しいLevelをTime Of Dayから作成しました。

f:id:kazuhironagai77:20211010215703p:plain

この名前から推測するにこのTemplateだと太陽の動きも自然に表してくれてるみたいです。

良く分からないのですがこのLevelを一回Re-Openしろと言っています。

しました。

これでWorld Partitionが使用出来る状態になったそうです。

確かにWorld SettingのWorld Partition Setupを見ると以下の様に変化していました。

f:id:kazuhironagai77:20211010215718p:plain

うーん。結構、面倒くさいですね。

Landscapeを作成します。

どうせなんで、Map1と同じ大きさにしました。

f:id:kazuhironagai77:20211010215733p:plain

Map1のLandscapeのDataです。

f:id:kazuhironagai77:20211010215747p:plain

こんな感じのLandscapeが出来ました。

f:id:kazuhironagai77:20211010215801p:plain

次にWorld Partition SetupのWorld PartitionをクリックしまくってGridsまで開きます。

f:id:kazuhironagai77:20211010215814p:plain

Cell SizeはUE4で言うComponentもしくはSectionに当たるのでしょうか?

Loading RangeはWorld Compositionでいう所のStreaming Distance

f:id:kazuhironagai77:20211010215826p:plain

に当たる部分でしょうか?

後は分かりませんね。

Preview Gridにチェックを入れます。

f:id:kazuhironagai77:20211010215839p:plain

すると以下のようにGridsが表示されます。

f:id:kazuhironagai77:20211010215852p:plain

ここでTutorialはStreamingが効いている事を示すためにFoliageを使用して木をPaintしました。

f:id:kazuhironagai77:20211010215906p:plain

代わりにLandscapeをデコボコにしました。多分これでも十分でしょう。

f:id:kazuhironagai77:20211010215919p:plain

テストします。

f:id:kazuhironagai77:20211010215941p:plain

奥に山が生成されています。

f:id:kazuhironagai77:20211010215954p:plain

Commandでws.Runtime.ToggleDrawRuntimeHash3Dと打ちます。すると以下に示したようなGridが表示されます。

f:id:kazuhironagai77:20211010220008p:plain

更に高い所でFlyと打ち込むとその高さから落ちないで移動出来ます。

でもこれ良く見ると円柱のRangeからはみ出ているLandscape消えてないですね。

f:id:kazuhironagai77:20211010220024p:plain

真ん中にある球が消えました。

f:id:kazuhironagai77:20211010220036p:plain

これはLandscapeは関係なくてActorとかFoliageとかにだけ影響するのかもしれませんね。

公式のDocumentであるWorld Partition [15] に簡潔な解説がありました。

こっちを先にやった方が良かったかもしれません。

今週はここまでにして来週はDocumentであるWorld Partition [15]を先に読みます。

7.Loading Screenの勉強の続き(Moduleについて)

時間が無くなってしまったので来週やります。

8.まとめと感想

時間が無くなってしまったので今週はなしにします。

9.参照(Reference

[1] CGHOW. (2021, August 22). Bugs in UE4.27 Niagara Tutorial | Download Files [Video]. YouTube. https://www.youtube.com/watch?v=Jfxh_hqcZKY

[2] Epic Games [Unreal Engine]. (2020, May 27). Building advanced effects in Niagara | Unreal Engine [Video]. YouTube. https://www.youtube.com/watch?v=syVSRDQxrZU

[3] CGHOW. (2021b, September 20). Disintegration in UE5 Niagara Tutorial | Download Files [Video]. YouTube. https://www.youtube.com/watch?v=4dYg4bvf4Rc

[4] CGHOW. (2021c, September 21). Disintegration Skeletal Mesh FX in U5 Niagara Tutorial | Download Files [Video]. YouTube. https://www.youtube.com/watch?v=72TSg5Tv5Ik

[5] Epic Games [Unreal Engine]. (2020b, May 27). Building advanced effects in Niagara | Unreal Engine [Video]. YouTube. https://www.youtube.com/watch?v=syVSRDQxrZU

[6] IGN. (2021, August 20). Black Myth: Wukong - Official Unreal Engine 5 Gameplay Trailer [Video]. YouTube. https://www.youtube.com/watch?v=nOMIwsupy9k

[7] Cloward, B. (n.d.). Shader Graph Basics. YouTube. Retrieved October 10, 2021, from https://www.youtube.com/playlist?list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl

[8] Ben Cloward. (2021, August 5). Input Vectors - Shader Graph Basics - Episode 9 [Video]. YouTube. https://www.youtube.com/watch?v=lrc-j7ub28U&list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl&index=9

[9] Epic Games. (n.d.-b). Coordinates Expressions. Unreal Engine Documentation. Retrieved October 10, 2021, from https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/ExpressionReference/Coordinates/#vertexnormalws

[10] VRIES, J. (n.d.). LearnOpenGL - Deferred Shading. Learn OpenGL. Retrieved October 10, 2021, from https://learnopengl.com/Advanced-Lighting/Deferred-Shading

[11] Unity3d.com. (n.d.). Unity - Manual: Transforms. Unity-Manual. Retrieved October 10, 2021, from https://docs.unity3d.com/550/Documentation/Manual/Transforms.html

[12] Cloward, B. [Ben Cloward]. (2021, August 12). View, World, Object, & Tangent Space - Shader Graph Basics - Episode 10 [Video]. YouTube. https://www.youtube.com/watch?v=E6Srr-HaicI&list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl&index=10

[13] Epic Games. (n.d.-a). Behavior Tree Node Reference: Decorators. Unreal Engine Documentation. Retrieved October 10, 2021, from https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/BehaviorTreeNodeReference/BehaviorTreeNodeReferenceDecorators/

[14] Smart Poly. (2021, June 3). Unreal Engine 5 | Open World Tutorial Using World Partition [Video]. YouTube. https://www.youtube.com/watch?v=efN4bGbzr78

[15] Epic Games. (n.d.-c). World Partition. Unreal Engine Documentation. Retrieved October 8, 2021, from https://docs.unrealengine.com/5.0/en-US/WorldFeatures/WorldPartition/