
Unityで地面が爆発するEffectのつくりかた
こんにちは、こはだです。
普段、音楽イベント向けのVRSNSのワールドを作っていたりします。
ライブパフォーマンスには演出がつきものです。
そして、現実ぽいものを作りたくなることもあるし、現実では不可能なものもやってみたくなります。
今回は、現実ではできない派手な演出として「破壊エフェクト」をやってみることにしました。
破壊エフェクトはなかなかVRSNSで観ることがすくなく、職人さんによって作られているものを見るたびにどうやっているのか興味深々でした。制作者の取材記事を見ると、Houdiniというアプリを使って作っていることが多く、いつかやってみようと思ってもコスト面でも学習面でも敷居が高くて、なかなか手を出せずにいました。
そこで、何かいい方法はないかと日々考えていたところ、Blenderで破壊アニメーションのチュートリアルがあったので、仕組みを学ぶために作ってみることにしました。

そして、Blenderで作った物理シミュレーションによるアニメーションをベイクして、FBXフォーマットで書き出して、Unityで破片のメッシュとアニメーション両方を取り込もうと思いましたが、破片の一つ一つに複数のアニメーションクリップが生成され、とても扱えるものではない、と思って断念しました。

試行錯誤してるうちに、こなごなにしたメッシュをUnityに取り込み、Rigid Bodyコンポーネントを付ければよいのではないかという、あたりまえの結論にたどり着きました。なので、その方向でのワークフローを書いておきます。
大まかな流れ
大まかな手順としては以下の通りになります。
作ったTerrainのメッシュをFBX形式で書き出す
上記メッシュをBlenderで読み込み、ポリゴンを粗くする
破壊アニメーションをさせる部分をポリゴンごとに別オブジェクトにする
別オブジェクトにしたポリゴンを押し出して立体にする
押し出して出来た面に別のマテリアルをアサインする
FBXで書き出す
元の地形と同じ位置に合わせる
破片オブジェクトにRigidBody、MeshColliderをつける
衝突物になるオブジェクトを作る
上記をぶつけるようにアニメーションを組む
終ったらオブジェクトをDestroyするようにスクリプトをアタッチする
ボタンを押したらオブジェクトが生成されるようにする
1.作ったTerrainのメッシュをFBX形式で書き出す
今回は地面を爆発させるということで、Terrainで作られた地形を加工しないといけません。Unityの公式が、ゲームオブジェクトをFBX形式で書き出すAddOnを提供しており、これを使います。
注:手元のプロジェクトでは、軽量化のために、「Terrain to Mesh」というプラグインを使ってTerrainをさらに分割しています。

2.上記メッシュをBlenderで読み込み、ポリゴンを粗くする

無事、TerrainのメッシュをBlenderにインポートできました。
これを全部使う必要はないので、加工対象を部分的にしたいと思います。位置を決めるために、Unityで上面からのシーンビューのスクリーンショットを撮って、見取り図としてBlenderに読み込みます。



このメッシュから、破壊させる部分をさらに絞り込みます。ちょうどライブステージの目の前の範囲を選択します。

もとのメッシュがこんなに細かくて、これを扱うとあとで管理が大変なことになりそうなので、粗くします。粗くするために、BlenderのDecimateモディファイアを使いました。


3.破壊アニメーションをさせる部分をポリゴンごとに別オブジェクトにする
メッシュをこなごなにするには、BlenderのAddOnで「Cell Fructure」というのがあるのですが、あまりいい感じに割れなかったので、今回は力技で別オブジェクトにします。

3キーを押して面選択モードにして、いっこずつ面を選択してPキーを押して、オブジェクトの分割をちまちまとやっていきます。そんなに細かいポリゴンではないと最初思っていましたが、あとの作業の大変さを予感させてきました。オブジェクトの管理や、ユーザーの表示負荷を考えると、見え方が許す限り、ポリゴン数は極力少ないほうがいいですね。
4.別オブジェクトにしたポリゴンを押し出して立体にする

別オブジェクトにした面をすべて選択して、下方向に押し出します。選択のちTabキーで編集モードにして、Eキーを押したのち、Zキーを2回押して真下に伸びるようにして、ドラッグします。

ところで押し出した直後は、面に妙な陰影が付いていることがあります。そういうときは面が重なっている、面が裏返っている、法線がなにかおかしいのいずれかのケースが考えられます。

この妙な陰影は、Unityに取り込んだときにも変な影になるので、取り除いておきます。編集モードで面選択モードにして、右クリックでコンテキストメニューを出し、Shade Flatで平らにできます。

5.押し出して出来た面に別のマテリアルをアサインする
もともと存在する面はUVがマッピングされていますが、押し出したことによって新たに作られた面には別のマテリアルを貼ります。土っぽいマテリアルを貼ることにしました。

これまた力技ですが、面をぽちぽち選択して、マテリアルをアサインします。別のオブジェクトをまたがって、それぞれの選択された面に一括でマテリアルを貼り付ける操作ができなくて、結構時間がかかりました。
6.FBXで書き出す

Blenderでの作業が終わったので、Unityで取り込むためにFBXを書き出します。
7.元の地形と同じ位置に合わせる
もともと切り出したTerrainの断片と位置を合わせます。Transformからワールド座標をコピーして、インポートしたFBXを同じ階層に入れたうえで、座標をペーストします。

8.破片オブジェクトにRigidBody、MeshColliderをつける
先ほどちまちま別オブジェクトにしてきたものたちに、RigidBodyとMeshColliderのコンポーネントをつけてあげます。前者が重力にのっとった物理的な挙動の付加で、後者は衝突の境界をつくるものです。

9.衝突物になるオブジェクトを作る
爆発したように破片たちを吹っ飛ばすには、同じく衝突の境界をもったオブジェクトでぶつけてあげなくてはいけません。なので、下から押し出すようにキューブを作りました。

10.上記をぶつけるようにアニメーションを組む
衝突が起きて、破片が飛び散って、飛び散った破片が落ち着くまでの流れをアニメーションにします。大まかに以下のように組みました。
破片たちは下に落ちないように、初期はUse GravityをOFFにする
コライダー付きの四角くて大きいキューブを下から突き上げる
ぶつかる寸前に、破片たちのUse GravityをONにする
ぶつかってしばしのち、破片たちのUse GravityをOFFにする(重力がないので高く飛んでいきます)
さらにしばしのち、破片たちのUse GravityをONにします(下に落ちます)

11.終ったらオブジェクトをDestroyするようにスクリプトをアタッチする
さて、これであとはこのアニメーションを再生するトリガーをつければ完成!といいたいところですが、一つ厄介な問題があります。
RigidBodyで動いたオブジェクトは、非アクティブにしても場所が戻ることがありません。スクリプトを組んで、最初の場所と回転位置を覚えさせておけばもとにもどすことができるみたいなのですが、スクリプトはつかえません。というのと、Animationの終わり際に初期位置のTransformのパラメータを入れて、場所を戻そうとしましたが、相対座標でしか入れられなくて、しかも値がすべて0が初期位置になっているけど実際はそうじゃなくて、機能しませんでした。(プレイモードにするとPositionに数字は入るのだけど。なぞ)
なので、アニメーションが終ったら、このオブジェクト全体をDestroyして、出現トリガーを引くと都度Createするという方式をとることにしました。
生成物にするので、この一連のオブジェクトをPrefab化して、この中に必要なもの全部が含まれるようにしておきます。出現と同時にアニメーションが再生されればいいので、Animation Controllerも必要なく、Animationクリップのアタッチのみでシンプルな形にしました。そして生成されたことをトリガーにして、おおよそ物理挙動が終わって落ち着く10秒で自動的にDestroyされるようにCCKのスクリプトをアタッチしてます。

12.ボタンを押したらオブジェクトが生成されるようにする
Destroyされるというのは、ヒエラルキーから消えてしまうので、表示させるときは生成する必要があります。生成をさせるトリガーをなにかのボタンに付けておきます。

そして、生成をするためのコンポーネントを何かのオブジェクトに付けます。

この中での大事なのは、Create Item Gimmickの「Spawn Point」で、最初はこの破壊アニメーションのオブジェクトを置いている場所に位置を設定していたのですが、一度Destroyされると、ヒエラルキーの階層外に生成されるので、Spawn位置がだいぶずれてしまいます。なので、位置指定用の空オブジェクトを作って、そこをSpawn Pointに設定するとよいと思います。
Item TemplateはPrefabを指定しておきます。存在と同時に再生されるアニメーションがオブジェクトにくっついているので、置いたままにしておくと入った瞬間いきなり地面が爆発するし、生成させて演出させるため、ヒエラルキーの中にオブジェクトを置く必要はありません。

以上、駆け足でしたが、地面を破壊するアニメーションのワークフローを書きました。こういう風にオブジェクトをばらばらにするのって、一つ一つのオブジェクトを扱うのと違うむずかしさがあってなかなか大変でした。
BlenderとUnityをはしごしながら思ったのは、けっこう細かい調整をしたくなっても、作業を後戻りしにくかったり、作り直しをするはめになったので、経験を積んで勘をみがくか、ノード式のワークフローみたいなHoudiniなら具合をみながら作り込めそうでいいなーって思いました!
以上です!
いいなと思ったら応援しよう!
