VFX & Particle System
Unity has two particle systems — Particle System (CPU-based, flexible) and VFX Graph (GPU-based, millions of particles) — for fire, smoke, rain, and magic FX.
Imagine...
A Particle System is a hand-fired firework — you set rate, angle, and color, and the CPU updates each particle. Precise, but slow at high counts. VFX Graph is an industrial firework machine — everything runs in parallel on the GPU, so millions of particles are possible, but you need a strong GPU and URP/HDRP. Pick based on particle count, target platform, and how complex the behavior is.
The concept in detail
The Particle System (also called Shuriken) is a component on a GameObject that creates and updates particles on the CPU. Each particle has position, velocity, color, size, and lifetime. Modules (Emission, Shape, Velocity, Color over Lifetime, Force, Collision…) give fine control. Good for: small fires, rain, snow, hit FX, smoke — tens to a few thousand particles.
VFX Graph (Visual Effect Graph) runs entirely on GPU compute shaders. You build update logic with a node graph similar to Shader Graph (Initialize, Update, Output). Millions of particles at good performance. Requires: URP or HDRP, a GPU with Compute Shader support (most GPUs from 2015+), not older Android/iOS.
VFX Graph splits particles into: Spawn (rate/burst), Initialize (initial attributes), Update (per-frame physics, color, size), and Output (how they draw: quad, mesh, ribbon). Contexts connect through a data flow, like a render pipeline.
Performance: a Particle System is fine on mobile and console if you stay
under ~1000 live particles. Cap with maxParticles and
Stop Action = Disable after play. VFX Graph scales better, but
profile it on the target platform.
Particle System vs VFX Graph
| Criteria | Particle System | VFX Graph |
|---|---|---|
| Processing | CPU | GPU (Compute Shader) |
| Optimal particle count | 100 – 10,000 | 10,000 – 10,000,000+ |
| Pipeline | Built-in, URP, HDRP | URP, HDRP only |
| Mobile | ✅ Good | ⚠️ Limited |
| Interface | Inspector module | Node graph editor |
| Script API | Full C# API | Limited (SetFloat, SetTexture) |
| Use when | Fire, rain, hit FX, smoke | Galaxy, fluid, crowd, destruction |
Hands-on steps (Particle System)
Create a Particle System
GameObject → Effects → Particle System, or Add Component → Particle System on an existing GameObject.
Configure the Main module
Duration, Looping, Start Lifetime, Start Speed, Start Size, Start Color — the basics.
Pick an emission shape
Shape module: Cone (fire), Sphere (explosion), Box (rain from above), Circle (ring). Tune Radius and Arc.
Enable Color over Lifetime
Add a gradient: red → orange → yellow → transparent to fade a fire out.
Play from code when needed
ps.Play() / ps.Stop() / ps.Emit(count) — or EmitParams for an instant burst.
Interactive simulator
Tune Particle System parameters and press Burst / a preset to see the effect.
Code example
BasicPlay/Stop a Particle System and emit a burst on demand (e.g. a hit effect).
using UnityEngine;
public class HitEffect : MonoBehaviour
{
public ParticleSystem sparks;
public ParticleSystem smoke;
// Call on bullet impact — PlayOneShot a prefab at a position
public static void PlayAt(Vector3 pos, Vector3 normal,
GameObject fxPrefab)
{
GameObject fx = Instantiate(fxPrefab, pos,
Quaternion.LookRotation(normal));
Destroy(fx, 2f); // Clean up after 2 seconds
}
// Burst emit: 30 particles immediately, no looping
public void BurstEmit()
{
ParticleSystem.EmitParams ep = new ParticleSystem.EmitParams();
ep.position = transform.position;
sparks.Emit(ep, 30);
smoke.Play();
}
// Change particle color at runtime via Main module
public void SetColor(Color color)
{
var main = sparks.main;
main.startColor = new ParticleSystem.MinMaxGradient(color);
}
}Code example
AdvancedDrive VFX Graph from C# — set properties and fire events with ExposedProperty.
using UnityEngine;
using UnityEngine.VFX;
public class VFXController : MonoBehaviour
{
public VisualEffect vfx;
// Property IDs — must match Exposed Property names in VFX Graph
static readonly int SpawnRateID = Shader.PropertyToID("SpawnRate");
static readonly int ColorID = Shader.PropertyToID("BaseColor");
static readonly int ExplodeID = Shader.PropertyToID("OnExplode");
void Start()
{
vfx.SetFloat(SpawnRateID, 50f);
vfx.SetVector4(ColorID, new Vector4(1f, 0.5f, 0f, 1f));
}
public void Explode()
{
// SendEvent fires an event in VFX Graph (Burst spawn)
vfx.SendEvent(ExplodeID);
}
public void SetIntensity(float t)
{
// Spawn rate 0 → 500 from intensity
vfx.SetFloat(SpawnRateID, Mathf.Lerp(0f, 500f, t));
}
}📌 Quick recap
- ▸Particle System: CPU, every platform, <10k particles
- ▸VFX Graph: GPU, URP/HDRP only, millions of particles
- ▸Stop Action = Disable to reuse (object pooling)
- ▸ps.main.startColor must be reassigned as a struct
- ▸VFX Graph: SendEvent() fires a Burst context
⚠️ Common mistakes
❌ Spawning many PS prefabs without Destroy
Thousands of Particle Systems pile up → heavy lag
✅ Pool the PS, or Destroy(fx, lifetime) after it finishes
❌ Editing ps.main.startColor.color directly
Struct copy — the change never applies
✅ var main = ps.main; main.startColor = new MinMaxGradient(c);