はじめに
みなさんこんにちは!
Unreal Engine 4 Advent Calendar 2020 5日目の記事を担当します、WataruVFXです。
UE4アドカレには初めて参加させていただきます。
12月5日は、UE4 Niagaraを使った以下の動画の、魔法攻撃エフェクトの中身の説明です。
Fateの「無限の剣製」?とは方向性が違いますが、なんとなくそんな感じのものです。
動画はこちら↓
https://youtu.be/H-725dhxuPA
プロジェクトファイルのダウンロード
プロジェクトファイルは、以下からダウンロードしていただけます。(約21MB)
https://www.dropbox.com/s/t8eedrvztwf591a/AdventCalendar2020.zip?dl=0
使用バージョン: UE4.26.0
※プロジェクトファイルを開いて、中を見ながら読み進めてみてください。
エフェクトのデータはほぼすべてContents直下のFXフォルダに入っていますが、グレイマンのブループリントはThirdPersonBPフォルダ内にあります。
では、このエフェクトを作るのに必要な要素を説明していきたいと思います。
要素や気をつけた点
・魔法陣発生の際に記号としてフラッシュを入れる
・魔法陣から剣が発生しているように見せる
・発生した剣ならびに魔法陣がキャラクターの位置を向きつづける
・剣の軌跡を入れて刺さっていくのを見えやすくする
・刺さった剣はグレイマンのモーションに合わせて動く
今回のNiagara芸は最低限の簡易的な表現にするため、4つだけエミッタを使用しています。
Niagara Systemは1つで完結させます。
主体となっているのは「Sword-Main」という名前のエミッタで、このエミッタのパーティクル発生位置に他のエミッタが発生するようEvent Handler(イベントハンドラ)で制御をしています。
説明
##0.ブループリントの設定
まずはエミッタの説明の前にブループリントの説明をしておきます。
今回は、ThirdPersonCharacterの中に制御を入れています。
キーボードのEキーを押すと、剣のエフェクトが発生してグレイマンに刺さります。
Spawn System Attachedノードで使用するNiagara Systemアセット(ここでは「SwordSpawner」)を指定しています。
Attach to Componentにはグレイ万の「Mesh」が入り、これでグレイマンのスケルトンも参照されます。
LocationにはGetActorLocationノードで取得したプレイヤーの位置を入れています。
下段のほうはプレイヤーの現在位置をNiagaraに渡し続ける制御です。
Niagara Parameter Collectionを使うことでグローバルに値を渡すことができますので、Niagara内でパラメータ名を指定するだけで値を取得できます。
Niagara Parameter Collectionアセットの中身
アセット名は「NPCSwords」です。
このアセットに任意のパラメータを追加し、ブループリントなどで値を与えます。その値はプロジェクト内のすべてのNiagara Systemから簡単に参照できますので、グローバルな制御をするのに役立つ仕組みです。
##1.剣本体のエミッタ
Niagara Systemアセットの名前は「SwordSpawner」としています。
System Setting
System SettingはこのNiagara Systemアセット内のすべてのEmitterに作用します。
User Parametersでスケルタルメッシュアセットを参照しており、あとで差し替えが外からできるようにしています。
これは後ほどSkeletal Mesh Locationで使用されます。
ではエミッタ本体の設定ですが、剣本体のエミッタ名は「Sword-Main」です。
Emitter Setting
CPUSimを使っているのはイベントハンドラが現状ではGPUパーティクルで働かないためです。
Emitter Update
ここでは3回のBurstをして、それぞれが33個ずつパーティクルを発生させています。
Timelineのほうを見てみると、それが視覚化されています。(下図の赤枠)
ここでAfterEffectsライクにタイミングを調整したりBurstを追加できますので大変便利です。
Particle Spawn
Initialize ParticleモジュールのMesh Attributesでこのようなスケールを入れているのは理由がありまして…
その理由なのですが、RenderのところでMesh Rendererを使っていますが、Cubeを引きのばして剣に見立てているためです。
ちゃんとした剣のモデルを用意する場合はこのスケール処理は異なった値になるはずです。
Torus Locationモジュールでは位置をプレイヤーを囲うようにパーティクルが発生する制御をしています。
「Emitter Age」を使い、エミッタが発生してから1秒かけて発生範囲が400から800になるようにしています。
この間に3回Burstがありますので、順番にBurstの際に発生するパーティクルの位置が変化します。
Torus Originでエクスプレッションを使い、「NPC.NPCSwords.PlayerPos」と入力しています。
これでロケーションの中心がプレイヤーの位置になります。
「NPC.NPCSwords.PlayerPos」は、ブループリントのところでプレイヤーの位置を渡したものになります。
NPCで定義した値ですので、以下のようにParametersのタブのNiagara Parameter Collectionでその存在を確認できます。
Set(Particles)initPos
これはパーティクルが発生した瞬間の位置を格納しておくためにあります。
モジュールの追加で「Set new or existing parameter directly」を選択することで以下のようなSet Parametersモジュールはが作成されますので、このモジュール右上にある✙アイコンをクリックしてFloat型を追加し、「intPos」というパラメータを作りました。
Init Generate Location Event
Init Generate Location Eventモジュールは、イベントハンドラを使ってこのエミッタが発生させるパーティクルの位置に魔法陣やフラッシュなどを出す際に、それらが発生時だけ位置情報を取得してほしいので入れています。
このInit Generate Location Eventモジュールは、Generate Location Eventモジュールを複製して少しだけ手を加えたものです。その違いは以下のとおりで1か所だけです。
この「Event Name」を分けてあげないと、魔法陣とフラッシュのような発生の瞬間だけ位置を見てほしいものと、軌跡のリボンのような追従してほしいものを分けて制御できなかったためにこのような面倒な手順をふんでいます。
Particle Update
Skeletal Mesh Location
Skeletal Mesh Locationモジュールでは、Userパラメータで登録しておいたスケルタルメッシュ(ここではSK_Mannequinが入っている)を使用しています。
Vertex Sampling Modeでランダムを使用していないのは、Particle Updateでランダムを使用すると毎フレームランダムの計算が入ってしまって、剣の刺さる位置がめちゃくちゃになるためです。ですので、仕方なしにVertex Sampling Modeは「Direct(All Vertices)」にしてVertex IDの0~2000のどれかを使うようにしています。(もっとお手軽でよい方法があれば教えてください…)
Lerp Pos
Lerp Posモジュールは、自作のモジュールでScratch Padの機能を使っています。
現在のパーティクルの位置を格納されたParticles.initPosへLerpを使って移動しているように見せています。
CurveIndex はEmitterのLoopAgeを使用しています。ParticleのNormalizedAgeではありません。2秒後にパーティクルがIDの順番にどんどんと移動して突き刺さっていく、となっています。
モジュールの中身は以下のとおりです。
Sale Mesh Size
Sale Mesh Sizeモジュールで、剣に見立てたキューブが魔法陣の中心から伸びて出てくるように見せています。
Renderのところで後述しますが、Pivot Offsetを入れていますので魔法陣の後方にだけ伸びます。
Mesh Orientation3
Mesh Orientation3モジュールでは、発生したメッシュがプレイヤーのほうを向くようにするためにあります。
プレイヤーの向き(ベクトル)は、パーティクルの現在位置Particles.Positionから発生するプレイヤーの位置を引き算すると得られます。
Rotationの「Rotation Coordinate Space」を「Mesh」ではなく「World」としておきます。
このMesh Orientation3モジュールは、Initial Mesh Orientationモジュールを複製して加工したものです。
複製したモジュールの変更する箇所は下図のとおりです。これにより、アップデートがかかる際に1フレーム前の回転を考慮しなくなるので、Updateのためにおかしな回転になることが無くなります。
(もし、もっと簡単に向きをUpdateしていく方法があったらごめんなさい!🙇)
Generate Location Event
Generate Location Eventモジュールはイベントとして位置情報などを渡すために必要ですので必須です。
Render
Mesh RendererのPivot OffsetのXにオフセットを入れています。こうやって中心点をずらしてあげることで、剣に見立てたキューブが発生してスケールをかけた際、魔法陣の中心から後方へ伸びるような挙動になります。
長くなりましたが、本体部分のエミッタの設定は以上です。
##2.発生の瞬間のフラッシュ
エフェクトが発生する際、記号的に強調することは多々ありますが、これもその一環です。
「init-Flash」という名前のエミッタがそれです。
Particle Update
サイズとアルファ値の制御
Particle Updateでサイズとアルファの調整をしています。
一般的な処理なので簡潔に説明しますが、下図のようなカーブを描くことで発生の瞬間にポンッと出てきたように見えるはずです。
Event Handler
Event Handlerによる発生の制御
Emitterのグループを見ていただくと分かるのですが、このエミッタにはEmitter RateやBurstといったパーティクル発生のモジュールがありません。発生はこのEventによって制御されます。
Emitter Handler PropertiesのSourceで「Emitter:Sword-MainEvent:InitLocationEvent」を指定します。LocationEventではなくInitLocationEventです。LocationEventは軌跡となるリボンのほうで使用します。
剣本体のエミッタのほうでLocation EventをGenerateするモジュールを複製してこのInitLocationEventの部分を作りましたが、こうやって分けることで発生時のみ作用させるものと、アップデート時に作用し続けるものを別々に制御しています。
あとはReceive Location Eventを入れ忘れてはいけません。これがないとモジュール名のとおりですがロケーションのイベントを受け取り(Receive)できません。
##3.魔法陣
つづいて、発生時の魔法陣のエミッタです。エミッタ名は「Init-Mahojin」としています。
Particle Update
スケールとカラーの制御は好みで変わると思いますが、今回は下図のような感じです。
*Mesh Orientation 3 *
魔法陣のエミッタでもMesh Orientation 3モジュールを使って魔法陣がプレイヤー位置を向くようにします。
Orientation Axisだけが違い、Yが1.0で他が0.0となります。
Event Handler
このエミッタのEvent Handlerは先ほどの「Init-Flash」エミッタと同じですので、説明を割愛します。
Render
Planeのメッシュを用意してそこにマテリアルを適用した。ごく一般的なものです。
魔法陣のテクスチャは、みなさんお馴染みのいらすとやさんからお借りしました。
https://www.irasutoya.com/
パーティクルで使用するマテリアルは以下のように大変シンプルです。どちらの面も見えるようにするのでTwo Sidedにチェックを入れています。
マテリアル内で回転を入れている理由ですが、メッシュを回転させつつプレイヤーの方向を常に向くようにする手間を省くための手抜き手法です。
テクスチャのほうでTiling MethodをClampにしておくことで、マテリアル内で回転させたテクスチャがWrapして食み出て表示されることを防ぐことができます。
※下図はWrapでテクスチャの絵を回転させた際の不具合
##4.剣の軌跡となるリボン
最後のエミッタです。剣が刺さっていく移動の際に出す軌跡のエフェクトです。エミッタ名は「Trail-LocationBasedRibbon」です。
Event Handler
Emitter Handler PropertiesのSourceで「Emitter:Sword-MainEvent:LocationEvent」を指定します。
それ以外はLivetimeが0.1と大変短い以外は特記することはありません。
##種明かし?
プレイヤーに向かって剣が刺さっていくわけですが、何が行われていたかというと、
** ✕実際に剣がプレイヤーの方向に飛んでいく **
** ○刺さる位置は事前に保持されていて、上空のランダムな位置から保持された位置にLerpで補完しているだけ **
なので、この攻撃は絶対不可避です。(当たり前ですが…)
今回のものはシンプルに仕上げていますが、ヒットする際のエフェクトも入れられると良さそうです。
全部は刺さらないで一部は弾かれるといったことも不可能ではありません。時間があればそういった部分もどこかで共有したいです。もしくは、どなたかこのデータから改造するとかして試してみてください。
おわりに
以上になります。Niagara芸人が来年はもっと増えますように…。
(*念のためですが、注意書きを…。業務ではUE4を使用しておりません。会社での仕事とこの記事には一切関係がありません。)
最後まで目を通していただき、ありがとうございました。
Happy holiday!
そして、良いお年を。
↓もしよかったらフォローしてやってください↓
https://twitter.com/WataruVFX
(グレイマンじゃなくてサンタさんを串刺しにしようとしていましたが、思いとどまりました。)