UE4の勉強記録

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

「Unreal Engine 4.xを使用してRPGを作成する」の足りない部分を作成する Prologueの作成

f:id:kazuhironagai77:20220123225038p:plain

<前文>

<Metaverseについての個人的な考え>

失敗すると思います。以下に理由を示します。

<<Facebookがやるから>>

Facebookは日本ではGAFAとか呼ばれてSoftware Business界の一角と思われていますが、アメリカのいわゆるz世代から蛇蝎の如く嫌われています。誰もやらないでしょう。じゃどんな層が今Facebookを支持しているのかというと、所謂トランプ支持派や隠れ白人至上主義者たちです。彼等は他のSNSでは遅かれ早かれ実態がばれてBanされているのですが、Facebookはとぼけて彼等に自由に活動させています。のでトランプ支持派や隠れ白人至上主義者たちはFacebookが大好きです。

今、沢山のトランプ支持派や隠れ白人至上主義者たちがワクチンを打たないでコロナにかかって死んでいますが、一言で言って彼等は非科学的なんです。彼等の中では論理が通っているらしいんですが、科学の勉強を全くしてこなかったのか、あるいは勉強したけど理解出来なかったのか分かりませんが兎に角、科学技術に疎いです。彼等が積極的にMetaverseをやるとは思えません。

<<キャラがアニメ調じゃない>>

英語の話せない日本人に言うとびっくりされますが、アニメや漫画はもう世界の共通語になっています。ほぼほぼ単一民族である日本に暮らしていると理解出来ませんが、世界は人種、宗教、性別でその人の評価は90%位決まってしまうんです。それがアニメのキャラをアバターにする事で、そういうしがらみから自由になれるんです。だからMedia(情報を伝達するための媒体と言う意味で)としてのアニメの潜在力は凄いんです。そして日本人以外の世界の若い世代はその潜在力の凄さにみんな気が付いています。

敢えてアニメ調でない3Dデザインを採用しているので若い世代から興味を持たれる事はないでしょう。

そしてもう一つ、アニメ調でない3Dデザインを採用している事でほとんどの日本人が気が付かないメッセ―ジを彼等は発しています。それはキリスト教の価値観の押しつけです。

本当の聖書に書かれているキリスト教的な価値観ではなく、中絶絶対禁止のようなカルト的なキリスト教の価値観の事です。

誰も興味持たないですよ。

<<世界村の誕生に気が付いていない>>

Internetの誕生による最も大きな変化は世界が一個の村になった事だと思っています。その真意ですが、村で起きたあらゆる事は村人みんなが知っているって事です。今までは地球の裏側で起きた事はほとんどおとぎ話に出て来る聖人のように精製されて伝わって来ましたが、これからは醜悪な実態も一緒に伝わって来ます。

つい最近もアメリカの裁判官が会社が経営する刑務所から賄賂を貰っていたのが判明しました。その裁判官はその刑務所が黒字になるように何でもかんでも有罪にしてその刑務所に送っていた事もついでに判明しました。

今までだったら、アメリカでも裁判官の道徳観を疑うと言う事な無かったと思います。「神聖にして侵すべからずの精神で、裁判官は調べなくても正しい道徳観に基づいて行動しているはず。」と勝手に思っていたはずです。そういう考えが無くなって来てるというより、そうでないと言う情報が簡単にネットに流れるようになって来ました。

これがInternetによる世界村の誕生の意味です。

これからはMetaverseは利用者のどんなDataを企業に売るのかについてとその倫理も厳しく問われるでしょう。その事自体にFacebookが気が付いてないと思われます。

<<VRの悪酔い対策について>>

Internetの発達は更に、誰でも世界に向かって平等に発信出来るという変化ももたらしました。それによってFacebookの技術を陰に陽に支えているMITに対する対応も変わってくると思っています。世間はそれまではMITは凄いとしか言わなかったでしょうが、これからは技術が劣っていたり頓珍漢な事をしたりすれば世間から簡単に駄目って言われるようになるでしょう。

VRゴーグルを使用したら酔うんです。それもかなりの悪酔いをします。これに対してどんな解答をMetaverseの技術を支えるMITは用意しているんでしょうか?

多分ですが何も考えていないと思います。

<<まとめ>>

これだけ批判的な事を書きましたが、本当はMetaverseに成功して欲しいと思っています。

私は大学で3d Graphicsを専門にしたのですが、MRIのデータの3d化とかの医学関連の研究をしてました。その時友人に言われたのが「その技術って10年前から医療に革新を起こすって言っているけど、一向に医療現場で採用されないね。」でした。実際、Computer Scienceの他の分野の成功に比べて3d Graphicsはここ何年か鳴かず飛ばずです。

ここは絶対、3d Graphicsが必要って分野がGame以外で一個位出来て欲しいです。そういう意味では本当はMetaverseには成功して欲しいと思っています。

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

<本文>

1.今週の予定

以下の内容をやっていきます。一応、先週で武器屋の直しは終わったので代わりにPrologueの作成を始めます。

  • Niagara: CGHOW氏の何かを勉強する
  • Material :Projectionの勉強の続き
  • NPCAIを作成する
  • Game DesignポケモンHxHの念能力( 戦闘システムの作成)
  • Prologueの作成
  • UE5naniteの勉強

2.Niagara: CGHOW氏の何かを勉強する

2.1 Wave in UE5 Niagara Tutorial [1] を勉強する

最近、Niagaraの勉強としてCGHOW氏のTutorialを勉強していますが、その内容のほとんどはMaterialの作成でした。NiagaraによるVFXの成果の30%位はMaterialの良さで決まってしまうので仕方のない面もありますが、今回はもっとNiagaraよりのTutorialをやりたいです。

そこでWave in UE5 Niagara Tutorial [1]を選びました。

f:id:kazuhironagai77:20220123225218p:plain

何となくですが、Niagaraの機能を沢山使ってそうです。

<ざっと全部見る>

予想に反して90%位Materialを弄っています。まあしょうがないですね。これをやります。

特に知らないNodeは無かった気がします。Noise用のTextureは付属のPerlin Noiseで代用します。

しかしこのTutorialでのCGHOW氏のUE5は凄い回数クラッシュしていますね。CGHOW氏もこの原因が分からないと言っています。CGHOW氏はこれ以降のTutorialではUE5の使用を停止しています。みんながUE5に移行した時に、「どんでもないバグがありました。」とならないと良いですね。

<作成方法の予想>

軽くですが一回既にTutorialを見ておいてあれですが、どのようにこのVFXを作成したのかを予測します。

この練習をしないと、何時まで経っても自分でNiagaraを使用して自由にVFXを作成出来るようにならないからです。

といっての全くの無からこのVFXの作成方法を予測するは荷が重すぎます。一回軽く見てその記憶を参考にしつつ作成方法を予測する事にしました。

Niagara のEmitterは3つ、Materialも3つ使用します。

まず、脇に立っているヤシの木用に一つです。これはMesh Renderingで一個だけ生成しているだけでしょう。

次に波の泡です。泡はNoiseを使用して作成します。泡の移動はPanningで行います。斜めに移動するためにRotation をTexCoord[0]に掛けます。

最後に波と砂の色です。これはLerpノードで色付けします。移動は先程の波の泡に同期させて行います。

上記の2つのMaterialを使用するNiagaraのEmitterは一個のSpriteを発生させるだけです。後、Dynamic Material Parameter Moduleを使用してMaterialのParameterをセットします。

<実際にTutorialをやる>

3つのEmitterを使用しています。

f:id:kazuhironagai77:20220123225258p:plain

一つ目は波の作成、

f:id:kazuhironagai77:20220123225316p:plain

2つ目は水と砂を表しています。

f:id:kazuhironagai77:20220123225339p:plain

今気が付いたんですが、水と砂の位置は動いていません。パッと見には動いているように見えていました。

最後は木です。

f:id:kazuhironagai77:20220123225400p:plain

はい。この部分はMeshが作れないと作成出来ないので今週はやりません。

<波用のMaterialを作成する>

まず波用のMaterialから作成していきます。

f:id:kazuhironagai77:20220123225425p:plain

Tutorialだと一気に以下の実装を作成して

f:id:kazuhironagai77:20220123225455p:plain

以下の波の端のギザギザ感を出しています。

f:id:kazuhironagai77:20220123225517p:plain

それぞれのノードの役割の説明は全くないです。しかたないので自分で実装した後、調べます。

出来ました。

f:id:kazuhironagai77:20220123225538p:plain

以下の様な結果になりました。

f:id:kazuhironagai77:20220123225554p:plain

Noiseに付属のPerlin Noiseを使用しているので完全に同じには出来ませんが、まあ良いでしょう。

それぞれのNodeの役割を見て行きます。

f:id:kazuhironagai77:20220123225627p:plain

ここは分かります。波が発生しているのは端だけなのでそれ以外のギザギザ感を消すためにMaskをかけています。

RでMaskをかけるので横方向にMaskがかかります。以下に示した黒い部分にMaskがかかります。

f:id:kazuhironagai77:20220123225656p:plain

以下に示した部分でMaskを掛けています。

f:id:kazuhironagai77:20220123225716p:plain

次のNodeの目的が分かりません。

f:id:kazuhironagai77:20220123225735p:plain

波の端の位置を移動するためなら以下の様に組む必要があります。

f:id:kazuhironagai77:20220123225752p:plain

これならMaskの位置が移動します。

色々試してみると、最初のTutorialのやり方でも同じ結果に成る事が分かりました。

f:id:kazuhironagai77:20220123225932p:plain

確かに0以下と1以上の値はImageには現れませんが、存在はしているので、ここで数字を足しても同じ結果になります。

次のNodeです。

f:id:kazuhironagai77:20220123225949p:plain

前のNodeらは波の位置を決定しています。今度のNodeは波の細かさでも指定しているのでしょうか。

これの意味は良く分からないです。

取りあえず先に進みます。

最後のノードです。

f:id:kazuhironagai77:20220123230008p:plain

これは灰色の箇所を白黒に変換するために実装しています。

Instanceを作成してParameterのAddの値を変更すると確かに位置が変わります。

0.5の場合です。

f:id:kazuhironagai77:20220123230045p:plain

-0.5の場合です。

f:id:kazuhironagai77:20220123230103p:plain

よく見るとAdd = 0.5の時に灰色の箇所が存在しています。

これを取るためにSaturateノードを追加します。

f:id:kazuhironagai77:20220123230124p:plain

結果です。

f:id:kazuhironagai77:20220123230141p:plain

確かに灰色の箇所が無くなりました。

ただ意味が分かりません。

Floorを掛ければ少数点以下の値は0になるはずです。そしたら0か1、又は1以上の整数になって灰色の値(つまり0よりは大きくて1よりは小さい。)が出て来る可能性は無いはずです。

なのに実際は灰色の値が発生しています。

これは推測ですが、Materialノードでは実装の最後にFloorを指定してもHLSLに変換された時はFloorは最後に実装されなくなるのではないのでしょうか?

例えば、Vertex Shaderの最後でFloorが実行されるとか。

そしてSaturateノードはFloorノードとは違い必ずpixel shaderの最後で実行されるのかもしれません。

あくまでも推測ですが。

うーん。HLSLを見たら分かるんでしょうか?

試してみます。

Saturateノードを使用した場合と使用しない場合でHLSLを作成してNotePad++で比較してみました。

Saturateを使用している箇所がすぐに見つかりました。

Saturate有のHLSL

f:id:kazuhironagai77:20220123230217p:plain

Saturate無しのHLSL

f:id:kazuhironagai77:20220123230231p:plain

これ見ると同じ個所でやってますね。

と言う事はFloorの返し値は整数ではない時もあるという事なんでしょうか?

分かりました。

Saturateノードの機能を以下に示します。

f:id:kazuhironagai77:20220123230246p:plain

SaturateノードはFloorのように小数点の値を整数に変更するんじゃなくて0以下1以上の値をClampするんでした。

つまりFloorで生成された2、とか3の値を1に変更しているんでした。

これがBase ColorからOutputされているなら2とか3の値は自動的に1になりますが、Emissive Colorなのでそのまま出力されていたんです。

つまり以下のSaturate無しのノードの場合は灰色の部分の値が1で白い部分の値は2だったんです。

f:id:kazuhironagai77:20220123230319p:plain

以下に示した様にUnlitで表示すると灰色と白の違いが無くなります。

f:id:kazuhironagai77:20220123230338p:plain

更にSaturateした後のMaterialを見直すと、一見白くなっていると思われていた箇所は実は灰色になっています。

f:id:kazuhironagai77:20220123230357p:plain

これでSaturateノードの役割が理解出来ました。

次に行きます。

次の実装はShockwaveのTutorialで既に細かい解説をやっているので解説は簡単にしかしないそうです。

これの事でしょうか?

f:id:kazuhironagai77:20220123230415p:plain

これも後で勉強する必要がありますね。

何を作成するのか今一分からなかったんですが以下のような実装を追加するみたいです。

f:id:kazuhironagai77:20220123230436p:plain

この白い部分にだけ先程のNoiseが乗る訳です。

こうする事で波らしくなるんでしょうか?今一分かりません。

が此処で大切なのはこの形の実装が出来るようになる事です。その後でこの実装方法が必要な個所とそうでない箇所についてDesignの観点から取捨選択すれば良い訳ですから。

実装しました。

f:id:kazuhironagai77:20220123230453p:plain

テストします。

f:id:kazuhironagai77:20220123230507p:plain

結果です。

f:id:kazuhironagai77:20220123230524p:plain

Offsetの値を0 にします。

f:id:kazuhironagai77:20220123230543p:plain

あれ。

これってひょっとして波の最終到達地点をずらすために実装したの。

色々試してみましたが、波の最終到達地点をずらす(offset)するのが目的で間違いないです。

それでは以下の部分がどうやって波の最終到達地点をずらしているのかについて調べます。

f:id:kazuhironagai77:20220123230639p:plain

ただこれは一個ずつノードを調べても分からないかもしれません。その時は

f:id:kazuhironagai77:20220123230703p:plain

を勉強するしかないですね。

まずこのAddの部分ですがこれは分かります。

f:id:kazuhironagai77:20220123230720p:plain

Offsetにマイナスの値を入れるとMaskした黒い部分が増えます。

次のStepノードが何をやるノードが覚えていません。

f:id:kazuhironagai77:20220123230740p:plain

Ctr+Altで詳細を示しても

f:id:kazuhironagai77:20220123230757p:plain

何も説明してくれないです。

ああ、思いだしました。これは前も調べました。昔のBlogを遡ってみましょう。

2021-11-14のBlogにStepの機能が書かれていました。

このBlogを読み直すとStepノードの公式のDocumentは見つからないしもしかするとないかもしれないと書かれています。UnityのMaterialにあるStepノードの機能から推測してXの値がYより小さい時は0、Xの値がYより大きい時は1を返す機能のようだと結論づけてました。

確認のために軽くテストをしてみます。

Yの値を0.5に固定します。

f:id:kazuhironagai77:20220123230817p:plain

X=0の時です。

f:id:kazuhironagai77:20220123230832p:plain

0です。

X=0.4の時も0でした。

X=0.6 の時です。

f:id:kazuhironagai77:20220123230849p:plain

1になりました。

因みにx = 0.5の時も同じ値になりました。

今回の実装でOffset Parameterの値を0.5にセットした場合を考えてみます。

f:id:kazuhironagai77:20220123230909p:plain

1-xノードを通しているのでXの値は0.5です。

この実装ではxの値を固定してYの値を変化させているので結果の予測が少しだけ難しいかもしれませんが、結局X >Y の時に1、X<Yの時に0、 X=Yの時は1になるはずです。

上の結果を見るとそうなっています。

次に、この結果を先程のAddの結果をMultiplyノードを使用して掛けています。

f:id:kazuhironagai77:20220123230928p:plain

これで以下の様なOffsetが作成出来た訳です。

f:id:kazuhironagai77:20220123230946p:plain

ただこれだと以下に示した様に

f:id:kazuhironagai77:20220123231033g:plain

波を表す白い部分の幅は前と同じです。

そこで最後の実装部分であるPowerが必要になります。

f:id:kazuhironagai77:20220123231103p:plain

Powerを追加してWidthの値を変化させると

f:id:kazuhironagai77:20220123231131g:plain

白い部分の幅が小さくなります。

これは先週のBlogでもやりましたが、Powerの性質が

f:id:kazuhironagai77:20220123231154p:plain

値が大きくなればなるほど、カーブが急になるからです。

大体理解出来ましたが、最初の

f:id:kazuhironagai77:20220123231214p:plain

この部分って要らない気がします。

この部分の機能って黒い部分をMaterialの左側に生成する事だと思うんですがそれをするためには値がマイナスにならないと出来ません。

Offsetは0から1の間の値しか基本的には取れませんから、つまり機能してないと思うんです。

それについて検討します。

外してみます。

f:id:kazuhironagai77:20220123231231p:plain

外して直接つないでみました。

Width Parameterの値を変化させます。

f:id:kazuhironagai77:20220123231408g:plain

一寸見た目が汚く成りましが、これでも機能してはいますね。

ああ、分かりました。

f:id:kazuhironagai77:20220123231451p:plain

この部分はOffsetの白と黒の境界の部分の白い部分の値を常に1にするためにあるんです。そうする事で表示されている白い部分で0から1までのカーブを描けるわけです。

試しにOffsetが0.5の場合を考えてみます。白と黒の境界線は0.5になります。そこに0.5を足します。つまり1になります。

でもOffsetが0.5の場合の左端、つまり値が0の箇所は0.5を足す事で0.5になりますね。白い部分で0から1までのカーブを描けるわけじゃなくてOffsetの値から1までのカーブを描いていますね。

成程、分かりました。

波を表現するんで、左端が常に0になっても困る訳です。枠からはみ出してる箇所にも波が見えないだけである訳でOffsetの値から1までのカーブを描く必要がある訳ですね。

納得しました。

今度はこの白黒にNoiseを乗せます。

先程の実装をnoiseを示してるTexture SampleにAddノードを使用して追加します。

f:id:kazuhironagai77:20220123231519p:plain

その結果にPowerノードを繋いでExpの値をParameter dissolveで指定します。

f:id:kazuhironagai77:20220123231533p:plain

更にその結果をもう一回先程の実装で掛けます。

f:id:kazuhironagai77:20220123231550p:plain

この部分に関してはtutorialでも説明していました。

これをする事でNoiseが白い部分以外からはみ出なくなるそうです。

0を掛ければ何でも0になるのでまあ納得です。

残りは前と一緒です。

テストします。

Dissolveの効果を見るために以下の条件でDissolveの値を変化させます。

f:id:kazuhironagai77:20220123231608p:plain

結果です。

f:id:kazuhironagai77:20220123231650g:plain

Dissolveとはよく言ったもんです。

この部分の実装に関してはあんまり疑問もないのでこのまま次をやります。

次のFront lineにも歪みを追加します。

以下の部分を追加実装しました。

f:id:kazuhironagai77:20220123231832p:plain

結果が真っ黒になってしまったのであれっと思ってコードを見なおしたらRから繋いでいました。

f:id:kazuhironagai77:20220123231854p:plain

テストします。

以下の条件でDistortの値だけ変更します。

f:id:kazuhironagai77:20220123231913p:plain

結果です。

f:id:kazuhironagai77:20220123231940g:plain

後ろもDistortしていますね。これだったら敢えてDissolveを作成する必要はない気もします。

この辺はアートに関する話になるんでしょうか?

最後に波を斜めにします。

Custom Rotatorノードでtextureを回転させます。

f:id:kazuhironagai77:20220123232004p:plain

これは散々勉強したので今回はスキップします。

これで波のMaterialの作成は終りだそうです。

次に海と砂浜のMaterialを作成します。

<海と砂浜のMaterialを作成する>

波のMaterialのTextureを回転させる部分と同じ実装を作成して

f:id:kazuhironagai77:20220123232027p:plain

以下のイメージを作成します。

f:id:kazuhironagai77:20220123232040p:plain

Lerpノードを使用して色をつけます。

f:id:kazuhironagai77:20220123232057p:plain

更にヤシの木の下の砂の色を追加するためにRadial Gradient Exponentialノードを追加します。

f:id:kazuhironagai77:20220123232120p:plain

追加された箇所をはっきりさせるためにDensityの値を上げると以下のような結果になります。

f:id:kazuhironagai77:20220123232139p:plain

Densityを下げます。

f:id:kazuhironagai77:20220123232219p:plain

今度はここに波を追加します。

波ってさっき別のMaterialで作成したんじゃないの?と思ったんですが

さっき作成したのは波の泡の方で、今度は以下に示したような線の部分を作成するんだそうです。

f:id:kazuhironagai77:20220123232240p:plain

となると当然以下の様なImageを作成したいわけです。

f:id:kazuhironagai77:20220123232257p:plain

以下の方法で作成しました。

f:id:kazuhironagai77:20220123232315p:plain

これは見れば意味は分かりますが一か所だけ気になるのがNoiseの形状を変化させるのをRotatorの後でやっています。これTexCoord[0]、もしくはそのすぐ後で計算した場合はどうなのかと言う事です。

試してみます。

Appendを外してTexCoord[0]の値を以下のような設定にします。

f:id:kazuhironagai77:20220123232331p:plain

f:id:kazuhironagai77:20220123232342p:plain

結果です。

f:id:kazuhironagai77:20220123232406g:plain

雨が降ってるような動きになってしまいました。

これは後で検証する事にします。

f:id:kazuhironagai77:20220123232424p:plain

これを使って検証します。

この結果に

f:id:kazuhironagai77:20220123232442p:plain

を掛けます。

結果です。

f:id:kazuhironagai77:20220123232458p:plain

逆ですね。

One Minusで逆向きにします。更に波を小さくするためにPowerノードを繋ぎます。

f:id:kazuhironagai77:20220123232515p:plain

その結果と先程の結果を繋ぎます。

f:id:kazuhironagai77:20220123232534p:plain

こうなりました。

f:id:kazuhironagai77:20220123232552g:plain

取りあえずはOKとします。

以下の枠を追加します。

f:id:kazuhironagai77:20220123232616p:plain

以下の方法で作成したそうです。

f:id:kazuhironagai77:20220123232632p:plain

うーん。

天才性を感じる作り方ですね。何と言うかシンプルで単純なのに目的の形状を完璧な形で作成しています。

f:id:kazuhironagai77:20220123232648p:plain

こうなりました。

Niagaraの作成>

ここからはTutorialの解説もかなりAboutになるので推測を含めてやっていきます。

まず全部のParameterをDynamic Parameterに変更します。

f:id:kazuhironagai77:20220123232711p:plain

f:id:kazuhironagai77:20220123232720p:plain

NiagaraのEmitterをFountainから作成します。

f:id:kazuhironagai77:20220123232738p:plain

いつもの様に要らないModuleを捨てます。

更にRender SectionのSprite RendererにM_waveをセットします。

そしてParticle Update SectionにDynamic Material Parameter Moduleを追加します。

f:id:kazuhironagai77:20220123232802p:plain

TutorialのDynamic Material Parameter Moduleの設定を見ると

f:id:kazuhironagai77:20220123232840p:plain

Parameterが全然違う。

<結果>

色々自分で工夫して以下の様にしました。

f:id:kazuhironagai77:20220123233009g:plain

うーん。もっと時間をかければ良くなるのは分かりますが、この位で止めておきます。

<良く分からなかった事>

Niagara Emitterを二つ並べた時、どっちのSpriteがCameraから見て前に生成されるのかが分かりませんでした。

Niagaraの勉強としていますが、そのほとんどがMaterialの勉強になっています。もう少しNiagaraの割合が多いtutorialを探して勉強する事にします。

3.Material : Ben Cloward先生の何かを勉強する

3.1 Triplanar Projection - Shader Graph Basics - Episode 28 [2] を勉強する

今週は、Ben Cloward先生のTriplanar Projection - Shader Graph Basics - Episode 28 [2] を勉強します。

今度こそ歪みの無いProjectionの方法が学べるんでしょうか?

まず軽く全部見ました。

またUnityを主で解説しています。が今回はそれなりにUnrealでも作成方法も詳しく説明してありました。

驚きですが、歪みの無いProjectionを作るのに先週、勉強した歪んでいる箇所をMaskする方法が使用されていました。

おお。

何で、一週間も掛けて歪んでいる箇所をMaskする方法を勉強しなければならないのかと先週は思いましたが、それが伏線だったとは。

歪みの無いProjectionの作成方法ですが、簡単に説明すると、最初にx、y、z方向にそれぞれMaskして歪みを隠したProjectionを作成します。そしてそれらをLerpノードを使用して合成します。これだけでした。

その後で、それぞれのProjectionの接線の歪みを無くすための方法が紹介されています。

<実際にやってみる>

先週やった事を丸コピーしました。

f:id:kazuhironagai77:20220123233110p:plain

今、見ると結構覚えてないです。Vertex Normal WSとAbsolute World Positionを選択した理由とか覚えていません。

一応、先週の勉強内容も復習してからやった方が良いですね。復習します。

<<先週の復習>>

先週のBlogを読み直すとかなり分かり易くまとめてありました。

まず、先週のMaterialの勉強のまとめで、一番大きく書かれていたのが、歪みの無いProjectionを作成すると言っておきながら歪んでいる箇所をMaskしてるだけだったと言う事です。

f:id:kazuhironagai77:20220123233130p:plain

これがあるから今週のTutorialの内容が光って来る訳で、かなり重要な感想です。

そして次に述べているのがMaskをするためにはそのMaterialを貼るMeshのNormal Vectorが必要であると言う事です。ここで何でNormal Vectorが必要になるかについても深堀してくれれば今週、何でVertex Normal WSノードをそもそも使用しているんだっけ?と言う疑問は生まれなかったかもしれませんね。

ただし先週は、Normal VectorにVertex Normal WSノードを使用してもPixel Normal WSノードを使用してもどっちでも良いと言うTutorialの説明に基づいて、Vertex Normal WSノードとPixel Normal WSノードの違いについてかなり詳しく調査していました。Normal Mapの影響を受けないのがVertex Normal WSノードで受けるのがPixel Normal WSノードになります。

f:id:kazuhironagai77:20220123233151p:plain

f:id:kazuhironagai77:20220123233158p:plain

後はTutorialの手順を簡単にまとめているだけですね。

Normal Vectorの絶対値を計算してContractをはっきりさせるためにPowerノードを使用したりしています。その結果をx、y、z軸で分割するとその軸以外をMaskする事が出来る値を得る事が出来ます。

f:id:kazuhironagai77:20220123233213p:plain

更にx、y、z軸の境界にある黒い部分の取り方も解説してありました。それはPowerの結果と(1,1,1)でDot Productをします。先程のPowerの結果をこのDot Productで割ります。

f:id:kazuhironagai77:20220123233230p:plain

この部分の計算が今週の計算にどの程度の影響を与えるのかについては非常に興味がありますね。

<<Tri-planar Projectionの実装>>

まず以下のMaskの箇所にMask(GB)とMask(RB)を追加しました。

f:id:kazuhironagai77:20220123233251p:plain

この部分の実装が何をやっているのか忘れてしまったので確認します。

上記のTexture SampleのPreviewです。

f:id:kazuhironagai77:20220123233304p:plain

ああ、Projectionを投影する機能を担当している箇所でした。

試しにMask(RG)をMask(GB)に変更してみます。

f:id:kazuhironagai77:20220123233321p:plain

x軸方向が中心になってProjectされています。

この3つのProjectionで歪んでいない箇所をこれからBlendしていきます。

まずTutorial通りにやってその後で理屈を考えます。

Mask(GB)とMask(RB)を先程のNormal Vectorから作成した歪んだ部分をMaskする実装の最後のノードであるSplit ComponentsのGでLerpします。

f:id:kazuhironagai77:20220123233340p:plain

更にその結果とMask(RG)をSplit ComponentsノードのBでLerpします。

f:id:kazuhironagai77:20220123233358p:plain

最後にこの結果をProjectしたいTextureがセットされているTexture Sampleノードに接続して更にResult NodeのBase Colorに接続します。

f:id:kazuhironagai77:20220123233417p:plain

結果です。

f:id:kazuhironagai77:20220123233437p:plain

前よりは確実に良くはなりましたが、歪みが無いとは言えませんね。x、y、zの境界線の部分はかなり歪んで見えます。

Tutorialではこれからこの問題の解決方法についてやっていくのですが、その前に先程の実装部分の検証を行います。

<<先程の実装部分の検証>>

今、実装した3つのProjectionで歪んでいない箇所をBlendしたやり方について検証します。

まず最初にLerpを使用した2つの色の混合について確認します。

以下の実装を作成しました。

f:id:kazuhironagai77:20220123233511p:plain

結果は

f:id:kazuhironagai77:20220123233531p:plain

でした。

と言う事は値が0の時はAの色、1の時はBの色、その間はAとBのBlendした色になると言う事になります。

最初のLerpの箇所を見てみます。

f:id:kazuhironagai77:20220123233548p:plain

まずSplit ComponentsノードのGの値です。

f:id:kazuhironagai77:20220123233605p:plain

f:id:kazuhironagai77:20220123233615p:plain

Y軸方面が白、それ以外は黒になっています。

と言う事は、黒い部分はAに繋がっているMask(GB)の値が代入され、白い部分はBに繋がっているMask(RB)の値が代入されるはずです。

この状態でTexture Sample ノードにつないで出力してみると以下の様になっていました。

f:id:kazuhironagai77:20220123233636p:plain

XとY軸はそれなり歪みがない値が代入されたためこの結果に表示されているTextureもXとY軸の面はあまり歪んでいません。Z軸の面はX軸の値を使用しているので凄い歪んでいます。

次は以下の実装を行います。

f:id:kazuhironagai77:20220123233656p:plain

まずSplit ComponentのBの値を可視化した図を以下に示します。

f:id:kazuhironagai77:20220123233713p:plain

Z軸の面が白くそれ以外が黒くなっています。LerpのAが黒い部分、Bが白い部分になって混合されるので、Z軸の面以外は先程の結果がそのまま適応されます。

その結果こうなる訳ですね。

f:id:kazuhironagai77:20220123233731p:plain

分かりました。

<Projectionの接線の歪みを無くす>

<<解決策1:Normal VectorのPowerのExpの値を変更する>>

以下に示した様にNormal VectorのPowerのExpの値を8から64に増やします。

f:id:kazuhironagai77:20220123233756p:plain

すると

f:id:kazuhironagai77:20220123233812p:plain

歪みのある境界面が細くなります。

更にその後の計算で、Divide ノードとSplit Componentsノードの間にRoundノードを入れるとBinary Maskが入る事になり、

f:id:kazuhironagai77:20220123233830p:plain

以下のような結果になります。

f:id:kazuhironagai77:20220123233845p:plain

Binary Mask云々に関しては良く分かりませんが、境界面は更に小さくなっていますね。Roundノードを入れると何で境界面が小さくなるのかの理屈も良く分かりませんね。これは後で調べる事にします。

しかしこの方法だとどんなに頑張っても以下に示した様にSeam(つなぎ目)が残ってしまいます。

f:id:kazuhironagai77:20220123233903p:plain

それで次の方法です。

<<解決策2: Textureのサンプルの混合を先にする>>

Textureのサンプルの混合を先にするってどういう意味なのか分からなかったですが、Tutorialみたら分かりました。

f:id:kazuhironagai77:20220123233943p:plain

Lerpする前にTexture Sampleノードを使用しています。この方向だとそれぞれのMask毎にTextureを呼ぶ必要が出てかなりコストが高くなりますね。

それでTutorialでもCost、Costと言っていたんですね。

肝腎の結果ですが

f:id:kazuhironagai77:20220123234020p:plain

思いっきりSteamが見えています。元々のTextureがTitlingに対応していないとSeamlessな実装を組んでも駄目って事でしょうね。

これは2021-04-04のBlogで勉強したUnreal Sensei氏もそのTutorialで述べていた事ですが、MaterialのProgrammingをどんなに頑張っても、結局は元々のTextureを繰り返すだけなのでそのTextureの質以上の物は作れないと言うやつです。

別なTextureで試してみます。

Tutorialで使用しているのに近いTextureを選択しました。

f:id:kazuhironagai77:20220123234039p:plain

結果です。

f:id:kazuhironagai77:20220123234059p:plain

赤で囲った部分を見れば分かりますが、とてもSeamlessとは言えません。

Tutorialを見たらその後で、PowerのExpの値を8に戻して、Roundノードを外していました。

f:id:kazuhironagai77:20220123234426p:plain

その結果、

f:id:kazuhironagai77:20220123234443p:plain

こうなりました。これはかなりSeamlessです。Blendしている箇所は一寸ぼやけて見えますが、汚れていると思えば全然、許容範囲です。

よしそれなら俺のSmileちゃんもSeamlessになるかもと試したら

f:id:kazuhironagai77:20220123234506p:plain

とんでもない結果になってしまいました。

やっぱりblendしている箇所が駄目ですね。

これでTutorialは終わりでした。

3.2 Materialについてのまとめ

Materialを勉強して来て、勉強した範囲においてですが、何が重要なのかが少しだけ見えて来ました。

その重要な事の一つに「TexCoord[0]ノードを利用して色々な形のGradientを作成する。」があります。

それについて今まで勉強した内容をここにまとめて直します。

<枠抜きのGradient

f:id:kazuhironagai77:20220123234538p:plain

実装方法:

f:id:kazuhironagai77:20220123234557p:plain

<円形のGradient

f:id:kazuhironagai77:20220123234615p:plain

実装方法1: Radial Gradient Exponentialノードを使用する。

f:id:kazuhironagai77:20220123234632p:plain

実装方法2:自分で一から作成する

f:id:kazuhironagai77:20220123234650p:plain

<三角形のGradient

f:id:kazuhironagai77:20220123234709p:plain

実装方法:

2021-12-06のBlogで勉強したんですが複雑過ぎて今見ただけじゃ何やっているのか分かりません。

後でまとめる事にします。

この分野はこれから勉強していくにつれて増えていく事になります。順次足していこうと思います。

4.Prologueの作成

4.1 紙芝居の作成

兎に角、絵が無いと話が出来ません。

先週の続きを作ります。

f:id:kazuhironagai77:20220123234738p:plain

借り絵ですが以下の様に作成しました。

f:id:kazuhironagai77:20220123234758p:plain

銀河鉄道の夜を読み終わって僕は激怒しました。

何で親友のカムパネルラが死ななくちゃいけないんだ。

頭に来た僕は散歩に出かけました。

そしたら山の上に神社を見つけました。

折角なのでその神社まで行って

「神様、何でカムパネルラが死ななくちゃいけないんでしょうか?せめて物語の最後で生き返らせて下さい。」

とお願いしました。

f:id:kazuhironagai77:20220123234827p:plain

そしたらその夜、へんな夢を見ました。

神様の使いがやって来てこう言いました。

「本当にカムパネルラを助けたいなら明日の夜、もう一回神社に来なさい」

f:id:kazuhironagai77:20220123234849p:plain

次の日の夜

その夢の話がどうしても気になったので

僕はおばあちゃんに内緒で、夜、おばあちゃんの家を抜け出して神社に行きました。

f:id:kazuhironagai77:20220123234912p:plain

神社に着くと人魂が現れました。

僕は怖くて泣きそうになりました。

そしたら昨日の夢に出て来た神様の使いが本当に目の前に現れてこう言いました。

本当に来たのかい。勇気があるね。

奥に神様が待っているよ。

f:id:kazuhironagai77:20220123234934p:plain

僕が神社の奥に行くと、

綺麗なお姉さんが居ました。

そしてその奥に電車が止まっていました。

お姉さんが言いました。

「よく来ましたね。君がカムパネルラ君を助けたい少年ですね。

カムパネルラ君を助けるためには、金の切符が必要です。

金の切符は幻想界にしか存在しません。

貴方はこの銀河鉄道に乗って幻想界に行くのです。そして金の切符を見つけて下さい!

そうすればカムパネルラ君は助かります。」

もう少し推敲が必要ですが、こんな感じでPrologueを作成します。

5.Game DesignポケモンHxHの念能力( 戦闘システムの作成)

5.1 Drag and Dropの続きを作成する

先週、Drag and Dropを作成していましたがその中でProgramming以外に作らなければならない箇所が沢山あります。それをやっていきます。

<召喚出来る全Monsterのイラストを作成する>

128x128で以下のMonsterのイラストを作成します。

f:id:kazuhironagai77:20220123235020p:plain

しました。

以下に示します。

f:id:kazuhironagai77:20220123235033p:plain

f:id:kazuhironagai77:20220123235040p:plain

<Monsterクラスを作成する>

Monster Parameterに先程作成したImageを追加します。

f:id:kazuhironagai77:20220123235117p:plain

f:id:kazuhironagai77:20220123235129p:plain

このData Tableの要素を変数として持つMonster クラスを作成します。

取りあえずActorクラスから作成しました。

f:id:kazuhironagai77:20220123235159p:plain

f:id:kazuhironagai77:20220123235209p:plain

Skeletal meshを動かすためにはAnim Classも必要でした。

f:id:kazuhironagai77:20220123235255p:plain

<Draggable Monster Widgetを改良する>

よくよく考えたらMonster BPを作成する前に以下のImageを示しているWidgetにMonsterの情報を追加する必要がありました。

f:id:kazuhironagai77:20220123235318p:plain

Draggable Monster Widgetに変数を追加する

f:id:kazuhironagai77:20220123235333p:plain

以下の変数を足しました。

f:id:kazuhironagai77:20220123235349p:plain

Row NameがあればMonster Data Tableから先程作成したMonsterクラスを生成出来るはずです。

<<ActorのPrefixについて>>

ああ、突然思い出しました。Naming Conventionでした。

ActorのPrefixが分からないので調べようと思ったんですが、それらの一般名を何て言うのかが思い出せなかったです。

公式のDocumentであるRecommended Asset Naming Conventions [3] によると

f:id:kazuhironagai77:20220123235412p:plain

でした。直します。

f:id:kazuhironagai77:20220123235425p:plain

念のためにWidgetも確認したらWBP_だそうです。

f:id:kazuhironagai77:20220123235444p:plain

うーん。こんなの使ってる人見た事ないです。

でも一応直しておきます。

f:id:kazuhironagai77:20220123235459p:plain

f:id:kazuhironagai77:20220123235504p:plain

因みにNiagara SystemのPrefixは

f:id:kazuhironagai77:20220123235527p:plain

となっていました。正直このPrefixを使用しているTutorialは見た事ないですね。でも公式のConventionなんで、いきなりこれ使用しろって強制してくる可能性もある訳で、ここは敢えて公式のConventionにそろえておくのも有りかもしれません。

あれ。

今、見直したらActorじゃなくてActor ComponentがAC_でした。

f:id:kazuhironagai77:20220123235550p:plain

何でActorのPrefixが載ってないの?

もう一回調べ直したら、こっち(Coding Standard [4] )にありました。

AActorのPrefixはAでした。

f:id:kazuhironagai77:20220123235604p:plain

でもこれはUEC++のNaming Conventionですね。

BPの場合はどうなんでしょう。

一応A_に戻しました。

f:id:kazuhironagai77:20220123235618p:plain

でもこれだとActorなのかA Monsterなのか曖昧ですよね。

<Draggable Monster Widgetを動的に生成する>

WBP_PlayerSummonに

f:id:kazuhironagai77:20220123235645p:plain

f:id:kazuhironagai77:20220123235654p:plain

以下の実装を追加します。

f:id:kazuhironagai77:20220123235719p:plain

Make Brush From Textureノードが何故かTexture 2Dしか受け付けないのでMonster ParameterのImageのTypeをTextureからTexture 2Dに変更しました。正直TextureとSlate Brushの違いも良く分かっていません。この辺りは何時かしっかり調べます。

テストします。

f:id:kazuhironagai77:20220123235735p:plain

動的に生成されたDraggable Monster Widgetが召喚出来るMonster一覧に普通に表示されています。

f:id:kazuhironagai77:20220123235757p:plain

f:id:kazuhironagai77:20220123235809p:plain

試しにMonsterを選んでDrag and Dropで紫のWidgetにセットしてみました。

f:id:kazuhironagai77:20220123235829p:plain

<Drag to WidgetDrop されたDraggable Monster Widgetの情報を保持させる>

先週、あれだけPayloadは使わないと言っていたんですがPayload使って作成しました。

WBP_DropToノードの

f:id:kazuhironagai77:20220123235901p:plain

On Drop() 関数を以下の様に書き変えました。

f:id:kazuhironagai77:20220123235918p:plain

勿論、WBP_DraggableMonsterの

f:id:kazuhironagai77:20220123235932p:plain

On Drag Detected()関数も以下の様に変更しました。

f:id:kazuhironagai77:20220123235950p:plain

テストします。

WBP_DropToノードのOn Drop() 関数に以下の実装を足して

f:id:kazuhironagai77:20220124000006p:plain

WBP_DropToノードが実際にWBP_DraggableMonsterの情報を持っているのか確認します。

結果です。

f:id:kazuhironagai77:20220124000031p:plain

WBP_DropToにWBP_DraggableMonsterをDropしたらWBP_DraggableMonsterの情報をPrint しました。

はい。

出来ています。

<Game Mode にSummonするMonsterを保持する変数を作成する>

当然ですが、召喚するMonsterのDataをWBP_DropTo以外の何処かに保持する必要があります。今回はGame Modeに保持させます。

このProjectのGame Mode BaseであるMy Game Mode Baseに

f:id:kazuhironagai77:20220124000101p:plain

以下の3つの変数を追加しました。

f:id:kazuhironagai77:20220124000118p:plain

WBP_PlayerSummonウィジェット

f:id:kazuhironagai77:20220124000136p:plain

決定ボタンを押したら

f:id:kazuhironagai77:20220124000153p:plain

My Game Mode Baseの先程の3つの変数にWBP_DropToウィジェットのDraggable Monster WidgetのRow Nameの値をAssignします。

f:id:kazuhironagai77:20220124000210p:plain

テストします。

確認のためにMy Game Base クラスのEvent

f:id:kazuhironagai77:20220124000230p:plain

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

f:id:kazuhironagai77:20220124000247p:plain

テスト開始です。

分かり易いようにRow Name が最初の3つを召喚する事にしました。

f:id:kazuhironagai77:20220124000303p:plain

結果です。

f:id:kazuhironagai77:20220124000321p:plain

それぞれのMonsterのRow NameがPrintされました。出来ています。

これでMonsterを召喚出来る下地が出来ました。

<Monsterを召喚するEventを作成する>

My Game Mode BaseにMonsterを召喚するEventを作成します。

今までのEvent Player SummonとEvent Monster Summon をEvent Player Summon Decide とEvent Monster Summon Decideに変更します。

f:id:kazuhironagai77:20220124000344p:plain

次にEvent Player Summon Action とEvent Monster Summon Actionを作成します。

f:id:kazuhironagai77:20220124000403p:plain

このEventでMonsterを召喚します。

と言っても実際にMonsterを作成するのはLevel BPなので、Event DispatcherでEvent to Levelを作成して

f:id:kazuhironagai77:20220124000433p:plain

Level BP内でこれに実装をBindする事にします。

Game Mode内のEvent Player Summon ActionはEvent DispatcherであるEvent to Levelを呼ぶだけです。

f:id:kazuhironagai77:20220124000452p:plain

次にLevel BP内のEvent Begin Playで今作成したEvent to LevelをEvent Player’s MonsterにBindします。

f:id:kazuhironagai77:20220124000509p:plain

Event Spawn Player’s Monsterでは以下の実装を実行する事でMonsterを召喚します。

f:id:kazuhironagai77:20220124000528p:plain

取りあえず左端のマスにだけMonsterが召喚出来るようにしました。

テストします。

Event Player Summon ActionをWBP_PlayerSummonウィジェットとWBP_MonsterSummonウィジェットの適当な場所にセットします。

f:id:kazuhironagai77:20220124000544p:plain

f:id:kazuhironagai77:20220124000553p:plain

f:id:kazuhironagai77:20220124000602p:plain

f:id:kazuhironagai77:20220124000609p:plain

結果です。

まず召喚するMonsterをセットしました。

f:id:kazuhironagai77:20220124000624p:plain

左端のマスにMonsterが召喚されました。

f:id:kazuhironagai77:20220124000647p:plain

向きがオカシイですね。直します。

f:id:kazuhironagai77:20220124000715p:plain

残り2体のMonsterも召喚出来るようにします。

先程の実装を関数化して以下のように実装しました。

f:id:kazuhironagai77:20220124000743p:plain

結果です。

f:id:kazuhironagai77:20220124000800p:plain

Monsterが3体召喚されています。

他のMonsterも試してみます。

f:id:kazuhironagai77:20220124000839p:plain

亡霊族のMonsterを召喚したら全員左を向いて召喚されてしまいました。

他のMonsterも試してみます。

f:id:kazuhironagai77:20220124000907p:plain

ゾンビ族は大丈夫でした。

ドラゴン族は

f:id:kazuhironagai77:20220124000932p:plain

向きは大丈夫ですが大きさに問題があります。

<Monsterの種族によって向きやサイズを変更する>

直しました。

結果です。

亡霊族を召喚しても前を向いています。

f:id:kazuhironagai77:20220124001005p:plain

ドラゴン族を召喚した場合はサイズが小さくなります。

f:id:kazuhironagai77:20220124001023p:plain

実装方法ですが、

f:id:kazuhironagai77:20220124001042p:plain

単にSwitchでそれぞれのMonsterに応じてSpawnする向きやサイズを変えただけです。

<見やすいように少し改良>

以下の様になりました。

f:id:kazuhironagai77:20220124001105p:plain

f:id:kazuhironagai77:20220124001118p:plain

f:id:kazuhironagai77:20220124001132p:plain

ここまでは一緒です。

ここから少しだけ変わりました。

f:id:kazuhironagai77:20220124001158p:plain

召喚するMonsterを選択します。

f:id:kazuhironagai77:20220124001223p:plain

決定を押すと直ぐにMonsterを召喚します。

f:id:kazuhironagai77:20220124001247p:plain

この後、敵の魔術師のターンになります。

敵の魔術師がMonsterを召喚する実装はまだ作成していません。

この後、お互いのMonsterが戦闘を始めます。

f:id:kazuhironagai77:20220124001313p:plain

負けました。

5.2 敵の魔術師がMonsterを召喚する

今週は時間が余ったので、更に敵の魔術師のMonster召喚も実装します。

まずGame Modeに敵の魔術師のMonsterの名前を保持する変数を作成します。

f:id:kazuhironagai77:20220124001340p:plain

WBP Monster Summon Decideウィジェット内で召喚するMonsterを決定します。

f:id:kazuhironagai77:20220124001425p:plain

名前が無茶苦茶です。

召喚されるのがMonsterなのに敵の名前がMonsterになっています。後で整理します。

まずMonster Summon Decideウィジェット

f:id:kazuhironagai77:20220124001442p:plain

召喚するMonsterを決定します。

f:id:kazuhironagai77:20220124001506p:plain

Choose Monster関数の中身はこんな感じで完全にRandomでMonsterを選んでいます。

f:id:kazuhironagai77:20220124001521p:plain

Monster Summon DecideウィジェットのEvent Begin Playの最後でGame ModeのEvent Monster Summon Actionを呼びます。

f:id:kazuhironagai77:20220124001536p:plain

Event Monster Summon Actionでは以下の実行を行います。

f:id:kazuhironagai77:20220124001556p:plain

まずWidget、WBP Monster Summon Playを作成し、表示します。

f:id:kazuhironagai77:20220124001610p:plain

f:id:kazuhironagai77:20220124001619p:plain

その後でEvent To Level Opponentを呼びます。

Event dispatcherである To Level OpponentはLevel BP内でBindされ

f:id:kazuhironagai77:20220124001639p:plain

以下のEventを実行します。

f:id:kazuhironagai77:20220124001657p:plain

ここではMonster Summon Decideウィジェットで決定したMonsterを所定の位置に召喚します。

大体の流れはこんな感じです。

テストします。

f:id:kazuhironagai77:20220124001711p:plain

敵の魔術師もMonsterの召喚に成功しました。

上のScreenshotは分かりにくいので、敵が先攻の時のScreenshotを上げておきます。

f:id:kazuhironagai77:20220124001729p:plain

Monsterの召喚に成功しています。

<Monsterの召喚結果を見てから召喚するMonsterが決められる件について>

大変なバグに気が付いたんですが、後攻が先攻のMonsterの召喚結果を見てから、召喚するMonsterが決められます。これだと後出しジャンケンになってしまいます。

最初の召喚は見えない様に、黒い霧か何かにする必要があります。

後で直します。

6. NPCAIを作成する

6.1 不自然な伸びについての検証

のっけからちょっと別な話をしますがSkeleton Assets: Importing, Sharing Skeletons & Anims | 02 | [5] で同じSkeletonを使用しているがサイズが著しく違うキャラの場合の調整方法を教えています。

f:id:kazuhironagai77:20220124001804p:plain

かなり昔にこのTutorialを勉強したんですが、その時既に、サイズが違うけど同じSkeletonを使用しているSkeletal Meshのモデルは配布終了になってしまっていました(上記のScreenshotの右側)。それで実際に試す事が出来ずに見るだけで終わってしまいました。

先週、NPCに伸びをするAnimationを追加しましたが、腕が異常な長さで伸びる現象が見られました。

f:id:kazuhironagai77:20220124001825p:plain

これら以上な腕の伸びがみられるNPCは元のキャラのScaleを不等な比率で変更したキャラばかりです。

f:id:kazuhironagai77:20220124001851p:plain

それで逆説的にですが、サイズを不等な比率で変更すればSkeleton Assets: Importing, Sharing Skeletons & Anims | 02 | [5]で教えている同じSkeletonを使用しているがサイズが著しく違うキャラの場合の調整方法を勉強出来るんじゃないのかと思いました。

のでそれを試してみます。

まず普通のサイズでAnim_Body_Stretch_1を試してみます。

f:id:kazuhironagai77:20220124001907p:plain

以下の結果になりました。

f:id:kazuhironagai77:20220124001926p:plain

別にオカシクはないです。

次に

f:id:kazuhironagai77:20220124002104p:plain

のScaleに変更したMannequinで

f:id:kazuhironagai77:20220124002123p:plain

Anim_Body_Stretch_1を試してみます。

f:id:kazuhironagai77:20220124002141p:plain

横に伸びた瞬間に腕が3倍位の長さになりました。

うーん。ひょっとするとこれはAnimationの後でScalingを掛けているからこうなっているのかもしれませんね。

となるとSkeleton Assets: Importing, Sharing Skeletons & Anims | 02 | [5]で教えている同じSkeletonを使用しているがサイズが著しく違うキャラの場合の調整方法とは別な問題で、単にScalingをする時は等倍でしなければならないだけですね。

でも何処かでAnimationの前にScalingする事が出来れば良い訳でそれをちょっと調べます。

Third Person CharacterのMeshからScalingしてみました。

f:id:kazuhironagai77:20220124002204p:plain

f:id:kazuhironagai77:20220124002214p:plain

結果です。

普段のサイズです。

f:id:kazuhironagai77:20220124002239p:plain

足の長さが4倍位になっています。

f:id:kazuhironagai77:20220124002302p:plain

この方法は駄目ですね。

SK_Mannequinを直接調べたんですが

f:id:kazuhironagai77:20220124002319p:plain

該当するParameterはなさそうです。

f:id:kazuhironagai77:20220124002340p:plain

となるとUEにImportする前にScaleを変更する必要があると言う事になりますね。

今回はScalingは等倍に留めておきます。

先程のNPCも等倍に変更したら伸びの時に腕が三倍位長くなる現象が無くなりました。

f:id:kazuhironagai77:20220124002415p:plain

はい。以上です。

6.2 Texture Streaming Poolについての検証

先週、突然以下の警告が現れたんですが、

f:id:kazuhironagai77:20220124002435p:plain

何故か、今週は現れません。完全に消えてしまっています。

先週見つけたTexture Streaming Pool に関する記事Fixing ‘Texture Streaming Pool Over Budget’ in UE4 [6]だけ読んでおく事にします。

この記事によると元々UEではTextureに使用出来るMemoryのサイズが決まっているそうです。そのサイズを超えた量のTextureを使用するとこの警告が現れるそうです。そして使用しているTextureのサイズは自動で小さくなるそうです。

となると当然ですが解決策は2つあります。

  • Texture用に使用出来るMemoryのサイズを大きくする。
  • Level上で使用しているAssetをもっと最適化してその結果Textureの使用量を少なくする

です。

更に、この問題の本質を理解するためにはUEではどうやってTextureをStreamしているかを理解する必要があると述べています。そしてUEにおけるTexture Streamingの話に続きます。

興味深いので続きも読んでおきます。

まず大前提として普通にLevel DesignしたらTextureは沢山使用します。

そりゃそうでしょう。

LandscapeでもMaterialは使用しますし、その上に配置する全てのAssetもそれぞれ独自のMaterialを使用します。

でもそれをそのままにしておくと直ぐにTextureの使用限界に達してしまうそうです。

そこでUEのTexture Streamingでは2つの方法を採用する事でLevel 上で沢山のTextureを使用してもTextureの使用限界に達しない様に工夫されています。その2つの方法とは

  • Runtime中に動的にTextureloadする
  • Mipmappingを使用する

です。

これはまあ当然と言えば当然でそんなに凄い話ではないですね。UEがこの技術を採用した当時は凄かったんでしょうか?

これらは自動でBuildされますが、以下に示したBuild Texture Streamingを選択する事で手動で行う事も出来るそうです。

f:id:kazuhironagai77:20220124002518p:plain

これ位は別に特別な話として解説されなくても知ってるよ。と思ったらこの話は次の解説をするための前提知識の確認でした。

次にTexture Streaming Poolについて解説しています。

まずTexture Streaming Poolとは、このStreaming SystemによってDrawされた全てのTextureが収まっている仮想のContainerだそうです。仮想である所がPointですね。実際はどのMemoryに収まっているのかは決まっていないんですね。

書いた人の知識と理解の凄さがこういう簡単な文から滲み出てくるんです。結構凄い人なのかもしれませんね。

コマンドのstat streamingで、このStreaming SystemがどうやってMemory内で分割されているのかが見れるそうです。

試しにやってみたら以下の表示が見られました。

f:id:kazuhironagai77:20220124002542p:plain

次の節ではTexture Streaming Poolの警告が出た時の直し方について解説しています。当然読みます。

<解決策1:  Texture用に使用出来るMemoryのサイズを大きくする>

まずその場だけ直す方法です。

以下のCommandでサイズを指定します。

f:id:kazuhironagai77:20220124002604p:plain

Defaultでは1000だそうです。

次にそのProjectで常に使用出来るサイズを大きくする方法です。

DefaultEngine.ini の[/Script/Engine.RendererSettings]にr.Streaming.PoolSize =で指定します。

f:id:kazuhironagai77:20220124002623p:plain

だそうです。

<解決策2: Textureのサイズを小さくする>

TextureのCompressionにあるMaximum Texture Sizeに最大値を指定します。

f:id:kazuhironagai77:20220124002649p:plain

注意書きですがこの値は必ず2の乗数である必要があるそうです。言われてみればそうですね。

最後にどの程度のPoolが必要なのかの調査の方法についての解説が載っていましたが、この記事を書いた人も別な人から引用していたので、その方法をここで書くと引用の引用と言う結構面倒な事になるので端折ります。

以上です。

最後に記事を書いた人の紹介が載っていました。

f:id:kazuhironagai77:20220124002707p:plain

大変分かり易かったです。この人の記事が検索で引っかかった時は優先して読む事にします。

6.3 またLevel2_5 がSave出来なくなりました。

f:id:kazuhironagai77:20220124002733p:plain

うーん。イラつく。

ただ今回は理由は何となくわかっています。先程、以下のNPCのサイズを直しましたが

f:id:kazuhironagai77:20220124002752p:plain

それをSaveした時、このNPCが存在している

f:id:kazuhironagai77:20220124002810p:plain

を選択していないで

f:id:kazuhironagai77:20220124002826p:plain

を選択したままでした。

これでSave出来ないErrorが発生したんだと思います。

一回こいつを消してしまいましょう。

一応、設定だけ残しておきます。

f:id:kazuhironagai77:20220124002844p:plain

駄目でした。

f:id:kazuhironagai77:20220124002901p:plain

前、どうやって直したのか調べます。

2021-11-14のBlogで直していました。

前回の時と症状が違うのが、前回はSaveの印が出ないんですがSaveは出来ました。今回はSaveも出来ないですね。

前回やった方法は、Sub LevelのActorを全部Persistent Levelに移して戻すと言う方法でした。

今回はこれをやるためのSaveが出来ません。前の状態に戻ってしまいます。

うーん。PCを再起動してみます。

再起動した後でもう一回このProjectを開いてみます。

やっぱりSave出来ません。

あれ、UE Editorが2個開いています。

全部閉じてもう一回開き直しました。

今度はLevel2_5を選択した後に

NPCのサイズを変更して

f:id:kazuhironagai77:20220124002920p:plain

f:id:kazuhironagai77:20220124002931p:plain

Saveしてみました。

f:id:kazuhironagai77:20220124002950p:plain

あれ。出来ている。

Game画面でもNPCのサイズが変更されています。

f:id:kazuhironagai77:20220124003007p:plain

一端、U4のEditorを閉じて再度、このProjectを開いて見ました。

f:id:kazuhironagai77:20220124003026p:plain

NPCのScaleは変更した値を保持しています。

何か直っていますね。

以下に考えられる原因をまとめておきます。

<原因1>

単にUE4 Editorが二つ開いていた。同じProjectを二つ開いているとSave出来なくなります。これってTaskで確認すると実際は表示されていないのに2つ開いている場合があります。普段はSave出来ないとすぐTaskから調べるんですが、今回はその事をすっかり忘れていました。

<原因2>

やっぱりSub Levelに存在しているActorの設定を変更した時に、Persistent Levelを選択したままSaveした事だと思います。

Save出来た時は、NPCが存在しているSub Levelをわざわざ選択してSaveしました。

これら以外の原因も考えられるかもしれませんが、次にSave出来ない事態が起きた時はこの2つをまずチェックする事にします。

6.4 全部のNPCを動的に生成する

災い転じて福となす。じゃないですが、先週作成した動的にNPCを生成するための実装ですが、これはPersistent LevelじゃなくてNPCが発生する場所のSub Levelに作成する必要がありました。

f:id:kazuhironagai77:20220124003110p:plain

<Level2_5NPCだけ動的に生成してみる>

試しにLevel2_5のNPCである

f:id:kazuhironagai77:20220124003131p:plain

をLevel2_5のLevel BPから動的に生成してみます。

まず、Level2_5のSub Levelで生成するNPCのData TableをGame Instance BP内に作成します。

f:id:kazuhironagai77:20220124003149p:plain

中のDataはこれ一個だけです。

f:id:kazuhironagai77:20220124003205p:plain

それで先週作成した以下の実装と同じ物をそれぞれのSub Levelに作成すると

f:id:kazuhironagai77:20220124003229p:plain

全てのSub LevelでTick() 関数を使用しないといけなくなるのでEvent で管理したいです。

以下の方法で試してみます。

まずGame Mode BP内にEvent DispatcherであるSunriseとSunsetを作成します。

f:id:kazuhironagai77:20220124003243p:plain

これをMap 1 のLevel BPから以下の様に呼び出します。

f:id:kazuhironagai77:20220124003300p:plain

このDespatcher EventはそれぞれのSub Level内でBindします。

Level2_5のBPです。テストなので以下のような実装にしています。

f:id:kazuhironagai77:20220124003318p:plain

結果です。

朝になりました。

f:id:kazuhironagai77:20220124003330p:plain

夜になりました。

f:id:kazuhironagai77:20220124003345p:plain

出来てます。ここにNPC SpawnとNPC killの実装を行います。

Event NPC Spawnの実装です。

f:id:kazuhironagai77:20220124003358p:plain

まずClearノードですがArrayそのものは綺麗にしますが、そのArray内の要素がさしているそれぞれのObjectはそのままだった気がします。

ただ日没の時点でArray、Spawn NPCはその要素のObjectは全部Destroyしているのでここでそんなに真剣にCheckする必要はないと思いそのままにしておきました。

Spawn NPC関数ですが

f:id:kazuhironagai77:20220124003413p:plain

Spawn Actor From Classノードを使用しているためBlueprint Function Libraryを使用しての共有化は出来ません。のでSub Level毎に実装する事にします。

Event NPC Killの実装です。

f:id:kazuhironagai77:20220124003425p:plain

こっちは完全にActorをDestroyしています。夜になったらNPCは直ぐに消える必要があるからです。

テストします。

静的に配置されていたNPCを消しました。

f:id:kazuhironagai77:20220124003440p:plain

Saveし直します。

Level2_5も普通にSave出来ました。

f:id:kazuhironagai77:20220124003503p:plain

これでテストします。

はい。NPCですが普通に動的に生成されました。

f:id:kazuhironagai77:20220124003524p:plain

話しかけると普通に会話に入ります。

f:id:kazuhironagai77:20220124003543p:plain

散歩も伸びも普通にしています。

f:id:kazuhironagai77:20220124003603p:plain

夜になったらNPCは消えました。

f:id:kazuhironagai77:20220124003617p:plain

出来ています。

これと全く同じ仕組みを他のSub Levelでも作成します。

<残りのNPCも動的に生成する>

まずWorld Outlinerで静的に配置されている全てのNPC_Personを調べます。

f:id:kazuhironagai77:20220124003636p:plain

このDataを元にGame Instance BP内にそれぞれのSub LevelにおけるNPC Spawn Dataを作成します。

<<Level2_7NPCの動的な作成>>

Level2_7のNPCのData Tableが出来ました。

f:id:kazuhironagai77:20220124003657p:plain

f:id:kazuhironagai77:20220124003706p:plain

f:id:kazuhironagai77:20220124003718p:plain

一辺に全部作成すると何処かで失敗した時に分からないくなるのでこれだけ先に作る事にします。

のでLevel2_7のNPCを動的に生成するための実装を作ります。

f:id:kazuhironagai77:20220124003737p:plain

読み込むData tableがNPC Spawn Data Level 2_7以外は全く同じ実装です。

テストします。

Sub Level 2_7に配置されているNPCを全部消しました。

f:id:kazuhironagai77:20220124003751p:plain

全員は確認出来ませんでしたが生成されているみたいです。

f:id:kazuhironagai77:20220124003810p:plain

次の日の朝になったら4人ともSpawnしました。確認出来なかった詩人もSpawnしました。

f:id:kazuhironagai77:20220124003836p:plain

出来ています。

残りもやっていきます。

<<Level2_8>>

やっている内容は全く同じなので結果だけ示します。

f:id:kazuhironagai77:20220124003858p:plain

f:id:kazuhironagai77:20220124003912p:plain

<<Level3_7とLevel3_8>>

出来ました。結果も全員確認しました。

6.5 NPCによっては歩き回らない。夜でも存在するなどの選択が出来る様にする

これは来週以降考えます。

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

7.UE5Naniteの勉強

今週はYouTubeに出て来たNaniteのTutorialを片っ端から見て来週からどれで勉強するのかを決めます。

Unreal Engine 5 Tutorial - Create Nanite Meshes [7]

AIのTutorialで散々お世話になったRyan Laley氏が作成したTutorialです。

普通のStatic MeshをNaniteのMeshに変換する方法が説明されているみたいです。

最初の3分位、見ただけですが分かり易く解説していてこれは勉強しておいて損はないでしょう。

Unreal Engine 5 - Nanite Tutorial [8]

Beardgames氏のTutorialです。この人のTutorialは始めてみます。

f:id:kazuhironagai77:20220124003935p:plain

初めて見ますが、再生回数に対しての評価がかなり高いのでひょっとすると良いTutorialかもしれません。

最初の1分間だけ見ました。

かなりまとまって分かり易くNaniteの特徴について解説していました。

コメント欄にも

f:id:kazuhironagai77:20220124004019p:plain

短くはっきりとまとまっていると書かれています。

全体で3分程度なのでこれも見ておく事にします。

unreal engine 5 how to enable nanite [9]

JSFILMZ氏のTutorialです。この人はUEを使って映画を作る事を目的にしたTutorialを作成しているそうです。

最初の一分間だけ見ました。まあNaniteの一般的な特徴について解説していました。見てて負担になる作りではないし軽く見る事にします。」

Nanite: Everything You Should Know [Unreal Engine 5] [10]

William Faucher氏のTutorialです。

最初だけちょっと見ましたが、これもNaniteの一般的な特徴についての解説のようです。内容は良くまとまっているようなので一応見ておく事にします。

以上です。

<まとめと感想>

どれもNaniteの一般的な解説であまり差がないですね。

どうせならNaniteでStatic Meshを100万個、作成する方法みたいなもう少し実践的なのがあるとうれしかったです。

8.まとめと感想

今週は結構進みました。時間的にも余裕がある状態で作業が出来ました。

Niagaraの勉強>

今週もCGHOW氏のTutorialを勉強しましたが、特に問題なく理解出来ました。最初の頃はCGHOW氏のTutorialが何を言っているのか全く分からなかった事から考えるとかなり成長しています。ただMaterialの勉強に偏り過ぎている気はするのでこれからはNiagaraが主のTutorialを選ぼうと思います。

<Materialの勉強>

Ben Cloward先生のProjectionシリーズがこんなに深い内容に発展していくとは思ってもいませんでした。これは全部、勉強したらまとめ直します。

後、Materialの作成において必要な知識が段々分かってきたのでそれについてもまとめたいと思います。

<Prologueの作成>

やっとRPGの話の部分がしっかりして来ました。

隠れたテーマとは何かとそれをどうやって物語に組み込むかが物語の作成の肝だったんです。

来週、紙芝居の部分だけ実装してみます。

Widget上で作成するとAnimationの追加などが出来るのでもう少し工夫が出来ると思うからです。その後でBack Ground Imageの数をどうするかとかCharacterは別のImageで制作して動かすとかを考えようと思います。

<Game Designポケモン+HxHの念能力( 戦闘システムの作成)>

今週は、Monsterの召喚まで実装しました。

これ実装した後、思ったんですが単なるグー、チョキ、パーのカードを並べるだけでも機能的には一緒でした。

Prototypeの作成なので無駄な実装は極限まで省くべきで、そういう面からもGame Planningの役割は実際には大切だと思いました。

NPCのAIを作成する>

町のNPCの生成方法はもう少し改善する必要がありますね。

  • Playerが操作するキャラが町に入っていないのに町人のNPCは既に生成されています。
  • 動的にPlayerを生成する(Create Actor From Class ノードを使用する必要がある)ため全てのSub Levelにほぼ同じ実装を書く必要がありました。

World Partitionは理論的には素晴らしい方法です。しかし私が作成したような小さいMapではその真価は発揮できないみたいです。

<UE5:Naniteの勉強>

今週は軽くTutorialを調べるだけにしました。特に凄いTutorialは見つかりませんでした。でも一応、操作の基本などについては学べそうです。

以上です。

9.参照(Reference

[1] CGHOW. (2021, October 16). Wave in UE5 Niagara Tutorial | Download Files [Video]. YouTube. https://www.youtube.com/watch?v=ZoTIWIinWQc

[2] Cloward, B. [Ben Cloward]. (2021, December 23). Triplanar Projection - Shader Graph Basics - Episode 28 [Video]. YouTube. https://www.youtube.com/watch?v=sjpszGetM40&list=PL78XDi0TS4lEBWa2Hpzg2SRC5njCcKydl&index=28

[3] Epic Games. (n.d.-b). Recommended Asset Naming Conventions. Unreal Engine Documentation. Retrieved January 23, 2022, from https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/AssetNaming/

[4] Epic Games. (n.d.-a). Coding Standard. Unreal Engine Documentation. Retrieved January 23, 2022, from https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/DevelopmentSetup/CodingStandard/

[5] Epic Games [Unreal Engine]. (2015, September 28). Skeleton Assets: Importing, Sharing Skeletons & Anims | 02 | v4.8 Tutorial Series | Unreal Engine [Video]. YouTube. https://www.youtube.com/watch?v=JkcJ5bjGPsg

[6] Mower, N. (2021, November 21). Fixing ‘Texture Streaming Pool Over Budget’ in UE4. Techarthub. Retrieved January 23, 2022, from https://www.techarthub.com/fixing-texture-streaming-pool-over-budget-in-ue4/

[7] Laley, R. [Ryan Laley]. (2021, June 1). Unreal Engine 5 Tutorial - Create Nanite Meshes [Video]. YouTube. https://www.youtube.com/watch?v=YucYfUbazKY

[8] Beardgames. (2021, May 27). Unreal Engine 5 - Nanite Tutorial [Video]. YouTube. https://www.youtube.com/watch?v=s7PVCWOQaII

[9] JSFILMZ. (2021, May 26). unreal engine 5 how to enable nanite [Video]. YouTube. https://www.youtube.com/watch?v=wHp8e84je7U

[10] Faucher, W. [William Faucher]. (2021, June 15). Nanite: Everything You Should Know [Unreal Engine 5] [Video]. YouTube. https://www.youtube.com/watch?v=P65cADzsP8Q