Unity を使って、3Dモデルをバラバラに粉砕する演出ができるアセットを見つけたのですが、性能面でちょっと嵌ったので、顛末をまとめました。
1. とりあえず分解してみる
分解したいオブジェクトにRayFire Rigid コンポーネントをアタッチします。
対象となるオブジェクトにはMesh Filter とMesh Renderer がアタッチされている必要があります。
(この例では、初期状態で対象となるオブジェクトにRididBody はアタッチしていないので、自由落下はしません。)
エディタ上でRayFire Rigid コンポーネントを見るとDemolish ボタンがあるので、再生モードにしてからこのボタンを押してみます。
すると、ヒエラルキーツリーにRayFireMan オブジェクトが生成され、その下に元のメッシュが粉砕された新たなメッシュが生成されます。
RigidBody が粉砕パーツに付与され、自由落下していきます。他の物体にぶつかると、粉砕パーツがバラバラになっていきます。
なお、エディタ上での操作ではなく、スクリプトによって粉砕処理を実施する場合は RayFire.RayfireRigid コンポーネントのDemolish() メソッドを呼び出せばOKです。
2. メッシュ分割を前処理しておく
ドキュメントには「RayFire Rigid component」の項に以下のように書いてありました。
• Manual Prefab Precache: Special type if you want to manually precache mesh data in prefab and save it as asset in project folder. Regular Precache type can not store mesh data as assets because Unity Mesh is not serializable. Precached Prefab stores mesh data in serializable format way which allows to store it as asset.
Manual Prefab Precache を使えということですね。ドキュメントには参考用の動画も貼られていました。
ここの通りにやれば解決できるということでしょう。
3. アセットの実体とドキュメントが整合していない?
実際のアセットを動かしてみると、肝心の「Manual Prefab Precache」が見当たらりません。なぜだ?
もしかして、バージョンアップに伴って廃止された?
早速ChangeLog を見てみると以下の記載を発見。
[1.23]
- Rigid. Removed Obsolete Manual Precache, Manual Prefab Precache and Manual Prefragment demolition types.
Replaced by Reference Demolition: Prefragment with Shatter -> Mesh Export -> Prefab -> Reference Demolition.
2020/11/28 時点での最新版は1.24 なので、前のバージョンで手動系のいくつかの機能が抹消されているようですね。。。
一応、代替の方法が書かれてあったので、これを試してみました。要は、
- Prefragment でバラバラになったメッシュ群をMesh Export でPrefab にして、
- RayFire のRayFire Rigid component のDemolish Type をReference Demolition にして、
- バラバラになったPrefab を参照させる
ということでしょう。バラバラになった瞬間に、バラバラPrefab を生成して瞬時に置き換えるのですね。
アセットの構造をシンプルにしたかったがための対策なのでしょうかね。
早速試してみました。
4. 事前準備
まず、Mesh Export なるものを実行するための準備をします。
上記のサイトを参考にして、Export するための機能をエディタに追加します。
これはエディタでの作業の際にだけ必要ですので、以下のようなクラスを追加しました。
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class MeshExporter : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
#if UNITY_EDITOR
[MenuItem("CONTEXT/MeshFilter/Save .asset")]
public static void SaveFromInspector(MenuCommand menuCommand)
{
var meshFilter = menuCommand.context as MeshFilter;
if (meshFilter != null)
{
var mesh = meshFilter.sharedMesh;
if (mesh != null)
{
// 保存するパスを指定する
var path = string.Format($"Assets/{mesh.name}.asset");
AssetDatabase.CreateAsset(mesh, path);
AssetDatabase.SaveAssets();
}
}
}
#endif
}
5. バラバラPrefab の生成
バラバラに粉砕したい対象にRayFire Rigid コンポーネントをアタッチし、Main -> Demolition Type をAwake Prefragment にします。
そして、エディタを再生開始し、一時停止状態にします(分解した状態を留めるため)。
RayFire Rigid コンポーネントのDemolish ボタンを押し、前述したRayFireMan オブジェクト以下に生成される新たなメッシュを確認します。
このままでPrefab 化してもメッシュ部分が消えてしまい、Reference Demolition で参照できる状態にならないので、メッシュ部分をExport します。
- 分解されたパーツを選択する
- MeshFilter コンポーネントを選択し、Save .asset を選択する
- Asset フォルダの直下に分解パーツのメッシュが出力されているのを確認する(適宜適当な場所に移動する)
- 分解パーツの親玉もPrefab 化する(3. によりメッシュを消滅させずにPrefab 化できる)
以下の図も参考にしてください。
これで分解後のPrefab オブジェクトが完成しますので、エディタの再生を終了します。
6. Reference Demolition の設定
- 先ほどのRayfire Rigid コンポーネント(Demolition Type をAwake Prefragment にしていたもの)を更に変更し、Demolition Type をReference Demolition に変更する
- Reference Source に分解されたパーツの親玉Prefab を設定する
これで、RayFire.RayfireRigid コンポーネントのDemolish() メソッドを呼び出すと、粉砕されたPrefab により新たな粉砕済みインスタンスが生成されます。
7. その他
粉砕する時のメッシュ分割処理も重たいのですが、それ以上に物理演算処理もかなり重たいです。大きな理由は、粉砕パーツはすべてMesh Collider によって当たり判定処理が行われるためです。そのため、発生頻度の低い限定的な演出に使用するか、発生頻度が高い場合は物理演算処理をサボるような工夫をするとか、まだまだ考えることがありそうです。
2020/12/19追記:
https://youtu.be/Ac3b91vG6ec
Mesh Collider を使っている場合と使わないで物理演算処理をサボった場合の比較を動画にしてみました。