Niagaraのモジュールスクリプトをカスタマイズしてみよう！

[Unreal Engine (UE) Advent Calender 2023](https://qiita.com/advent-calendar/2023/ue) シリーズ3　18日目の記事を書かせて頂きます。Qiita初投稿になるので、温かい目で見て頂けると嬉しいです。




## 自己紹介
まずは簡単な自己紹介
元映像業界でエフェクト制作に携わってましたが、リアルタイム表現に魅力を感じゲーム業界に転身しました。制作業務を行う傍らディレクション業務やマネジメント、技術サポート等色々やったりしています。

また CGWORLD Online Tutorial から初心者・中級者向けのNiagara教材を出しておりますので、ご興味がありましたらチェックして頂けると嬉しいです。

[UE4のNiagaraを使用したエフェクト制作講座](https://tutorials.cgworld.jp/set/1396?) 
（モジュールスクリプトについても解説しています）




## 本題
さて今回私からは「Niagaraのモジュールスクリプトをカスタマイズしてみよう！」という事で、以下２つを例に解説していきます。

#### ・親の進行方向に沿った角度でデカールを貼る　　
![01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/e7fd6656-71cf-bf67-62a5-de893400953b.gif)


#### ・マテリアルの模様でパーティクルの色と位置を変化させる
![01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/e37eda0b-33d7-0307-b417-5314a4fc0532.gif)


## モジュールスクリプトって何？
解説に入る前に「モジュールスクリプト」について簡単に説明します。
モジュールスクリプトとはパーティクルが持っている情報（位置とか大きさ等）に対して変化を与える仕組みで、これによってパーティクルの色・大きさ・動き等が制御されています。ちなみに情報の事は「アトリビュート」と呼ばれています。

よく使われる「重力（Gravity Force）」もモジュールスクリプトで出来ていて、速度に対して変化を与える仕組みがノードによって作られています。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/5b945043-2d02-49ee-9ead-bc59a9a3531f.png)
↑黄色く囲ってるのは全てモジュールスクリプトで作られたもの

なので「Add Velocity」や「Gravity Force」等をダブルクリックする事でモジュールスクリプト内にアクセスして中身を見る事が出来ます。

【Add Velocity の中身】
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/86578ba9-dee4-988d-59a3-efa743c50d01.png)

【Gravity Force の中身】
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/408e87e1-4484-8344-d9b7-9fd033c5e373.png)

今回はピンポイントで何処を編集するか解説していきますが、アトリビュートとそれを制御するモジュールスクリプトに詳しくなると痒い所に手が届く様になるので、色々触ってみると良いと思います。

（注意：オリジナルを編集される場合は一旦複製してバグが無いか検証するか、複製したもので運用した方が良いと思います）


## （解説）親の進行方向に沿った角度でデカールを貼る
#### 大枠の仕組みについて
ではこちらの解説に移ります。まず、親（Parent)にあたる「矢印状のメッシュ」が地面に衝突した時に「Generate Collision Event」で衝突時の情報を作り、子供（Child）側は「Event Handler Properties」と「Receive Collision Event」で受け取っています。その状態で Render を「Decal Renderer」にしてデカールが描画される様にしています。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/a022ab7e-4b65-bde0-45e2-a9aa2ada292b.png)

![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/0d31fb14-5813-c0b9-d85f-9fbd5d94b29f.png)

ただ、この状態だとデカールの方向が一律同じな為、 Receive Collision Event の「Velocity」にある「Inherited Velocity Scale」に値を入れると親（矢印）の速度を継承出来るので、それを「Update Decal Orientation」の 「Side Direction」に代入しています。

![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/f893b627-0517-bda0-e259-111dd32ef331.png)

こうする事でデカールが貼られる向きを「速度」に基づいた角度にする事が出来ます。
ちなみにほんの少しだけ値を入れているのは、単に大きな値を入れてしまうとデカールが移動して意図しない見え方になってしまうのを防ぐためです。（角度自体は小さな値でも取る事が出来ます。）

![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/4a0a68f1-3f2c-e4e7-aa69-2d685f367401.png)

ただ、この状態でも問題があります。地面へのデカール描画は大丈夫なのですが、壁への描画が伸びて意図しない見た目になっています。

![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/ca6ecb4c-2ddc-fa93-ab22-c32e86b28be6.png)

![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/8e5ea05f-f883-fd35-6624-6e34d84a29dd.png)

これは「Update Decal Orientation」の「Facing Direction」が原因で、今だと「Z」に対して値が入っているのでZ軸に対してデカールが描画される為、地面だけ正しく壁は伸びた形になっています。なので試しに「Z」ではなく「X」に値を入れると壁に対しての描画が正しくなっているのが確認出来ます。（反対に地面に対して伸びた形になります）

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/25121cf8-a23b-48ff-02b5-8c73f8464a19.png)
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/10a2b567-f78e-a46c-0ee4-dbcf83e081da.png)

なので、この Facing Direction を衝突するオブジェクトの面に対応する様にしてあげれば解決出来そうです。

#### Receive Collision Event　モジュールスクリプトをカスタマイズ
そこで目を付けたのはReceive Collision Event の「Collision Normal」です。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/bfeadf10-840d-8182-0122-bcb6dc274d91.png)
この中にある「Add Velocity Along Collision Normal」に値を入れると衝突した物体の法線に沿った動きを付ける事が出来ます。
![12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/d7d2c4dc-2cf1-39c2-9f47-419f55882187.gif)
なので、衝突したそれぞれの面の「向き」に沿った動きをしているのが分かると思います。なのでこの情報を「Facing Direction」に渡してあげればデカールが正しく描画されるはず。

なのですが、、、、、

まだ問題が残ってます。この「Add Velocity Along Collision Normal」は動き、つまり Velocityに作用してしまうので、「Update Decal Orientation」の「Side Direction」にも作用してしまい意図しない見た目になってしまいます。

うーん、困りました、、、、　この面の向きに沿った情報は欲しいけど、速度になってしまうのは避けたい。せめて別の情報（アトリビュート）に逃がせないだろうか、、、、

そうだ！　Receive Collision Event をカスタマイズしてみよう！　
ただ、オリジナルを編集すると良からぬバグが発生した時に影響が全体に広がってしまうので、複製して別管理下にします。Receive Collision Eventをダブルクリックすると中に入れるので、その状態でブラウザボタンまたは Ctrl + B を押します。
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/3a8e9e97-2a85-fb09-b799-a665352cd248.png)

するとアセット場所を特定出来るので複製してカスタマイズしたのが分かりやすい様に語尾に「_Custom」等別名にしておきます。ついでにモジュールスクリプト内にある「Category」にも別名入れておいた方が後でNiagaraから呼び出す時に分かり易く管理にも役立ちます。


![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/82fa8456-1dcd-45ab-9ebe-0902ee45d3de.png)

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/c44c59f1-1c27-965e-fbf0-cbf79389ba76.png)

では複製した Receive Collision Event の中身を編集していきます。と言っても編集しているのは１か所だけです。「Collision Normal Inherit Vel」とコメントが打たれた箇所に「velocity」と付いた箇所がありますが、これが速度が受け渡されてる場所になるのでここを変えていきます。
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/2344b049-f2be-87ff-b4b2-9443779a099f.png)

左側に Parametersと書かれたウィンドウがあるので、この中にある「Particle Attributes」横にあるプラスボタンを押し Vector 型のアトリビュートを作り分かりやすい様に名前を変えてあげます。（例では Collision_Normal にしてます）ちなみに、速度・向きはX,Y,Zで作られているのでVector型、寿命は単一の数字なのでFloat型と目的によって型は違います。

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/1dd568a7-e125-9e20-525e-a25addddc048.png)

作ったアトリビュート（Collision_Normal）を元々 Velocityが割当たってた箇所（Map Set）にドラッグすると追加出来るのでこちらに差し替えます。繋がらなくなったVelocityは右クリック→ Remove pin で除外しておきます。

![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/6b2d04c5-213b-b600-d9b5-9ee10280b342.png)

それから Update Decal Orientation のFacing Directionに作ったアトリビュート（Collision_Normal）を代入してあげると、どの面でも破綻しないデカールを貼る事が出来ます。

ただ、入り組んだ箇所にデカールが発生すると部分的に伸びたりしてしまうので貼るテクスチャも含め自然に見える様に調整して頂ければと思います。

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/69a27afb-3282-e6ba-0c9b-e41a53a5fda4.png)







## （解説）マテリアルの模様でパーティクルの色と位置を変化させる
#### 大枠の仕組みについて
ではこちらの解説に移ります。こちらは大枠の仕組みとしては「Sample Texture」という指定したテクスチャをパーティクルに描画する機能を使っています。大体は Spawn Particles in Grid と併せて使うケースが多いです。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/0a2302e7-056c-0499-4688-fbdcf5d1d45a.png)


![02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/64e1549e-6884-38b0-f087-fd9d1e99c523.gif)


これをテクスチャではなく「マテリアル」の模様で描画される様にして位置も連動する様にします。まずマテリアルを直接ピックする事が出来ない為「Render Target」を作ります。（コンテンツブラウザ右クリック→ Texture→ Render Target）
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/19c3fdb4-2b68-5a1a-0a2c-75fdbf681196.png)

Texture Render Target 2D の Size は256等大きめの値にしておきます。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/eaab9c72-14c7-f449-74d8-f06ba93c091a.png)

このRender Target はカメラビュー等をテクスチャとして描画する機能なので、これにマテリアルの模様を描画させてTexture Sample でピックする流れになります。その為にブループリントを使います。コンテンツブラウザ→右クリックから Blueprint Class を選択してクラスを Actor にします。

![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/ccd91715-d254-9de3-bfae-2c3cd136c1ca.png)

ブループリントの中身ですが、随時変化する様にしたいのでイベントを「Tick」にして毎フレ実行される様にします。
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/ce27ea4d-0518-a84c-7fac-81da17b73559.png)
「Begin Draw Canvas to Render Target」に前工程で作ったRender Targetを割り当てて、Draw Materialに描画するマテリアルを割り当てておきます。

【描画するマテリアル】
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/99c20f6b-5c8b-1f7f-de1a-d2e12cf13a0e.png)

次に「Texture Sample」モジュールスクリプトをカスタマイズしていくので、複製して別名＆別カテゴリーにしておきます。変更する箇所ですが終盤辺りに「SampleUV」という項目が受け渡される箇所があるので、これを「DynamicMaterialParameter」という項目を作り渡しています。

![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/4350573f-0ffb-4d30-bd15-9fa28a27a376.png)

こうする事で Texture Sample でマッピングしたUV情報を別のマテリアルに Dyanmic Parameter 経由で渡す事が出来るのでRender Targetで使わているいるテクスチャとは違うものを使いたい時に便利です。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/38801498-5607-b0ff-3dac-ddd2f5997b17.png)


最後にカスタマイズした Texture Sample を動的に変化させる為「Particle Update」に入れて動きを付けていけば良いのですが、少し問題があります。

![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/60870e59-6491-9ae5-f676-66b7dff4f0bd.png)


Texture Sample を使う場合、どの様に画像をマッピングするか？を設定するTexture UVには Normalized Array Location を使う事が多いのですが、Particle Update だとこの処理が上手く行かないので、今回はUV用にパラメーターを作って乗り切りました。
Particle Spawn横のプラスボタンから「Set new or existing parameter directly」を選択、Position型のアトリビュートを作り名前を「initial_Position」、パーティクルの位置(Position）を代入します。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/a390da62-ac35-4c8f-242d-81339ccc95eb.png)


これで初期状態のパーティクル位置を保持できるので、Texture Sample の Texture UV にそれぞれ代入します。

![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/5e7f91be-5e8f-c386-e876-622ac3454ddc.png)

今回はエクスプレッションで書いているので以下を参考にして頂ければと思います。

TextureUV
X : Particles.initial_Position.x * 0.001　　　
Y : Particles.initial_Position.y * 0.001
（0.001は調整用で付けてます）

ちなみにパラメーターをエクスプレッションに切り替えるのは、横にある▽逆三角をクリックして「New Expression」で出来ます。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/3841ea9c-379b-7acb-bee6-182fb953707b.png)

これで正しくマテリアルの模様がマッピングされました。
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/66d18183-e29b-85d6-35d2-9ee7b9f01e69.png)

最後にマテリアルの明るさに連動してパーティクルの位置が変わる仕組みを 「set new or existing parameter directly」 を使って組んでいきます。
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553673/65eebb32-b91b-435f-4359-71a1a4525d7d.png)

仕組みとしては「Lerp Position」を使ってⒶに元々の位置（なので前工程で作った initial_Position）、Ⓑにオフセットかけた位置（initial_Position　+　オフセット値）、Alphaにパーティクルの色を代入しています。

こうする事で明度が「低い場合はⒶ」「高い場合はⒷ」と動的に変化し位置に作用するので、例題みたいにマテリアルの模様によって色と高さが変化するエフェクトが作れます。

## まとめ
出来る限り図解を織り交ぜて分かり易い説明を心掛けましたが、いかがでしたでしょうか。


今回は元々あるモジュールスクリプトに対してカスタムする方法を説明しましたが、理解が進むと新規で組める様になって表現の幅が広がってくると思いますので、これを通して興味を持つキッカケになると幸いです。

実は説明した方法以外にも色々説明したい技法があって、機会が出来たら[X(旧:Twitter）](https://twitter.com/home)でアナウンスしようかなと思ってます。

という事でまたの機会に！　ありがとうございました:grinning: