There are many cases where you want to generate particles directly from the skin or clothing of a moving character (Skinned Mesh Renderer), such as when a character's entire body is on fire, when blood is scattered due to damage from intense battles, or when cutscenes are wrapped in an aura. However, if you simply set the parent-child relationship of objects, you will encounter a tracking failure bug where the effect continues to appear from the initial pose position (in the air, such as T pose) after the character runs away. In this article, we will explain the mechanism by which this bug occurs in an easy-to-understand manner using the analogy of ``a shadow running away with clothes on,'' and present the correct C# control code and setting procedure to ensure complete tracking.
1. Understand with an analogy: ``Sparks that keep coming out of a stone statue left at its initial position''
In order to intuitively understand this animation tracking failure bug, let's consider ``The difference between a sculpture and a real dancer'' as an example.
Suppose you want to create a performance in which golden sparks are beautifully scattered from the entire body of a dancer (animated skin mesh) dancing furiously on stage. However, the production staff (Particle System) did not look at the movements of the dancers themselves, but only looked at the ``initial pose stone statue (bind pose mesh data at the time of import)'' that had been placed in the dressing room before the dancers started dancing, and started emitting sparks from there.
Although the dancer herself is already dancing splendidly in the center of the stage, golden sparks keep coming out of the stone statue in the empty dressing room, and no effect is emitted from the dancer's body on stage. This is the true nature of ``Animation tracking failure bug'' in Skinned Mesh Renderer.
The animation deformation of the skinned mesh (calculating the tension of the mesh by the bones) is performed directly on the GPU side (inside the Vertex Shader stage) to speed up processing. However, Shuriken (Particle System) running on the CPU cannot know the ``polygon position after deformation'' calculated by the GPU. As a result, Shuriken continues to emit particles from the static mesh position of the "initial pose (bind pose)" stored in the CPU before the animation is applied, and as a result, it cannot keep up with the movement of the main body and the deformation of the limbs, and the effects are left behind.
2. Solution: Dynamic supply of deformed mesh data using BakeMesh
In order to resolve this disconnection of information between the GPU and CPU, it is necessary to use a C# script to implement a process that copies the latest mesh shape after animation deformation to the CPU side, and passes it back to the particle system every frame. This uses a powerful API called `SkinnedMeshRenderer.BakeMesh`.
Implementation steps:
- Place the Particle System on the child element of the target GameObject.
- Enable the "Shape" module of Particle System.
- Shape: Set to `Skinned Mesh Renderer`.
- Single Mesh: Attach the target `SkinnedMeshRenderer` component.
- Create the control script (`SkinnedMeshParticleFollower`) below and attach it to the same GameObject as the Particle System.
using UnityEngine;
[RequireComponent(typeof(ParticleSystem))]
public class SkinnedMeshParticleFollower : MonoBehaviour
{
public SkinnedMeshRenderer targetSkinnedMesh;
private ParticleSystem particleSystemComponent;
private Mesh temporaryBakedMesh;
void Start()
{
particleSystemComponent = GetComponent<ParticleSystem>();
// Create an empty mesh to temporarily receive the mesh data after deformation on the CPU side
temporaryBakedMesh = new Mesh();
temporaryBakedMesh.name = "TemporaryBakedMesh_Particle";
}
void LateUpdate()
{
if (targetSkinnedMesh == null || particleSystemComponent == null) return;
// 1. ★Most important: Bake the bone deformation (animation) results on the GPU to temporaryBakedMesh on the CPU side (Bake)
targetSkinnedMesh.BakeMesh(temporaryBakedMesh);
// 2. Reassign the latest baked mesh data as the reference destination of the Shape module
var shape = particleSystemComponent.shape;
shape.mesh = temporaryBakedMesh;
}
void OnDestroy()
{
// Explicitly destroy the used temporary mesh to prevent memory leaks
if (temporaryBakedMesh != null)
{
Destroy(temporaryBakedMesh);
}
}
}
This script is run every frame at `LateUpdate` (the phase after all animation pose calculations are completed). `BakeMesh` generates a temporary mesh with the exact 3D polygonal coordinates of the character's skin at the moment it is posed in the animation, and immediately reassigns it to the particle system's Shape source. As a result, the moment a character waves their hands or runs, sparks will be emitted from the skin surface of their moving arms and legs.