キャラクターの全身が燃え上がるような演出や、激しい戦闘によるダメージで血飛沫が飛び散る表現、オーラを纏うカットシーンなど、動くキャラクターの皮膚や衣服(Skinned Mesh Renderer)から直接パーティクルを発生させたいケースは多々あります。しかし、単にオブジェクトの親子関係を設定しただけでは、キャラクターが走り去った後に初期ポーズの位置(Tポーズなどの空中)から空しくエフェクトが出続けるという、追従破綻バグに遭遇します。本記事では、このバグが起きる仕組みを『服を着たまま走り去る影』の例え話を交えて分かりやすく解説し、完全に追従させるための正しいC#制御コードと設定手順を提示します。

1. 例え話で理解する:『初期位置に置き去りにされた石像から出続ける火の粉』

このアニメーションへの追従破綻バグを直感的に理解するために、『彫刻と本物のダンサーのすれ違い』を例えに考えてみましょう。

あなたが、ステージの上で激しく踊るダンサー(アニメーションで動くスキンメッシュ)の体全体から、金色の火の粉を美しく散らす演出を作りたいとします。しかし、演出スタッフ(パーティクルシステム)は、ダンサー本人の動きを見ず、ダンサーが踊り出す前に楽屋に置いてあった『初期ポーズの石像(インポート時のバインドポーズ・メッシュデータ)』だけを見て、そこから火の粉を出し始めました。

ダンサー本人はすでにステージの中央で華麗に舞っているにもかかわらず、誰もいない楽屋の石像から金色の火の粉が空しくシュシュと出続けてしまい、ステージ上のダンサーの体からは一切エフェクトが出ない状態です。これが、Skinned Mesh Rendererにおける『アニメーション追従破綻バグ』の正体です。

スキンメッシュのアニメーション変形(骨によるメッシュの引っ張り計算)は、処理を高速化するために**GPU側(Vertex Shaderステージ内)**で直接実行されます。しかし、CPUで動作しているShuriken(Particle System)は、GPUが計算した「変形後のポリゴン位置」を知ることができません。そのため、ShurikenはCPU内に保存されているアニメーションがかかる前の『初期ポーズ(バインドポーズ)』の静的なメッシュ位置からパーティクルを排出し続けてしまい、結果として本体の移動や手足の変形についていけず、エフェクトが置いてけぼりになってしまうのです。

2. 解決策:BakeMeshを用いた変形メッシュデータの動的供給

このGPUとCPUの情報の断絶を解決するためには、C#スクリプトを用いて、アニメーション変形後の最新のメッシュの形をCPU側へコピーし、それをパーティクルシステムへ毎フレーム渡し直すという処理を実装する必要があります。これには `SkinnedMeshRenderer.BakeMesh` という強力なAPIを使用します。

実装のステップ:

  1. 対象のGameObjectの子要素にParticle Systemを配置します。
  2. Particle Systemの『Shape』モジュールを有効にします。
    • Shape: `Skinned Mesh Renderer` に設定します。
    • Single Mesh: 対象の `SkinnedMeshRenderer` コンポーネントをアタッチします。
  3. 以下の制御スクリプト(`SkinnedMeshParticleFollower`)を作成し、Particle Systemと同じGameObjectにアタッチします。
using UnityEngine;

[RequireComponent(typeof(ParticleSystem))]
public class SkinnedMeshParticleFollower : MonoBehaviour
{
    public SkinnedMeshRenderer targetSkinnedMesh;
    private ParticleSystem particleSystemComponent;
    private Mesh temporaryBakedMesh;

    void Start()
    {
        particleSystemComponent = GetComponent<ParticleSystem>();
        
        // CPU側で一時的に変形後メッシュデータを受け取るための空のメッシュを作成
        temporaryBakedMesh = new Mesh();
        temporaryBakedMesh.name = "TemporaryBakedMesh_Particle";
    }

    void LateUpdate()
    {
        if (targetSkinnedMesh == null || particleSystemComponent == null) return;

        // 1. ★最重要:GPUでの骨変形(アニメーション)結果を、CPU側の temporaryBakedMesh に焼き付ける(Bake)
        targetSkinnedMesh.BakeMesh(temporaryBakedMesh);

        // 2. ベイクした最新のメッシュデータを、Shapeモジュールの参照先として再割り当てする
        var shape = particleSystemComponent.shape;
        shape.mesh = temporaryBakedMesh;
    }

    void OnDestroy()
    {
        // メモリリーク防止のため、使い終わったテンポラリメッシュを明示的に破棄
        if (temporaryBakedMesh != null)
        {
            Destroy(temporaryBakedMesh);
        }
    }
}

このスクリプトは、毎フレーム `LateUpdate`(アニメーションの姿勢計算がすべて完了した後のフェーズ)で実行されます。`BakeMesh` によって、アニメーションでポーズを取っているその瞬間の「キャラクターの皮膚の正確な3Dポリゴン座標」が一時メッシュとして生成され、それが直ちにパーティクルシステムのShapeソースに再代入されます。これにより、キャラクターが手を振ったり走ったりした瞬間に、その動いている腕や足の皮膚表面から正確に火の粉が追従して放出されるようになります。