UE4の勉強記録

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

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

f:id:kazuhironagai77:20211003211718p:plain

<前文>

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

今週は法の下の平等についてです。

日本でも最近、上級国民なる言葉が生まれて、一般人とは法律の扱いが違う人達がいると言われるようになりました。

私はアメリカに10年住んでいましたが、アメリカで上級国民に該当する言葉やその概念に付随する議論を全く聞いたことが無かったです。だからアメリカにはそういう人達は存在しないとずっと思っていました。

ところがですね。最近になって気が付いたんですが、アメリカで上級国民に該当する言葉やその概念に付随する議論を全く聞いたことがないのは、アメリカでは法律が公平かつ厳密に施行されている。からではなくて、上級国民なる階層が存在している事がアメリカ人にとっては当たり前すぎて議論するに値しない内容だからだったんです。

アメリカ人と言うか、西洋人全般が上流階級と言う場合は、その言葉に日本人の言う「法律の扱いが一般人とは違う人達」と言うニュアンスが既に入っていたんです。

それで、今から思い出してみると結構、アメリカ人でも上流階級や支配階級と言う言葉を日本人の言う上級国民のニュアンスと同じ意味で使っていたなー。と思いました。

2,3例を上げます。

これはアメリカ人の友人から直接言われた話です。「ヨーロッパの上流階級は生まれで決まっているが、アメリカは自分の才覚と努力で成り上がれる可能性があるから、アメリカの方がかなり自由である。しかもアメリカでは、上流階級はお金を持っているかどうかだけが基準で、そのお金をどうやって稼いだととか、親の血筋や職業は一切関係ない。」と。最後に彼女はこう忠告してくれました。「美男美女には特にNiceにしておくように。いつ、そういう上流階級と結婚するか分からないから。」

確かに、上級国民であるために、親の血筋が関係ないなら誰でも美男美女と結婚しますよね。

こんな話もありました。

後、化学の教授から言われたのが「アメリカでは20ドルを強盗すると5年も6年も刑務所に入らなくてはいけなくなるけど、何百億円も盗んだ奴は刑務所に入らなくてもいい。」です。

これは昔あった年金詐欺について言っていたんだと思いますが、大学教授でかつ専門分野では世界的な権威でもある人が、そんな話を真剣に語っているのを聞いた時は凄い違和感がありました。なんと言うか、日本で言えば、東大の教授が「上級国民が...。」と叫んでいたら何か変な気がしますよね。そんな感じでした。

これらの話は、今になって思えば、まさしく上級国民的な概念についての話をしていたんですが、当時の私はそんな概念を認識していなくてふーん。と聞き流していました。

後、Robert Kiyosaki氏の金持ち父さん、貧乏父さんシリーズで、散々、Fast trackに乗らないとビジネスチャンスは来ない。どうやってFast trackに乗るかが論じられていますが、これを私はずっと不動産投資をするなら元手に3千万円から5千万円が最低でも必要みたいな意味で解釈していたんですが、実際は上級国民のグループに入らないとビジネスは成功しないと言う意味だったようです。

日本は腐敗しているけどアメリカは違うと思っていたら、アメリカは日本以上の上級国民社会だったなんて結構ショックですよね。

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

<本文>

1.今週の予定

今週も以下の事を

  • Niagara : 先週の復習と何か
  • Material : 先週の続き
  • Map1の作成の続き
  • Loading Screenの勉強の続き
  • Good Skyの復習

をやっていきます。

2.Niagara : 先週の復習と何かをやる

2.1 Vector 2D From FloatとPhysics Drag変数の調査

Vector 2D from Float

これはDynamic Inputの一つでHanging Particulates Template内で使用されています。

f:id:kazuhironagai77:20211003211813p:plain

ここで使用されているVector 2D from Floatの意味が分からないんです。

f:id:kazuhironagai77:20211003211831p:plain

まずScale Factor変数ですが、floatタイプではなくVector 2D Floatでした。

f:id:kazuhironagai77:20211003211918p:plain

Vector 2D from FloatのScriptを開くと以下の様になっています。

f:id:kazuhironagai77:20211003211932p:plain

 

最初のMap Getノードに追加されたInput ValueのタイプはFloatです。これはUserがInputする値で、決してDynamic Inputがセットされた変数にパスする値ではありません。

先週はこれを勘違いしていました。

このMap Getノードに追加されたInput ValueのタイプがFloatだったので、Scale Factor変数がfloat型であると勘違いしてこのDynamic Input Scriptを読んでいました。

それで意味が分からなくなってしまったんです。

もう一度Vector 2D From Float のDynamic Input Scriptを読み直すと、UserがInputしたFloatの値を2つに分割してVector 2D Floatにそれぞれセットしています。

これがVector 2D from Floatの機能でした。

<Transient Physics Drag変数

まずName SpaceでTransientの範囲が良く分かっていません。TransientとLocalの違いはLocalのScopeはそのModule内、TransientのScopeはそのSectionの間と理解していますが、この解釈が正しいかどうかは分かりません。

この解釈の根拠になっているのが

f:id:kazuhironagai77:20211003211952p:plain

この解説文です。Localの説明と比較すると

f:id:kazuhironagai77:20211003212012p:plain

LocalではSingle Module内のみ書いたり読んだり出来ますが、Transientは全てのModule内で読んだり書いたり出来るとあります。しかしStageを跨いで値を維持する事は出来ないとあります。

ここで言うStageはSectionと同じ意味のはずです。

だから最初の「LocalのScopeはそのModule内、TransientのScopeはそのSectionの間」という解釈が出て来ます。

これを確認したいんです。

どうすれば確認出来るでしょうか?

こんなテストを考えました。

f:id:kazuhironagai77:20211003212136p:plain

Particle Update SectionにTest Transient ModuleをScratch Pad Moduleから作成します。

中身はこんな感じです。

f:id:kazuhironagai77:20211003212149p:plain

ので

f:id:kazuhironagai77:20211003212205p:plain

InputであるTest Transient Inputに値をセットするとTransientであるTest Transientにその値がセットされます。

そしてGravity Force ModuleでTest Transient Module内に作成したTransientであるTest Transientをセットします。

f:id:kazuhironagai77:20211003212220p:plain

これでTransientがModule外でも同じsectionなら値を保持しているならGravity Force ModuleのGravityのz値に‐980がセットされたはずです。

結果です。

Spriteに重力がかかり下に落ち始めました。

f:id:kazuhironagai77:20211003212234p:plain

Test Transient Module のTest Transient Inputの値を半分の‐460にします。

f:id:kazuhironagai77:20211003212247p:plain

Spriteの落ちる速度が遅くなりました。

TransientであるTest Transientの値によってGravity Force ModuleのGravityの値が変わっている事が確認出来ました。

同様のテストをLocalで試してみようとしたのですが、ModuleのInput値を指定する所のLink Inputの項にLocalは表示されませんでした。

f:id:kazuhironagai77:20211003212300p:plain

つまりLocal変数はModuleのInput値を指定する事は出来ない訳です。これだけでもLocalとTransientの違いを証明するには十分でしたね。

因みに

f:id:kazuhironagai77:20211003212313p:plain

Drag Moduleの後に別のScratch Pad Moduleを配置してどんなTransientがその変数の値を指定出来るかを調べると

f:id:kazuhironagai77:20211003212333p:plain

Drag Moduleで作成されたTransientであるPhysics DragやPhysics Rotational Dragの値が使用出来る様になっています。

<Transient Physics Drag変数まとめ

ここに書かれた内容だけだとTransient のテストは非常にスムーズに出来ているように見えますが、実際は結構、躓きました。

特に大変だったのはTransientの変数はMap GetからAccessする方法が分からなかった事で

f:id:kazuhironagai77:20211003212351p:plain

何をやっても前のModuleのTransient変数をMap Getから呼ぶ事が出来ないんです。

これがParticles ならば以下の様に別なModuleで作成したParticles の変数は

f:id:kazuhironagai77:20211003212422p:plain

Map Getに直接は表示されなくても

f:id:kazuhironagai77:20211003212435p:plain

ParametersのParticle Attributesの方に表示され

f:id:kazuhironagai77:20211003212449p:plain

Drag and Drop

f:id:kazuhironagai77:20211003212508p:plain

Map Getノードの要素に追加する事が出来ます。

ところが、Transientの場合は、

f:id:kazuhironagai77:20211003212532p:plain

Map Get ノード内にdynamic Inputで新しい変数を作成して、その変数にLink Inputを用いてTransientを繋げるしか方法がありません。

f:id:kazuhironagai77:20211003212546p:plain

有りませんと言うか、これしか方法が見つからなかったです。

でもSolve Force And VelocityノードのScriptのMap Getノードを見るとTransientのPhysics Dragを要素として使用しているんです。

f:id:kazuhironagai77:20211003212618p:plain

やり方が分からないだけでTransientである変数の値を直接呼び出す事は出来そうです。

2.1 Bugs in UE4.27 Niagara Tutorial | Download Files [1] を勉強する

今週はCGHOW氏のBugs in UE4.27 Niagara Tutorial | Download Files [1] を勉強します。

ざっと全部見ましたが、独自に作成したMeshを使用していないし、MaterialもこのEffectを完成させるのに重要な要素ではなさそうなので今週はこれを勉強する事にしました。

こんな感じのEffectです。

f:id:kazuhironagai77:20211003212638p:plain

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

f:id:kazuhironagai77:20211003212652p:plain

要らないModuleを全て消しました。

f:id:kazuhironagai77:20211003212706p:plain

Particle Spawn SectionにTorus Locationを追加します。

f:id:kazuhironagai77:20211003212719p:plain

こんな感じです。

f:id:kazuhironagai77:20211003212731p:plain

ここにTorus LocationのHandle Radius変数の値を0にセットします。

f:id:kazuhironagai77:20211003212744p:plain

すると

f:id:kazuhironagai77:20211003212757p:plain

ドーナツが楕円に変化します。

次にParticle Spawn SectionにAdd Velocity From Point Moduleを追加します。

f:id:kazuhironagai77:20211003212809p:plain

このModuleは初めて使用しますね。後でこのModuleの機能や特徴をしっかり勉強します。

追加した途端、Spriteの動きが以下の様に変化しました。

f:id:kazuhironagai77:20211003212822p:plain

Particle の数を1000に増やしました。

f:id:kazuhironagai77:20211003213205p:plain

このParticleの動きは単に外に広がっているだけなので、虫の動きには見えません。そこでCurl Noise Force Moduleを追加します。

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

f:id:kazuhironagai77:20211003213220p:plain

Curl NoiseのNoise StrengthとNoise Frequencyを以下の値に変更します。

f:id:kazuhironagai77:20211003213242p:plain

Spriteはかなり乱雑な動きに変化しました。

f:id:kazuhironagai77:20211003213258p:plain

しかし同時にSpriteはz軸上に上下するようになってしまいました。

f:id:kazuhironagai77:20211003213313p:plain

これをどうやって直すかが今回のTutorial前半の山場です。

z方向にDragすれば良いです。しかしDrag ModuleのDragには方向を指定する機能は有りません。

f:id:kazuhironagai77:20211003213327p:plain

それではどうしますか?

Velocity変数にMaskをします。

まずParticle のVelocity変数をParticle Update SectionのCurl Noise Force Moduleの下にセットします。

f:id:kazuhironagai77:20211003213343p:plain

そしてVelocity変数の値を以下のようにセットします。

f:id:kazuhironagai77:20211003213413p:plain

成程。

Velocity = Velocity*(1,1,0)

と同じ事を実装した訳ですね。

こんな感じで平らになりました。

f:id:kazuhironagai77:20211003213451p:plain

微調整して以下のようになりました。

f:id:kazuhironagai77:20211003213518p:plain

今度はこのSpriteにRibbonを追加します。

EmitterをDuplicateします。

f:id:kazuhironagai77:20211003213532p:plain

要らないModuleを全て消します。

f:id:kazuhironagai77:20211003213546p:plain

Emitter Update SectionにSpawn Particle From Other Emitter Moduleを追加します。

f:id:kazuhironagai77:20211003213629p:plain

Errorが表示されるのでFixを押します。

するとParticle Spawn SectionにSample Particles from Other Emitter Moduleが追加されます。

f:id:kazuhironagai77:20211003213648p:plain

次にEmitter Update SectionのSpawn Particle From Other Emitter Module Emitter NameにLeadをセットします。

f:id:kazuhironagai77:20211003213706p:plain

東京の路線図みたいなイメージに成りました。

f:id:kazuhironagai77:20211003213720p:plain

今度はSpriteを外してRibbonをRenderingします。

f:id:kazuhironagai77:20211003213733p:plain

Particle Spawn SectionにInitialize Ribbon Moduleを追加します。

f:id:kazuhironagai77:20211003213747p:plain

そしてParticle Spawn SectionのInitialize Particleから

f:id:kazuhironagai77:20211003213801p:plain

Spriteのサイズを0にします。

f:id:kazuhironagai77:20211003213825p:plain

更にParticle Spawn SectionのSample Particle from Other Emitter Moduleの

f:id:kazuhironagai77:20211003213838p:plain

Particle ID Sampling にApply Sampled ID as Ribbon IDをセットします。

f:id:kazuhironagai77:20211003213852p:plain

更にLead EmitterのEmitter Properties Moduleの

f:id:kazuhironagai77:20211003213906p:plain

Required Persistent IDsにチェックを入れます。

f:id:kazuhironagai77:20211003213919p:plain

Emitter Update ModuleのSpawn Particles from Other Emitter Moduleの

f:id:kazuhironagai77:20211003213933p:plain

Spawn RateをLeadのSpawn Rate ModuleのSpawn Rateと同じ値にします。

f:id:kazuhironagai77:20211003214010p:plain

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

f:id:kazuhironagai77:20211003214030p:plain

取りあえずは元々あるRibbon用のMaterialを使用しておきます。これで駄目なら色々考えます。

Initialize Ribbon ModuleをSample Particle from Other Emitter Moduleの上に移動しました。

f:id:kazuhironagai77:20211003214044p:plain

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

f:id:kazuhironagai77:20211003214100p:plain

ちょっとは虫みたく成りました。

今度はRibbon のサイズを時間によって変化させます。

Particle Update SectionにScale Ribbon Width Moduleを追加します。

f:id:kazuhironagai77:20211003214113p:plain

値は以下の様にセットしました。

f:id:kazuhironagai77:20211003214126p:plain

こんな感じです。

f:id:kazuhironagai77:20211003214144p:plain

最後にRibbonを丸くします。

Ribbon RendererのShapeにTubeをセットします。

f:id:kazuhironagai77:20211003214157p:plain

どちらかと言えば三角かもしれませんが、丸くなっていると言えばなってますね。

f:id:kazuhironagai77:20211003214210p:plain

Tube Subdivisionsの値を10にしました。

f:id:kazuhironagai77:20211003214224p:plain

今度はまん丸です。

f:id:kazuhironagai77:20211003214238p:plain

微調整をしたらこんな感じになりました。

f:id:kazuhironagai77:20211003214316g:plain

かなり虫っぽいです。

このTutorialで使用したModuleなどの細かい機能は来週調べます。

後、Curl Noiseを平面化する方法も判明したので、Noise StrengthやNoise Frequencyの機能についても調べていきます。

f:id:kazuhironagai77:20211003214358p:plain

3.Material : 先週の続き

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

3.1 Linear Interpolation Node (Lerp) - Shader Graph Basics - Episode 6 [3]を勉強する

<MIP Mapについて>

最初にMIP Mapについての説明がありました。先週のTextureの勉強で抜けていた内容を補うためだそうです。本当に良いTutorialです。

MIP Mapについて知っておくべき事で私が知らなかった事は、

  • TextureImportした時に自動的に作成される。
  • Pixel Shimmering Pixel Sizzling を自動的に防ぐとあるが、具体的にそれらが何を指しているのか知らなかったです。

<Textureを追加する方法>

Texture SampleノードにTextureをセットする事とTexture SampleノードのRGBをResult ノードのBase Colorに繋ぐ事を説明しています。

f:id:kazuhironagai77:20211003214441p:plain

話は逸れますがUE5のDetail欄は格好いいですね。

f:id:kazuhironagai77:20211003214454p:plain

UnityにおけるTextureのセットの仕方も説明されています。

f:id:kazuhironagai77:20211003214545p:plain

先週、一週間考えましたがUnityの勉強は止める事にしました。今の所、Unrealで困った事がないからです。

<Lerpについて>

はい。やっと今回のTutorialのメインであるLinaer Interpolation ノードについてです。

流石に私が幾らMaterialについて疎くてもLerpの使い方については知っています。今回は復習の感じで軽く見て行きます。

まず発音ですが、リープでなくラープって言っています。ʌのアの音です。結構Linearにつられてリープって言ってしまいそうですね。

次に実際のLinear Interpolationの計算をノードを使用して作成して説明していました。

f:id:kazuhironagai77:20211003214607p:plain

ここまで教えるのか。凄いです。

Linear Interpolationの計算位知ってるわ。と思っていましたが最後の計算、足すのか掛けるのか少し迷いました。

このTutorialで復習して良かったです。

後、混合率の説明で0や1の範囲からはみ出した場合について解説していましたが、私の考え方は2021-06-20のBlog内で勉強したBezier Curves and Splines [4]の解説と同じで、Interpolateと言っている以上0と1の範囲外の値を取るべきではないです。

<<Lerpを使用してMaskをする方法>>

以下に示したように、混合率を指定するalphaのPinにグレイスケールのTextureを使用すれば

f:id:kazuhironagai77:20211003214645p:plain

Textureの絵柄に沿ってAとBを混ぜた色を表示出来る様になります。

f:id:kazuhironagai77:20211003214701p:plain

これ、言われてみれば当たり前ですが、いきなりこの手法を思い付くかと言えば、結構難しいと思います。

先週、宿屋で寝ている時に表示されるBGIの絵にPerlin Noiseを追加しましたが、本当は雲の部分に赤とか緑を追加して星雲を表現したかったんです。

f:id:kazuhironagai77:20211003214722p:plain

でもやり方が分からなくて白いままで雲と言う事にしました。

これもLerpを使用すれば雲の白い部分に色々な色を追加する事が出来ました。

試しにPerlin Noiseを混合値に使用して星雲を作成してみました。

f:id:kazuhironagai77:20211003214742p:plain

f:id:kazuhironagai77:20211003214749p:plain

イイ感じじゃないでしょうか?

<Desaturation ノード>

Desaturationノードの使用方法と言うより、Desaturation ノードを利用してSample に使用しているTextureの彩度を1以上に上げる、ちょっとハッキング的な技術の紹介でした。

まず、Desaturationノードの機能からです。

公式のDocumentであるColor Expressions [5] ではDesaturationは以下のような説明がされています。

f:id:kazuhironagai77:20211003214808p:plain

つまりColor絵をGray スケールに変更している訳です。

先程の星雲にDesaturationを掛けてみました。

f:id:kazuhironagai77:20211003214822p:plain

こんな感じです。

f:id:kazuhironagai77:20211003214835p:plain

このTutorialではLerpとこのDesaturationを利用して彩度を限界以上に上げる方法を紹介しています。

こんな感じで元の絵とDesaturationした絵をLerpします。しかし混合率を-1にしています。

f:id:kazuhironagai77:20211003214849p:plain

その結果、以下のようなImageになりました。

f:id:kazuhironagai77:20211003214910p:plain

Interpolate用の計算式でExtrapolateしている訳で、厳密に言えばハッキング的な技法でしょう。

でも絵は鮮やかですね。

流体のSimulationとかだったら許されないと思いますが、ゲームならこのような技法も覚えておく必要があるんでしょうね。

3.2 Dot Product Node - Shader Graph Basics - Episode 7 [6] を勉強する

Dot Productは以下の計算が行われています。

f:id:kazuhironagai77:20211003214950p:plain

この計算自体は難しくも何ともないですが、このDot Productを計算すると何が得られるのかが良く分かりません。

Tutorialでは以下の例で解説しています。

f:id:kazuhironagai77:20211003215004p:plain

この例だとカメラに向かっているVectorは白くなります。カメラから垂直方向に向かうVectorは黒くなります。

f:id:kazuhironagai77:20211003215024p:plain

このように2つのVectorの関係性をGray Scaleで表す事が出来るのがDot Productです。

この説明で私は完璧に理解出来ますが、初めてMaterialを勉強するDesignerはどうなんでしょうか?

まず法線(Normal)って知っているでしょうか?

やっぱりDot Productを扱えるのは大学レベルの数学を修めて初めて扱えるようになると思います。

MaterialをDesignerが扱うのは無理でしょう。

<Dot Productの応用1:TextureをGray Scaleに変換する>

このTextureが

f:id:kazuhironagai77:20211003215043p:plain

こんな感じになります。

f:id:kazuhironagai77:20211003215057p:plain

実装部を示します。

f:id:kazuhironagai77:20211003215114p:plain

機能としてはDesaturationノードと同じですね。

ここで使用している0.213、0.715、そして0.0722はそれぞれ赤、緑、青を感じる錐体細胞の数の割合だそうです。この数字の割合でRGBをGray Scaleに変換すると自然なGray Scaleに変換されるようです。

<Dot ノードを使用したMaskについて>

Dot ノードを使用してMaskを作成する事も出来るそうです。

まずこういうTextureを作成します。

f:id:kazuhironagai77:20211003215132p:plain

赤色だけを表示すると

f:id:kazuhironagai77:20211003215144p:plain

f:id:kazuhironagai77:20211003215149p:plain

緑色だけを表示すると

f:id:kazuhironagai77:20211003215201p:plain

f:id:kazuhironagai77:20211003215206p:plain

青色だけを表示すると

f:id:kazuhironagai77:20211003215219p:plain

f:id:kazuhironagai77:20211003215224p:plain

となります。このTextureとDot ノードを使用してMaskを実装します。

赤と先程のTextureをDot ノードに繋ぎます。

f:id:kazuhironagai77:20211003215238p:plain

すると

f:id:kazuhironagai77:20211003215253p:plain

が現れます。

赤を、緑や青にすると

f:id:kazuhironagai77:20211003215314p:plain

f:id:kazuhironagai77:20211003215319p:plain

f:id:kazuhironagai77:20211003215325p:plain

f:id:kazuhironagai77:20211003215331p:plain

2や3が現れます。

うーん。成程としか言いようがありませんね。

ただどんな局面で使用出来るんでしょうか?

<Dotノードの最初のサンプルの解説>

Dotノードの最初のサンプルの解説をしています。

f:id:kazuhironagai77:20211003215354p:plain

はっきり言って滅茶苦茶、凄いです。

以下に示した様に、さっき私は大学レベルの数学を知らないとDot ノードは理解出来ないと結論づけましたが、この説明なら全くベクトルを知らない人でも理解出来る気がします。

f:id:kazuhironagai77:20211003215425p:plain

伊達に低評価0じゃなかったです。

f:id:kazuhironagai77:20211003215439p:plain

本来の解説はこのTutorialを見てもらうとして何がこの解説の凄い所かと言うと、以下に示した様に白い板、光源、光源に向かっている緑の矢印、そして白い板に対して垂直な赤の矢印から、Dot Productを光源に向かっている緑の矢印と白い板に対して垂直な赤い矢印の関係から、白い板の色を表す式であると解説しています。

f:id:kazuhironagai77:20211003215546p:plain

これ、一発で物理や数学の素人でも直観的にDot Productを理解出来ます。

例えば光源が白い板の真上まで動いたとします。太陽が光源だとするとお昼になった時です。

f:id:kazuhironagai77:20211003215559p:plain

白い板は真っ白になります。

今度は光源が白い板の真横まで堕ちたとします。太陽が光源だとすると日が沈む時です。

f:id:kazuhironagai77:20211003215613p:plain

白い板は真っ黒になります。太陽の光を一切受けないのだから当然です。

このDot Productを、光源に向かっている矢印と白い板に対して垂直な矢印の関係から、白い板の色を表す式だと言う説明は天才的です。

これを理解した後なら、球の場合、垂直な矢印が以下の様になる事さえ分かれば、

f:id:kazuhironagai77:20211003215627p:plain

(球の法線のImageはhttps://i.stack.imgur.com/RbDO4.png [7]より引用しました。)

球の真ん中に光を当てた場合、真ん中が常に白く端が常に黒くなる事を理解するのは難しくありません。

f:id:kazuhironagai77:20211003215655p:plain

こんな感じです。

更に抽象的に考えれば、緑の矢印は光源に対してじゃなくても良いはずです。

先程の最初のDot Productの例のようにカメラに対して緑の矢印を作成しても良い訳です。

f:id:kazuhironagai77:20211003215710p:plain

この説明ならDesignerが全くの数学の素人でもDot Productが何なのかを理解出来ます。と言うか理系の人でもDot Productをこういう理解の仕方を出来ている人って少ないんじゃないでしょうか?

更にこの後で、Fresnel反射がこのDot Productの値を1-xしただけとかの解説もしていますが、今回はこれを知っただけで十分です。

私がここで「すげええ!」と叫んでいても、何も変わらないですが、一応「いいねボタン」だけは押しておきました。

f:id:kazuhironagai77:20211003215724p:plain

3.3 Texture Coordinates - Shader Graph Basics - Episode 8 [8] を勉強する

軽く全部見ましたがこのTutorialもかなり分かり易かったです。

再現出来る箇所は自分でやってみて、出来ない部分はTutorialの画像を使ってまとめます。

UV Coordinateを説明するのにみかんの皮を使用しています。

f:id:kazuhironagai77:20211003215744p:plain

f:id:kazuhironagai77:20211003215751p:plain

3Dが2Dに展開されているのが良く分かります。

このみかんの皮の部分がMeshの表面に当たる訳です。

今度はTexCoord[0]と四則計算の関係を説明します。

f:id:kazuhironagai77:20211003215804p:plain

TexCoord[0]を繋いだだけでは何の変化もしません。

f:id:kazuhironagai77:20211003215817p:plain

TexCoord[0]に掛け算をします。

f:id:kazuhironagai77:20211003215831p:plain

f:id:kazuhironagai77:20211003215836p:plain

Textureが4つ分で一つになりました。

今度は0.5を掛けます。

f:id:kazuhironagai77:20211003215857p:plain

f:id:kazuhironagai77:20211003215905p:plain

サイズが4分の1になりました。

今度は0.5を足してみます。

f:id:kazuhironagai77:20211003215919p:plain

f:id:kazuhironagai77:20211003215926p:plain

0.5ずつ、x軸とy軸両方にずれました。

今度はx軸にだけ0.5ずらしました。

f:id:kazuhironagai77:20211003215940p:plain

f:id:kazuhironagai77:20211003215953p:plain

後Timeなどを使用してAnimationを表示する方法も示してます。

このTexCoord[0]の解説ですが、私自身が発明した凄い分かり易い説明があります。

Textureに掛け算が作用するのではなくCoordinate(座標)の方に作用すると考えます。こうすると全て説明がつき、何も覚える必要がなくなります。

それもここに記録しておきます。

TexCoord[0]とTextureの関係ですが、何もしない場合は以下の様になっています。

f:id:kazuhironagai77:20211003220017p:plain

f:id:kazuhironagai77:20211003220023p:plain

ここからTexCood[0]に

f:id:kazuhironagai77:20211003220044p:plain

2を掛けます。

するとCoordinateのサイズが2倍になります。

f:id:kazuhironagai77:20211003220057p:plain

ただし、TextureはWrapにセットされているので

f:id:kazuhironagai77:20211003220110p:plain

Textureが無い部分は同じTextureが繰り返されます。

f:id:kazuhironagai77:20211003220123p:plain

こんな感じにです。

足し算の場合も同じ様に説明出来ます。

0.5を足しました。

f:id:kazuhironagai77:20211003220136p:plain

座標が0.5ずつ移動します。

f:id:kazuhironagai77:20211003220153p:plain

Wrapなので以下のような表示になります。

f:id:kazuhironagai77:20211003220207p:plain

4.NPCAIを追加

現在のNPCのMotionはこんな感じです。

f:id:kazuhironagai77:20211003220233g:plain

全く歩き回らないし、Motionもこれだけです。

人間らしさを感じずロボットのようです。

一方で、Monsterは

f:id:kazuhironagai77:20211003220307g:plain

AIを使用して自由に歩き回ったりしていて、生きている感じがよく伝わって来ます。

この生きている感じをNPCに追加したいです。

4.1 UE4_AIの復習

といってもUE4のAI自体の内容をすっかり忘れてしまったのでまず復習をします。

2020-12-06のブログ

ブログを見直すと2回程、UE4_AIの勉強を集中してやっている時期がありました。最初に集中して勉強した時は、AIそのものが良く分かっていなくてUE4のAIというよりAI全般の勉強をしていました。ので2回目に何を勉強したのかをサラッと復習します。

まず、前回のAIの勉強のまとめをしています。

一言で言うと、UE4のAIはIf節と同じ

f:id:kazuhironagai77:20211003220342p:plain

と言う事です。

そしてUE4_AIの作成方法についてまとめています。それを簡単に書くと

  • UE4_AIを使用するためには4つのClassが必要。
  • その4つとはCharacterAIControllerBehavior Tree、そしてBlackboardである。
  • その4つのクラスを繋げる必要がある。
  • 4つのクラスを繋げる具体的な方法の説明。

となっています。

これは大変上手くまとめられていて、自分で書いたのを言うのもアレですが、素晴らしいです。

その後でGameModeクラスの変数をBehavior Treeから呼び出す方法について解説しています。

これは、ちょっと読んだだけだと分からない箇所があります。

後で試す必要があります。

2021-01-17のブログ

Ryan Laley氏のAI [9] を勉強しています。

その過程で、

  • TaskクラスのEvent関数の違いについて調べています。

f:id:kazuhironagai77:20211003220430p:plain

  • AI Perceptionについて
  • Flow ControlのObserver abortsの値の違いが良く分からないと述べています

f:id:kazuhironagai77:20211003220455p:plain

  • Playerの操作するキャラを追いかける時に、カクカクして追いかけるのを直しています。
  • Monsterにパトロールさせています。

結構いろんな事をやっています。

細かい部分で何を言っているのか、分からない箇所が結構ありました。

Blogの書き方の向上が必要です。

後で読むと、何を言っているのか分からない箇所が結構あります。後でこの問題について検討します。

2021-01-24のブログ

UE4_AIに関しては、以下の3つを行うと書かれています。

f:id:kazuhironagai77:20211003220540p:plain

これがあるだけでも理解するのが凄い楽になります。

読み易いBlogとは文章そのものよりも、Blogの構成というか、Formatが大切な気がします。

次の文

f:id:kazuhironagai77:20211003220607p:plain

の意味が分かりません。これは文章の意味そのものが分かりませんね。

TaskクラスにAbort関数、Execute 関数、そしてTick関数があって、それらの関数とDecoratorクラスのObserver abortsの設定方法について何らかの関係があるのでしょうか?

実際のUE4_AIを見て確認するしかないですね。

まずTaskクラスと言っているのでBehavior Treeで使用されるTaskクラスの事でしょう。

TaskクラスのBPを開くと

f:id:kazuhironagai77:20211003220637p:plain

Abort関数、Execute 関数、そしてTick関数があります。

次にDecoratorクラスのBPを開いてDetail欄のFlow Controlを見るとObserver abortsがありました。

f:id:kazuhironagai77:20211003220650p:plain

うーん。Taskクラスについては覚えていますがDecoratorクラスの役割そのものについて覚えていません。

Decoratorクラスって何をやるクラスでしたっけ。

まあ、いいです。先を読んでいきます。

f:id:kazuhironagai77:20211003220704p:plain

あー。

思い出して来ました。

Observer aborts でselfやLower Priorityをセットした時のBehaviorが良く分からなかったんで「これってBugじゃないの?」とそれまでBlogに書いていたんです。

そしたら「Unreal Engine 4で極めるゲーム開発」にLower Priorityが発動する時の条件と発動した時の確認方法のヒントがあったんで、Observer aborts でselfやLower Priorityをセットした時のBehaviorは「実はbugじゃないみたいだ。それを試してみよう。」となったんでした。

今、「Unreal Engine 4で極めるゲーム開発」を見直したら、P414のNote「中断にまつわるエトセトラ」に、Lower Priorityが発動する時の条件と発動した時の確認方法のヒントが書かれていました。

そして以下のテストを始めています。

f:id:kazuhironagai77:20211003220728p:plain

Lower Priorityが発動する時の条件と発動した時に確認する方法を構築するに当たってSequence ノードとSelectorノードの機能の違いをしっかり確認しておく必要があったようです。それでSequence ノードとSelectorノードの復習をやっています。

しかしここでSequence ノードとSelectorノードの何を確認するのかが書かれていないのに

f:id:kazuhironagai77:20211003220742p:plain

いきなりテスト用のBehavior Treeを表示しています。

そのせいで、今読んだら何を証明しようとしているのか、今一分からない文章になってしまっています。

更に、この図だけではTaskであるPrint String内でどのような実装がされているのかも不明です。

Blogだけではこのテストが何をやっているのかはっきりしないので、このテストを実行したProjectを探してみます。

ありました。

f:id:kazuhironagai77:20211003220757p:plain

Study AI From Ryan Laleyと言う名前のProjectでMy Behavior Treeクラス内に以下の実装がされていました。

f:id:kazuhironagai77:20211003220813p:plain

実装内容をよく見ると、最初のテストの条件とは少し違いますが、ヒントが得る事は出来るでしょう。

Print String Task クラスを開いて見ると、以下の実装がされていました。

f:id:kazuhironagai77:20211003220828p:plain

Finish ExecuteノードとFinish Abortノードを使用していますね。

Sequence ノードとSelectorノードの何を確認するのかが不明なので、それぞれの機能について調べます。

Sequence ノードとSelectorノードにCursorを載せるとそれぞれの機能についての解説が表示されます。

f:id:kazuhironagai77:20211003220844p:plain

f:id:kazuhironagai77:20211003220849p:plain

成程、SelectorではChildのノードが一つでも成功したらそれ以上のChildの実行はしない訳ですね。Sequenceではその逆にChildのノードが一つでも失敗したらそこで実行が終了するわけです。

分かりました。

その条件で最初のテストを実行すると

f:id:kazuhironagai77:20211003220903p:plain

Selectorですので、最初のTaskであるPrint Stringが実行されます。

Print String内の実装は以下の様になっています。

f:id:kazuhironagai77:20211003220918p:plain

のでEvent Receive Execute AIが実行されPrint String (Execute)がPrintされる訳です。

この後はそのまま読んでも理解出来ます。

理解出来ますが、テストの目的をはっきり言ってないのと、理論と仮説を抜かしていきなりこの実装ならこうなるはずです。と述べているため、今読むとなんでそう考えているのかちょっと不明です。

ちょっと考えます。

この週のBlogを最後まで読んだら、まだこの時はObserver Abortの機能を完全に理解してないで「Bugじゃないのか?」と言っています。

f:id:kazuhironagai77:20211003221110p:plain

これは他のUE4_AIのブログも読んでから、どうまとめ直すのかを考えた方が効率的ですね。

先に残りのUE4_AIに関連したBlogを読んでみます。

2021-02-01のblog

Online LearningのIntroduction to AI with Blueprintsを勉強しています。

f:id:kazuhironagai77:20211003221138p:plain

結構、というかほとんど内容を忘れていますね。

UE4_AIのDebugのやり方なんかこれ見るまで完全に忘れていました。

ただ勉強している量が半端じゃないです。このTutorial全部終わらしています。ちょっと鬼気迫る気迫を感じます。

この週のBlogではまだObserver Abortの機能はBugである可能性が高いと思っていますね。

2021-02-07のBlog

ここでは実際のMonsterのAIを作成していますが、その作成の途中でObserver AbortのLower Priorityの意味に気が付いています。その後でかなり丁寧なテストをしてObserver Abortの機能を検討し直しています。

後EOSについての勉強もしていますね。EOSはNPCの動きが生き生きするためにはかなり重要だと思われるので今回もよく復習しておきたいです。

UE4_AIの復習まとめ>

UE4においてAIはかなり重要な位置を占めています。更にここで勉強して判明したUE4_AIに関する内容はかなり貴重なものです。分かり易くまとめ直す事にしました。次の節でこれをやります。

内容は、

  • UE4_AIを構成する4つのクラスとその繋ぎ方
  • Task BPのAbort関数、Execute 関数、そしてTick関数
  • Task BPのFinish Execute ノードとFinish Abortノード
  • SequenceとSelector
  • Debugのやり方
  • Observer Abortの機能について
  • EOSについて

を予定しています。

4.2 UE4_AIの復習 Part 2

もう一回、Blogをまとめ直すためには、UE4_AIのBlogの復習を先に終わらせる必要があります。

2020-12-06のブログ

大体は、4.1でまとめられています。4.1の内容で大切な所だけ繰り返しですが書いておきます。

  • UE4AIIf節と同じ
  • UE4_AIを使用するためには4つのClassについて
    • その4つとはCharacterAI ControllerBehavior Tree、そしてBlackboardである。
    • その4つのクラスを繋げる必要がある。
    • 4つのクラスを繋げる具体的な方法の説明。

この後に4.1では書いてなかった事が2,3個ありますので、それもまとめておきます。

<<SelectorSequenceの違いについて>>

SelectorとSequenceの違いについて考察を行っていますが、間違っています。

<<Game mode内の変数の値をBehaviorTreeにパスする方法>>

2020-12-06のブログのやり方を簡便に書き直した内容を以下に示します。

まず、GameModeクラスに何でも良いので変数を作成します。今回はBool型の変数、MyAIStateを作成しました。

f:id:kazuhironagai77:20211003221244p:plain

MyAIStateの値を受け取る実装をDecoratorクラスに作成します。

f:id:kazuhironagai77:20211003221316p:plain

これでGame Modeクラスに作成した変数の値は、Decoratorクラス内のBlackboard KeyタイプであるMy Blackboard Keyにコピーされます。

My Blackboard KeyはPublicにセットする必要があります。

f:id:kazuhironagai77:20211003221337p:plain

次にBlackboard クラスにMy Blackboard Keyの値を保持するための変数を作成します。

f:id:kazuhironagai77:20211003221353p:plain

ここではOnceを使用します。

Behavior Treeに戻ってMyAIStateの値を受け取る実装をしたDecoratorクラスのDetailを見ると

f:id:kazuhironagai77:20211003221407p:plain

Defaultの部分にMy Blackboard keyが表示されています。ここにBlackboard Keyで作成した変数であるOnceをセットします。

以上です。

このまとめも、今なら理解出来ますが、後で読んだら訳わからなくなりそうです。

UE4_AIを作成するための4つのクラスとそのクラスの繋ぎ方で具体的な作成方法を示して、そこで使用した例を元にこのやり方を説明する必要があります。

次の節でこれもやります。

<<ServiceクラスとTaskクラスの違いについて>>

ServiceクラスとTaskクラスの違いが分からない。と述べています。

これは今でも分からないですね。UE4_AIのブログの復習が全部終わっても分からないかったら調べます。

以上です。

2021-01-17のブログ

このBlogに関しても4.1にまとめられていない箇所があるのでもう一度まとめ直します。

<<TaskクラスのEvent関数の違いについて>>

f:id:kazuhironagai77:20211003221435p:plain

まず、普通のEventとAiのEventの違いについて調べていますが、あんまり意義のある解答は得られていません。これも後で調べます。

次にExecute、Abort、そしてTickについて調べています。

f:id:kazuhironagai77:20211003221449p:plain

このテストの面白い所は、Finish Execute ノードやFinish Abortノードを使用していない事です。

<<AI Perceptionについて>>

AIが操作するキャラに視覚に基づいた判断を追加する方法をまとめています。

単に読んだだけでやり方を全部理解出来ました。びっくりするぐらい出来が良いです。

最後に簡潔に

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

とまとめられていました。

<<Path Pointを使ってNPCを移動させる方法>>

以下の図の様にPath Pointを配置してAIがControlするキャラを移動させる方法についてまとめてありました。

f:id:kazuhironagai77:20211003221521p:plain

説明をしっかり読んだら再現出来そうです。

2021-01-24のブログ

<<SequenceノードとSelectorノードの復習>>

SequenceノードとSelectorノードの違いについて以下の実装を用いてテストしています。

f:id:kazuhironagai77:20211003221543p:plain

ここでPrint String BPは以下の実装、Finish Executeを使用しているのがPointです。

f:id:kazuhironagai77:20211003221559p:plain

更に、この実装を使用してExecute、Abort、そしてTickの違いも検討しています。

Observer Abort におけるNone、Self、Lower PriorityそしてBothの違いも確認しようとしましたが、この方法では違いは確認出来ませんでした。

<<デバックのやり方1>>

以下のImageを表示するためのDebugのやり方についてまとめてあります。

f:id:kazuhironagai77:20211003221623p:plain

Debugのやり方は2種類あってその内の一つです。

2021-02-01のブログ

Online LearningのIntroduction to AI with Blueprintsを勉強しています。

f:id:kazuhironagai77:20211003221642p:plain

2021-02-01のブログにはそれぞれの授業の内容が簡潔にまとめられているんですが、今の私、この内容をほとんど覚えていません。復習すべきか悩んでいます。

2021-02-01のブログを読み直して復習が必要かもしれない箇所は:

  • 9 NavMesh Agents
  • 13 以降全部(AI Perceptionのやり方について)
  • 20 Testing the Behavior TreeBehavior TreeDebugのやり方を教えている。)
  • 22 以降全部(EQS の作成方法について)

まあほとんど全部です。

まとめの内容は、目次として利用するならとても便利な仕上がりになっています。

2021-02-07のブログ

実際のMonsterのAIを作成しています。

  • 視覚の追加
  • 縄張りの作成
  • トロールの追加
  • モンスターを元の位置に戻す

などの機能を作成しています。

ここでやっとObserver Abortの機能について正確に理解してそれを確認するためのテストを実行しています。

結論はこれです。

f:id:kazuhironagai77:20211003221719p:plain

4.3 MonsterのAnimationとAI

MonsterのAnimationとAIの関係について復習するのを忘れていました。

ただこれも今週調べるとなるとAIの復習だけで終わってしまいそうなので、これは来週以降やる事にします。

5.UE4_AIのまとめ

5.1 UE4_AIのまとめ

以下の事を誰が読んでもすぐ理解出来るようにまとめ直します。

<基礎編>

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

<応用編>

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

今週は基礎編だけ作成します。

5.2 UE4_AIは単なるIF節!

AIの勉強と言うとほとんどの人が機械学習や深層学習のような学問を思い浮かべると思います。

そして以下に示したようなAIの教科書で勉強しない限りAIを作ったりAIを使用して何かをしたりする事は、無理だと思っている方が大半だと思われます。

f:id:kazuhironagai77:20211003221821p:plain

しかしUE4のAIを理解するのに、一般的なAIの教科書で勉強する必要は一切ありません。

なぜからUE4のAIは単なる○○だからです。

この○○に入る言葉は10万円ぐらいの価値があります。本当は黙っていたいですがこのブログの読者にだけ特別に教えます。

なぜからUE4のAIは単なるIF節の塊だからです。

IF節の塊とは以下に示したような

f:id:kazuhironagai77:20211003221838p:plain

「もし○○ならば××を実行します。」というProgrammingの構文の事を指しています。

この事実を明確にしないままUE4のAIの複雑な設計方法をダラダラと勉強すると必ずどこかで訳わからなくなります。

そしてUE4のAIは難解で理解出来ないとなってしまいます。

私も一番最初にUE4のAIを勉強した時、この単純な事実に気が付かなかったです。そしてある程度勉強したら訳わからなくなってしまいした。

2回目にUE4のAIの勉強に挑戦した時は、ふとしたことで「UE4のAIは単なるIF節の塊じゃないか!」と気が付きました。これに気が付いてからUE4のAIの勉強を見直すと、あんなに複雑に見えたUE4のAIの構造が単なる一本道に変わりました。一本道なので迷う事は全くなくなりましたというか不可能になりました。

そしてこの一本道最短ルートでUE4のAIの山を登ったら、自分が辿った勉強方法と同じやり方で解説したUE4のAIのTutorialが一個もない事に気が付きました。

そこでここに自分のやった一本道最短ルートでUE4のAIを理解する為の勉強方法をまとめる事にしました。

この私が作成したUE4のAIを理解するためのTutorialはその結果かなり独特な作りになっていますが、その効果は私自身で保証済みです。

5.3 UE4_AIを構成する4つのクラス

UE4ではNPCやMonsterなどのAIに操られているキャラは、まるで生きているような複雑な反応や動きをします。

この複雑な反応を単なるIF節で管理すると直ぐにSpaghetti Codeになってしまいます。のでUE4ではBehavior TreeでIf節を管理しています。

Behavior Treeとは非常に簡略化して言えば以下に示した図のような

f:id:kazuhironagai77:20211003221902p:plain

Behavior tree (artificial intelligence, robotics and control) [10]より

木を逆さまにしたような形状で沢山のIF節を管理するGraphの一種です。

所がBehavior TreeでIf節を管理するという方法を取ったために、UE4_AIでは単なるIf節を書くのに4つのクラスを作成しなければならなくなりました。

4つですよ。

この時点でUE4_AIが単なるIF節であるという事実を知らない人達はわけ分からなくなってしまっています。

ではその4つのクラスを紹介します。

<Characterクラス>

f:id:kazuhironagai77:20211003221930p:plain

このクラスはUE4_AIを始めて勉強する方でも元々知っていると思いますが、一応説明すると、ゲーム内のキャラクターを生成するためのクラスです。

AIにコントロールされるキャラクターを作成します。

普通のProgrammingならこのクラス内にAI用のIF節を沢山作って終わりです。

<AI Controllerクラス>

f:id:kazuhironagai77:20211003221946p:plain

AI Controllerクラスの機能を理解するためには、その前にUE4におけるPlayer が操作するキャラとPlayer Controllerクラスの関係を復習する必要があります。

UE4では以下に示した様に、

f:id:kazuhironagai77:20211003222001p:plain

Userがコントローラーなどで入力した信号は直接Characterに伝わるのではなく一端、Player Controllerクラスにパスされます。

Player ControllerクラスはUserがコントローラーから入力した信号をCharacterクラスが理解出来る言語に変換して伝える通訳のような役割をしています。

同様な仕組みでAIが操作するNPCやMonsterも管理されています。

f:id:kazuhironagai77:20211003222016p:plain

この時に、AIが出した信号をNPCやMonsterが分かる言語に変換しているのがAI Controllerクラスです。

<Behavior Treeクラス>

UE4のAIではIF節をBehavior Treeを使用して管理していると書きましたが、そのBehavior Treeを作成するためのクラスがこのBehavior Treeクラスです。

f:id:kazuhironagai77:20211003222117p:plain

中身はこんな風になっています。

f:id:kazuhironagai77:20211003222131p:plain

このBehavior Treeの書き方を理解する事がUE4のAIの作成方法を理解する事になります。

後でじっくりとこのBehavior Treeの作成方法について勉強しますが、このBehavior Treeクラスに関して一個だけ今知っておかなければならない事があります。

それは何とBehavior Tree クラス、普通のクラスのように値を保持する事が出来ないんです。

If節の作成で

If(a >1)

とか書きますが、このa に値を保持させる事が出来ないんです。

「じゃあどうやってIF節を作成するんだ。」となりますが、それはBehavior Tree クラスの代わりに値を保持する専門のクラスに代わりにやってもらうんです。

<Blackboardクラス>

f:id:kazuhironagai77:20211003222152p:plain

Blackboardとは黒板の事です。前述のBehavior Tree クラスが値を保持出来ないので、このBlackboardクラスがBehavior Tree クラスの代わりに値を保持します。

5.4 4つのクラスの繋ぎ方

この4つのクラスの繋ぎ方について解説します。

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

CharacterクラスのBPを開きDetailの中のPawn欄にあるAl Controller Classに作成したAI Controllerをセットします。

f:id:kazuhironagai77:20211003222221p:plain

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

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

f:id:kazuhironagai77:20211003222242p:plain

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

Behavior TreeクラスのBPを開くと、以下に示したように、BehaviorTree、Blackboard Assetと表示されている箇所があります。そこに作成したBlack Boardクラスをセットします。

f:id:kazuhironagai77:20211003222313p:plain

これでCharacterとAIが繋がりました。

この後はBehavior TreeにどうやってIF節を書いていくのかについての解説になります。

5.5 実際にAIを作ってみる。そしてTask BPを使ってAIがきちんと作れているかテストする

ここからはBehavior TreeにどうやってIF節を書いていくのかについての勉強になります。

そしてここからは実際にUE4を起動してやって行きます。

まず以下に示した様に

f:id:kazuhironagai77:20211003222335p:plain

CharacterクラスからMyNPC_2、AlControllerからMyAIController2、Behavior TreeからMyBehaviorTree2、そしてBlackboard クラスからMyBB2を作成します。名前はなんでもいいです。

Characterクラスから中身を作っていきます。

MyNPC_2クラスのBPを開き、

f:id:kazuhironagai77:20211003222353p:plain

を選択してAnimation Mode、Anim Class、そしてSkeletal Meshに以下の値をセットし

f:id:kazuhironagai77:20211003222406p:plain

Gray Manをセットします。

f:id:kazuhironagai77:20211003222426p:plain

そしてPawnのAI Controller Classに先程作成したAlControllerであるMyAIController2をセットします。

f:id:kazuhironagai77:20211003222440p:plain

これでCharacterの設定は終わりです。

次にAlControllerであるMyAIController2の設定を行います。

MYAIController2のBPを開くとお馴染みのGraphが表示されるので、以下に示した様に

f:id:kazuhironagai77:20211003222502p:plain

Event BeginPlayにRun Behavior Treeノードを接続します。

そしてBTAssetの欄に先程作成したBehavior TreeのMyBehaviorTree2をセットします。

これでAlControllerの設定は終です。

最後にBehavior TreeであるMyBehaviorTree2にBlackboard クラスであるMyBB2をセットします。

MyBehaviorTree2のBPを開くとDetail欄にBlackboard Assetと言う項目があります。

f:id:kazuhironagai77:20211003222517p:plain

ここにMyBB2をセットします。

これで完了です。

Level上にこのAIがControlするCharacterを配置してPlayを実行してみると、

f:id:kazuhironagai77:20211003222531p:plain

何も起きません。

これでは、実際にAIが正しく出来ているのか分かりません。

確認する必要があります。

そのためのテストをBehavior TreeであるMyBehaviorTree2に作成しましょう。

いや、Behavior TreeってIF節、つまり以下の様になってる訳でしょう。

f:id:kazuhironagai77:20211003222545p:plain

どうやってテストなんか作成出来るんですか?と思うかもしれません。

今回はこの“何かの条件”の部分を空欄にして、全ての条件で“実行する内容”を実行するようにします。

“実行する内容”がPlay中に実行されたらAIが正しく組めていると考えられます。

ではこのIF節の“実行する内容”はどうやって作成するんでしょうか?

はい。それを担当するのがTaskクラスになります。

まずMyBehaviorTree2のBPを開きます。

以下に示した様にRootノードのみが表示されています。

f:id:kazuhironagai77:20211003222559p:plain

このRootノードからBehavior Treeを作成します。

本来はこのRootノードに直接Taskを繋げたいのですが出来ません。仕方ないのでCompositeであるSequenceを繋げます。

f:id:kazuhironagai77:20211003222613p:plain

このCompositeであるSequenceやSelectorの機能については後で解説しますので、今は無視して下さい。

SequenceノードにはTaskが接続出来るようになっています。

f:id:kazuhironagai77:20211003222626p:plain

ここで既存のTaskクラスを使用しても良いのですが、そうするとTaskクラス以外の設定もしなければならなくなるので更に複雑になってしまいます。

ので接続するTaskクラスを新しく作成する事にします。

<Taskクラスの作成>

MyBehaviorTree2のBPのTool Barを見るとNew Taskがあります。

f:id:kazuhironagai77:20211003222646p:plain

New TaskをクリックしてBTTask_BlueprintBaseを選択します。

f:id:kazuhironagai77:20211003222702p:plain

するとTaskクラスであるBTTask_BlueprintBaseが作成されそのBPが勝手に開かれます。

f:id:kazuhironagai77:20211003222715p:plain

ここにTaskの実装を作成するのですが、その前に名前を変えます。

Browesボタンを押してBTTask_BlueprintBaseが保存されている場所へ飛びます。

f:id:kazuhironagai77:20211003222728p:plain

名前をFirst Taskに変えます。

f:id:kazuhironagai77:20211003222807p:plain

First TaskのBPに戻ってEvent Graphのどこかをクリックすると以下のようなものが表示されます。

f:id:kazuhironagai77:20211003222837p:plain

ここからEvent Receive Execute AIを選択して下さい。このEventのExecute、Abort、そしてTickの機能についても後で解説します。

そして表示されたEvent Receive Execute AIノードにPrint Stringを接続します。

f:id:kazuhironagai77:20211003222850p:plain

CompileしてSaveをします。これで最も簡単なTaskが完成しました。

作成したtaskをMyBehaviorTree2のSequenceに繋げます。

f:id:kazuhironagai77:20211003222914p:plain

これでMyBehaviorTree2も完成しました。

このMyBehaviorTree2の実装は

IF(何でもTrue){

First Task; }

みたいな感じになっています。

それではテストしてみます。

f:id:kazuhironagai77:20211003223010p:plain

First taskで指定したprint Stringが実行されています。

AIが働いている事が確認出来ました。

残念ながら時間が足りなくなってしまいました。Loading ScreenやGood Skyもやりたい事がありますので、AIの残りは来週やる事にします。

5.6 Task BP、Service BPそしてDecorator BPについて

5.7 Task BPのAbort関数、Execute 関数、そしてTick関数

5.8 Task BPのFinish Execute ノードとFinish Abortノード

5.9 SequenceとSelector

5.10 Debugのやり方

5.11 Observer Abortの機能について

6.Loading Screenのテスト(World Composition

一つ試したい事を思い付きました。それをやってみようと思います。

それは簡単に説明すると、以下に示したMap1

f:id:kazuhironagai77:20211003223034p:plain

のLandscapeと全く同じサイズのLandscapeをWorld Compositionを使用して作成してそこに以下に示したHight Mapを

f:id:kazuhironagai77:20211003223054p:plain

Import出来ないかと言う事です。

これを試してみます。

そしてもし時間があるならWorld CompositionのTutorialをもう少しやる事にします。

6.1 Map1用のWorld Compositionの作成

新しいMapを作成し、Enable World CompositionをTrueにしました。

f:id:kazuhironagai77:20211003223153p:plain

Sub Levelを追加します。名前はLevel1としました。

Sub LevelにLandscapeを追加します。

Sub Levelには一個のComponentだけ作成して沢山のSub Levelを作成する予定です。

Map1のLandscapeのデータを見ると

f:id:kazuhironagai77:20211003223213p:plain

Sectionのサイズが63x63 QuadsかつSections Per Componentが1x1 Sectionです。

となると新しく作成するSub Level用のLandscapeの条件は以下の様になるはずです。

f:id:kazuhironagai77:20211003223232p:plain

これで作成します。

f:id:kazuhironagai77:20211003223246p:plain

World Compositionを開くと以下の様になっていました。

f:id:kazuhironagai77:20211003223301p:plain

Map1のComponentの数は4*9=36です。

36個のSub Levelを作成します。

f:id:kazuhironagai77:20211003223315p:plain

f:id:kazuhironagai77:20211003223325p:plain

ここにMap1のHeight MapをImportします。

f:id:kazuhironagai77:20211003223349p:plain

あっけない程、普通に出来ちゃいました。

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

10000にします。

f:id:kazuhironagai77:20211003223409p:plain

確か単位はcmなので100mでUnloadされるはずです。

この条件でテストしてみます。

f:id:kazuhironagai77:20211003223424p:plain

ちょっとだけ進むと

f:id:kazuhironagai77:20211003223439p:plain

奥に山が現れました。

こっちの方が断然良いです。

6.2 LandscapeをPaintする

Map1のPaint方法と同じ方法でこのLandscapeをPaint出来るのか試してみます。

先週、Streaming Levelのテスト用に丸々Map1をImportしたヤツのデータがあったのでそれから調べます。

Map1はLandscape MaterialにMap1_Instを選択しています。

f:id:kazuhironagai77:20211003223458p:plain

Target Layersは以下の様な設定です。

f:id:kazuhironagai77:20211003223513p:plain

Swampに対応したLayerInfoも確かImportしたはずです。

まあ無くても9割は出来るでしょう。

Paint出来たのはLevel1だけです。

f:id:kazuhironagai77:20211003223528p:plain

それ以外にPaintしようとすると以下の様に

f:id:kazuhironagai77:20211003223543p:plain

Grass Maps Needs to be Rebuiltと表示されてPaint出来ません。

Build Grass Mapsを選択してもなにも起きません。

f:id:kazuhironagai77:20211003223556p:plain

うーん。全く分かりません。

分かりました。

それぞれのSub LevelはLandscape Streaming Proxyという

f:id:kazuhironagai77:20211003223615p:plain

Objectみたいなので管理されていました。それぞれのLandscape Streaming Proxyで

f:id:kazuhironagai77:20211003223628p:plain

Landscape Materialの設定をする必要がありました。

36個の全てのLandscape Streaming ProxyのLandscape MaterialにMat1_Instをセットすると

f:id:kazuhironagai77:20211003223643p:plain

既に色が塗られていました。

Grass Maps Needs to be Rebuiltは相変わらず表示されています。

ただこれは、Map1をImportした時にGrassに関する何らかのDataが消えてしまったからだと思います。

Play画面はこんな感じです。

f:id:kazuhironagai77:20211003223659p:plain

いいんじゃないでしょうか?

f:id:kazuhironagai77:20211003223715p:plain

奥のLandscapeが読み込まれました。

f:id:kazuhironagai77:20211003223730p:plain

あんまり変わった感じはありませんね。

後、気が付いた事ですが、PCが凄い静かです。

高い山からなら遠くまで見えるから、Unloadされていると不自然に見えるかも知れないと山を登って遠くを眺めて見ました。

f:id:kazuhironagai77:20211003223752p:plain

全然普通です。

後、Grass Maps Needs to be Rebuiltは一回Build Grass Mapsを行ったら消えました。

うーん。これはMap1を作り直したとしてもこっちの方が良いですね。

6.3 World Composition のTutorialを勉強する

何せ、World CompositionとStreaming LevelによるLandscapeのLoadingのTutorialを一個しかやっていません。

他のTutorialもやっておきます。

Unreal Engine 4 Tutorial - World Composition [11]

Ryan Laley氏のTutorialです。

AIであんなにお世話になったので、一番最初に見ました。特に新しい情報はなかったです。ActorをSub Level内に配置しているのは興味深かったです。

Unreal Engine 5 | Open World Tutorial Using World Partition [12]

よくよく考えたら、World Compositionとかそれに付随するLoading Mapの技術はUE5からは無くなります。UE5におけるそれらに変わる技術の使い方を勉強した方が良いですね。それでこのTutorialを見つけました。

半分位見ましたが、この作者凄い詳しいです。特に最新のUEの技術に対して凄いです。

これはいいサイトを見つけました。このUE5のTutorialも後でやってみる事にします。

7.Loading Screenの勉強(Module の作成)

すっかりこれの勉強を忘れていました。

Moduleの作成方法の復習だけなのでやります。

7.1 Moduleの作成方法のTutorial色々

まずどんなModuleの作成方法を教えるTutorialにはどんなのがあるのか調べました。

Unreal Engine 4 Scripting with C++ Cookbook [13] の8章のCreating a New Editor Module

いつものUE4C++の教科書です。ぱっと見た所、かなり簡単な作りになっていました。前にやった時は解説部分をよく読まなかったので、その辺をしっかりやれば新しい知見が得られそうです。

ただし文章メインなので途中で分からなくなった時、結構迷いそうなのと、最新のVersionに対応してないのが不安点です。

How to create Unreal Engine module (C++) [14]

時間も短くて、結構分かり易そうです。ですが、何故か評価が低くて再生回数も少ないです。

Unreal Engine 4 C++ Module Creation Basics Part 1 [15]

再生回数多くてそれなりに評価も高いですが、動画が作成されたのが2015年です。更にPart 2まであります。

UE4 Modules [16]

たった一年前に作成されているのと評価が物凄く高いのですが、あまりに専門的な話過ぎて半分まで見た所で飽きてしまいました。これはModuleの作成方法と言うよりModuleの全てを理解するためのLectureです。

7.2 How to create Unreal Engine module (C++) [14]を勉強する

色々検討した結果、これをやる事にしました。Moduleの勉強もそうですが、このTutorialの人気がない理由を知りたいからです。

このTutorialのために新しいProjectを作成する事にしました。

今まで使用していうるNiagaraはBP専用でやっているのでC++を追加したくないからです。

4.26、C++、First Person Templateで作成しProjectの名前はTest Moduleにしました。

f:id:kazuhironagai77:20211003223953p:plain

C++クラスが出来ています。それでは始めます。

6つのStepでModuleが作成出来るそうです。これは記録を書くのが簡単になりそうです。

<Step1: Folderの作業>

最初はProjectのFolderでやるそうです。

Source fileを開けて新しいFolderを追加しろと言っています。

f:id:kazuhironagai77:20211003224015p:plain

名前はNewModuleにしていますね。そっくり同じFolderを作成します。

f:id:kazuhironagai77:20211003224031p:plain

<Step2: 3つのFileをCopyする>

以下の3つのFileをCopyしろと言っています。

f:id:kazuhironagai77:20211003224048p:plain

しました。

そしたら先程作成した新しいFolderであるNewModule内にPrivate とPublicと言う名前のFolderを作成しろと言っています。

f:id:kazuhironagai77:20211003224107p:plain

しました。

f:id:kazuhironagai77:20211003224120p:plain

そしたら先程CopyしたFileをここでPasteしています。

もう先程CopyしたFileは上書きされてしまっています。もう一回CopyしてPasteしました。

f:id:kazuhironagai77:20211003224142p:plain

名前をNewModuleに変更してHeader fileはPublic、Cpp FileはPrivateに移します。

f:id:kazuhironagai77:20211003224200p:plain

しました。

NewModule.cppを開いて

f:id:kazuhironagai77:20211003224213p:plain

f:id:kazuhironagai77:20211003224228p:plain

に変更しました。

これは単に名前を変更したからやっているだけでしょうね。

次に以下のCodeからPRIPARYを消して

f:id:kazuhironagai77:20211003224244p:plain

IMPLEMENT_GAME_MODULEに変更しました。

f:id:kazuhironagai77:20211003224312p:plain

これは何処かで勉強したはずです。後でチェックします。

そして今度は()内のTestModuleをNewModuleに変更しました。

ただしその後の ”TestModule” はそのままです。

f:id:kazuhironagai77:20211003224326p:plain

今度はNewModule.Build.csを改造します。

何かStep2はやる事が多いんですが、Step3は存在しているんでしょうか?忘れてしまった事はないと思いたいです。

f:id:kazuhironagai77:20211003224340p:plain

名前をTestModuleからNewModuleに変更しました。

f:id:kazuhironagai77:20211003224354p:plain

これも単にFileの名前を変えた分の直しです。

開いたFileを全部閉じて、uproject fileが在るところまで戻ります。

そしてSln fileを消してuprojectを右クリックしてGenerate Visual Studio Project Fileを選びます。

f:id:kazuhironagai77:20211003224410p:plain

f:id:kazuhironagai77:20211003224415p:plain

出来ました。

f:id:kazuhironagai77:20211003224429p:plain

Visual StudioをReloadしたら

f:id:kazuhironagai77:20211003224444p:plain

NewModuleが追加されていました。

<Step3: Target.csの改造>

今度はTarget.csの改良です。Step3だそうですので忘れていなかったんですね。

TestModuleEditor.Target.csです。

f:id:kazuhironagai77:20211003224503p:plain

ExtraModuleNames.Add("NewModule");を追加しました。

全く同じ事をTestModule.Target.csにも行います。

f:id:kazuhironagai77:20211003224528p:plain

最後にuprojectファイルに以下の部分を追加します。

f:id:kazuhironagai77:20211003224541p:plain

そしてSlnファイルとIntermediateフォルダーを消した後で、またGenerate Fileを行います。

f:id:kazuhironagai77:20211003224556p:plain

Errorが出てしまいました。

f:id:kazuhironagai77:20211003224609p:plain

以下の部分が

f:id:kazuhironagai77:20211003224624p:plain

f:id:kazuhironagai77:20211003224631p:plain

だそうです。

f:id:kazuhironagai77:20211003224651p:plain

コメントを読むと最後の "TestModule" が要らなくなったようです。

f:id:kazuhironagai77:20211003224705p:plain

でもう一度試してみます。

f:id:kazuhironagai77:20211003224734p:plain

今度は出来ました。

新しく出来たProjectをUE4 Editorから開きましたが作成したModuleは表示されていません。

f:id:kazuhironagai77:20211003224748p:plain

それは新しく作成したModuleが何のFileも持っていないからです。

Actor Fileでも追加します。

f:id:kazuhironagai77:20211003224806p:plain

追加するModuleはNewModuleです。

Errorが起きましたがTutorialでも全く同じErrorが起きているのでそのまま続けます。

f:id:kazuhironagai77:20211003224820p:plain

Binary folder、Derived Data Cache folder、Intermediate folder、Saved folderを消してRebuildをします。

今度はUE4 Editorに新しく作成されたModuleが表示されています。

f:id:kazuhironagai77:20211003224838p:plain

以上です。

7.3 How to create Unreal Engine module (C++) [14]のReview

Stepが3までしかなかったのは兎も角一応、Moduleを自作する事が出来ました。

大体理解出来たので最初は自分の頭だけでModuleの仕組みを説明してみます。その後でしっかり調べてその通りかどうかを検討します。

まずはModule用のFolderを作成します。作成したModuleはSource Folder内に配置します。

f:id:kazuhironagai77:20211003224857p:plain

Moduleそのものの構成はProject名.Build.cs、Project名.h、Projct名.cpp fileの3つのfileと全く同じです。

f:id:kazuhironagai77:20211003224914p:plain

このModuleが存在する事を知らせるために以下の2つのTarget.cs fileとuproject fileにコードを追加します。

f:id:kazuhironagai77:20211003224928p:plain

f:id:kazuhironagai77:20211003224935p:plain

以上です。

かなり想像で書きましたが、大体は合ってるでしょう。

それではそれぞれのfileの機能について調べていきます。

<Target.cs fileについて>

公式のDocumentのTargets [17] によると

f:id:kazuhironagai77:20211003224954p:plain

と書かれています。するとEditor.Target.csがEditor用のTargetでTarget.csがGame用のTargetと推測出来ます。

実際、Fileを開くと

f:id:kazuhironagai77:20211003225008p:plain

f:id:kazuhironagai77:20211003225014p:plain

となっています。

Extra Module Nameですが、これについてもTargets [17] で解説されていました。

f:id:kazuhironagai77:20211003225032p:plain

ここからは私の推測が混じりますが、Target.csはCompileする時にこのModuleも追加しておくように。とEditorやGameに対して命令する権利があります。ので新しく作成したModuleがGameやEditorで使用出来るようにTarget.csで命令しているんです。

<uproject fileについて>

何故か.uproject fileについての公式のDocumentが見つからないので、

What’s the difference of .target.cs file and .uproject file? [18] の解答の一部を載せておきます。

f:id:kazuhironagai77:20211003225144p:plain

この解答によると.uproject fileは人に向かってどんなModuleが使用されているのかを説明しているだけのようです。最悪無くても大丈夫のようです。

もう少し掘り下げたかったんですが時間が無くなってしまいました。残りは来週やります。

8.Good Skyの復習

昼と夜をどうやって実現させたのかが先週の復習で分かったんですが、Map自体をWorld Compositionで作成し直すとなるとGood Skyとその機能を今、Landscape4に追加してもすぐ外す事になりそうなので止めておきます。

先週以下の計算が正しい事を理論的にも証明しておきたいと言っていましたが、

f:id:kazuhironagai77:20211003225224p:plain

これはちょっと考えたら当たり前でした。

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

f:id:kazuhironagai77:20211003225248p:plain

Delta SecondはFrame間の時間です。TickはFrame毎に発動します。

FPSが10の時、Delta Secondは0.1秒になります。よって1秒は1秒です。

FPSが20の時、Delta Secondは0.05秒になります。よって1秒は1秒です。

つまり上記のやり方ならFPSが変わっても昼と夜の時間は設定した時間通りに変化します。

9.まとめと感想

まとめる時間が無くなってしまったのでこれでお終いにします。今週終わらなかったところは来週やります。

10.参照(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] Cloward, B. (n.d.). Shader Graph Basics. YouTube. Retrieved October 3, 2021, from https://www.youtube.com/playlist?list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl

[3] Cloward, B. [Ben Cloward]. (2021a, July 15). Linear Interpolation Node (Lerp) - Shader Graph Basics - Episode 6 [Video]. YouTube. https://www.youtube.com/watch?v=SiPhlGjqdyo&list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl&index=7

[4] Matusik, W. (n.d.). Bézier Curves and Splines. MIT CSAIL. Retrieved October 3, 2021, from https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-837-computer-graphics-fall-2012/lecture-notes/MIT6_837F12_Lec01.pdf

[5] Epic Games. (n.d.-a). Color Expressions. Unreal Engine Documentation. Retrieved October 3, 2021, from https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/Materials/ExpressionReference/Color/#desaturation

[6] Cloward, B. [Ben Cloward]. (2021b, July 22). Dot Product Node - Shader Graph Basics - Episode 7 [Video]. YouTube. https://www.youtube.com/watch?v=sD6y-hUxEck&list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl&index=7

[7] math.stackexchange.com. (n.d.). Visualising $\pi_2(S^2)$ and $\pi_2(\mathbb{R}P^2)$. Mathematics Stack Exchange. Retrieved October 3, 2021, from https://math.stackexchange.com/questions/2634214/visualising-pi-2s2-and-pi-2-mathbbrp2

[8] Cloward, B. [Ben Cloward]. (2021c, July 29). Texture Coordinates - Shader Graph Basics - Episode 8 [Video]. YouTube. https://www.youtube.com/watch?v=reAlVCXBtjs&list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl&index=8

[9] Laley, R. (n.d.). AI. YouTube. Retrieved October 3, 2021, from https://www.youtube.com/playlist?list=PL4G2bSPE_8ukuajpXPlAE47Yez7EAyKMu

[10] Behavior tree (artificial intelligence, robotics and control). (2021, September 20). In Wikipedia. https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)

[11] Laley, R. [Ryan Laley]. (2020, June 10). Unreal Engine 4 Tutorial - World Composition [Video]. YouTube. https://www.youtube.com/watch?v=hzrwu_HHZcI

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

[13] Sherif, W., & Whittle, S. (2016). Unreal Engine 4 Scripting with C++ Cookbook (1st ed.). Packt Publishing.

[14] Iovbak, A. [Anton Iovbak]. (2020, August 23). How to create Unreal Engine module (C++) [Video]. YouTube. https://www.youtube.com/watch?v=ZQhWyGFj1Qk

[15] Dokipen. (2015, February 28). Unreal Engine 4 C++ Module Creation Basics Part 1 [Video]. YouTube. https://www.youtube.com/watch?v=piPkLJerpTg

[16] flassari. (2020, May 6). UE4 Modules [Video]. YouTube. https://www.youtube.com/watch?v=DqqQ_wiWYOw

[17] Epic Games. (n.d.-b). Targets. Unreal Engine Documentation. Retrieved October 3, 2021, from https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/TargetFiles/

[18] Epic Games. (2019, August 7). What’s the difference of .target.cs file and .uproject file? Unreal Engine Forums. https://forums.unrealengine.com/t/whats-the-difference-of-target-cs-file-and-uproject-file/129625