# 1. Particle System Fundamentals
Part of [[Particle Systems and Layered Effects]]. Requires Unity 6.3 LTS+ and URP. Next: [[2.Worked Effect - Trash Can Fire]].
The built-in **Particle System** (often called *Shuriken*) runs on the CPU, works on every platform including Quest, and supports physics collisions with your normal colliders. Each system is one component made of **modules**. Only modules with a tick are evaluated, so leave everything off until you need it.
## 1.1 Create your first system
1. **GameObject > Effects > Particle System**. You'll get a system emitting white dots upward from a cone.
2. In the Scene view, the **Particle Effect** overlay (bottom right) has Play, Restart, Stop, playback speed, and **Show Bounds**. Keep it open while tuning.
3. Rotate the GameObject to `(-90, 0, 0)` so the cone points along world up (+Y). Shapes emit along the local **Z** axis, so this rotation is how most upward effects start.
## 1.2 Main module
The main module sets each particle's starting values and how the system as a whole behaves.
| Property | What it does | Typical use |
|---|---|---|
| Duration | Length of one cycle in seconds | 1–5 for loops, exact length for one-shots |
| Looping | Restart after Duration | On for fire, off for explosions |
| Prewarm | Simulate one full cycle before the first frame (looping only) | Fire that is already burning when the scene opens |
| Start Delay | Wait before emitting | Stagger layered parts of a one-shot |
| Start Lifetime / Speed / Size / Rotation / Color | Per-particle start values | Use **Random Between Two Constants** for organic variation |
| 3D Start Size / Rotation | Separate X/Y/Z values | Stretched shapes, spinning debris |
| Gravity Source / Gravity Modifier | Multiplies `Physics.gravity` | Sparks: 0.3–1. Smoke: 0 or slightly negative |
| Simulation Space | Local or **World** | **World** for anything trailing behind a moving emitter (smoke, embers) |
| Scaling Mode | Hierarchy / Local / Shape | **Hierarchy** so scaling the prefab scales the effect |
| Play On Awake | Starts automatically when enabled | On for ambient effects, off for pooled one-shots |
| Max Particles | Hard cap. Emission stops when it's reached | Set this deliberately; it's your budget |
| Stop Action | None / Disable / Destroy / **Callback** | Callback for pooling (part 4) |
| Culling Mode | What happens off-screen | **Automatic** or **Pause** for looping ambient effects |
> [!tip] Curves and gradients
> Most values can be **Constant, Curve, Random Between Two Constants, or Random Between Two Curves**. Click the small arrow next to a field to switch. Curves in the main module are evaluated over the **system's Duration**; curves in "over Lifetime" modules are evaluated over each **particle's** lifetime.
## 1.3 Emission
- **Rate over Time**: particles per second. Fire flames: 20–40.
- **Rate over Distance**: particles per metre the emitter moves (in World space). Good for footstep dust and tyre smoke.
- **Bursts**: `Time, Count, Cycles, Interval, Probability`. One-shots (impact, explosion) use one burst with Rate over Time = 0.
## 1.4 Shape
Where particles spawn and which way they start moving. Common shapes: **Cone** (fire, fountains), **Sphere / Hemisphere** (explosions), **Box** (rain volumes), **Circle** (rings, the top of a trash can), **Edge**, and **Mesh / Mesh Renderer / Skinned Mesh Renderer** (a burning character).
Useful settings: **Radius Thickness** (0 = emit from the surface only, 1 = whole volume), **Arc** and **Mode** (Random, Loop, Ping-Pong, Burst Spread), **Randomize Direction**, and **Align To Direction** (rotates particles to their start direction).
## 1.5 Velocity, force, and noise over lifetime
- **Velocity over Lifetime**: adds linear, **Orbital**, **Offset**, and **Radial** velocity, plus a **Speed Modifier** curve. A curve dropping from 1 to 0.2 makes smoke slow down as it rises.
- **Limit Velocity over Lifetime**: clamps speed and adds **Drag** (optionally multiplied by size). Good for sparks that should lose speed quickly.
- **Inherit Velocity**: particles inherit the emitter's movement (Initial or Current).
- **Force over Lifetime**: constant acceleration in Local or World space. Smoke drifting with wind: `X = 0.3`, World.
- **Noise**: curl noise that makes motion organic. **Strength**, **Frequency** (lower = larger swirls), **Scroll Speed** (animates the field), **Damping**, **Octaves** (each adds cost), and **Quality** (1D/2D/3D; 3D is the most expensive). Flames and embers both use it.
## 1.6 Color and size over lifetime
- **Color over Lifetime**: a gradient multiplied with Start Color. Alpha keys at both ends set to 0 give a fade in and out, which hides pops.
- **Color by Speed**, **Size by Speed**, **Rotation by Speed**: map speed to a value. Fast sparks white-hot, slow sparks red.
- **Size over Lifetime**: a curve. Flames shrink (1 → 0), smoke grows (0.3 → 1.5).
- **Rotation over Lifetime**: a slow random spin (−30 to 30 °/s) on smoke hides the fact that every puff uses the same texture.
## 1.7 Texture Sheet Animation (flipbooks)
1. Enable **Texture Sheet Animation**, Mode **Grid**, Tiles `4 x 4` (or whatever your sheet uses).
2. **Animation: Whole Sheet**. **Time Mode: Lifetime** plays the sheet once per particle.
3. **Start Frame: Random Between Two Constants 0–15** for smoke, so puffs don't all look the same.
4. With **Single Row** mode and **Random Row**, each particle picks one row (good for sheets with several variants per row).
5. **Mode: Sprites** lets you use separate Sprite assets instead of a grid.
Turn on **Flip Book Blending** in the URP particle material (1.9) to cross-fade between frames, which hides low frame counts.
## 1.8 Renderer module
| Render Mode | Use |
|---|---|
| Billboard | Always faces the camera. Flames, smoke |
| Stretched Billboard | Stretches along velocity (**Speed Scale**, **Length Scale**). Sparks, rain |
| Horizontal / Vertical Billboard | Ground ripples and splashes (Horizontal) |
| Mesh | 3D debris, shell casings |
| None | Only trails are drawn |
Other settings that matter:
- **Material** and **Trail Material**.
- **Sort Mode**: By Distance, Oldest/Youngest in Front, By Depth. Smoke usually looks best with *Oldest in Front* off and *By Distance*.
- **Sorting Fudge**: biases this system against other transparent objects. Lower = drawn later (in front).
- **Order in Layer** / **Sorting Layer**: deterministic ordering between your own systems (used heavily in part 2).
- **Min/Max Particle Size**: a fraction of the screen; Max stops huge close-up particles from filling the screen and causing overdraw.
- **Render Alignment**: View, World, Local, Facing, Velocity.
- **Cast Shadows / Receive Shadows** (Lit shaders only).
## 1.9 URP particle materials
Create materials from **Assets > Create > Material**, then pick a shader:
- **Universal Render Pipeline/Particles/Unlit**: cheapest. Flames, sparks, glows.
- **Universal Render Pipeline/Particles/Lit**: reacts to scene lights and shadows. Smoke and dust that should be lit by the fire.
- **Universal Render Pipeline/Particles/Simple Lit**: Blinn-Phong, cheaper than Lit. A good choice for Quest.
Key material settings:
| Setting | Value |
|---|---|
| Surface Type | **Transparent** for nearly all particles |
| Blending Mode | **Additive** (adds light: flames, sparks, glows; never darkens), **Alpha** (smoke, dust), **Premultiply**, **Multiply** |
| Color Mode | Multiply (default), Additive, Subtractive, Overlay, Color, Difference: how the particle's vertex color combines with the texture |
| Flip-Book Blending | Cross-fade flipbook frames |
| Soft Particles | Fades the particle where it intersects geometry, so there's no hard line where smoke meets the floor. Set Near/Far fade distances |
| Camera Fading | Fades particles very close to the camera. Prevents screen-filling smoke |
| Distortion | Bends what's behind the particle (needs the **Opaque Texture**) *(the URP Particles/Lit and Unlit shaders expose Distortion; verify the option in your version)* |
> [!important] Depth Texture
> Soft Particles and Camera Fading read the camera depth texture. Turn on **Depth Texture** in the URP Asset (or on the Camera). If soft particles do nothing, this is almost always why.
## 1.10 Sub Emitters
A sub emitter is another Particle System that is spawned **from each particle** of the parent at a specific moment:
- **Birth**: continuously while the parent particle lives (a trail of smoke behind each spark).
- **Collision**: when the parent particle collides (requires the Collision module, part 3).
- **Death**: when the parent particle dies (firework bursts).
- **Trigger**: when the parent particle meets the Trigger module's conditions.
- **Manual**: only when you call `ParticleSystem.TriggerSubEmitter` from code.
The **Inherit** flags (Color, Size, Rotation, Lifetime, Duration) pass parent values to the child. **Emit Probability** is a 0–1 chance per event. The child must be a **child GameObject** of the parent system, and its own emission is normally a Burst with **Rate over Time = 0** and **Looping off**.
## 1.11 Collision module
- **Type: Planes** (cheap, only infinite planes you assign) or **World** (collides with your physics colliders).
- **Mode** 3D / 2D. **Dampen** (speed lost, 0–1), **Bounce** (0–1), **Lifetime Loss** (fraction of lifetime lost per hit; 1 = die on first hit).
- **Min Kill Speed** / **Max Kill Speed**: particles below/above these speeds are removed.
- **Radius Scale**: collision radius relative to size.
- **Collides With**: a layer mask. Exclude the player and the effect's own layer.
- **Quality: High / Medium / Low**. High raycasts every particle every frame. Medium and Low use a voxel cache (**Max Collision Shapes**, **Enable Dynamic Colliders**, **Voxel Size**), which is much cheaper but may miss thin geometry.
- **Send Collision Messages**: calls `OnParticleCollision` (part 3).
- **Visualize Bounds**: draws collision spheres in the Scene view.
## 1.12 Triggers module
Tests particles against a list of **colliders** (not layers) and reacts with **Inside / Outside / Enter / Exit** set to *Ignore, Kill,* or *Callback*. Callback calls `OnParticleTrigger()` on a script on the **same GameObject as the Particle System**, and you read the particles with `ps.GetTriggerParticles(ParticleSystemTriggerEventType.Enter, list)`. Example: snow that disappears when it enters a sheltered volume.
```csharp
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(ParticleSystem))]
public class TriggerTint : MonoBehaviour
{
ParticleSystem ps;
readonly List<ParticleSystem.Particle> enter = new();
void Awake() => ps = GetComponent<ParticleSystem>();
// Called because the Triggers module has Enter set to "Callback".
void OnParticleTrigger()
{
int count = ps.GetTriggerParticles(ParticleSystemTriggerEventType.Enter, enter);
for (int i = 0; i < count; i++)
{
var p = enter[i];
p.startColor = new Color32(80, 160, 255, 255); // turn blue inside the volume
enter[i] = p;
}
ps.SetTriggerParticles(ParticleSystemTriggerEventType.Enter, enter);
}
}
```
## 1.13 Lights module
Attaches a real **Light** to a percentage of particles. Make a Point Light prefab (disabled in the scene, Range ~1, no shadows), assign it, then set **Ratio** (0.1 = 10% of particles), **Maximum Lights** (keep it low, 4–8), **Use Particle Color**, **Size Affects Range**, **Alpha Affects Intensity**, and **Range/Intensity Multiplier**. In URP every light counts toward the per-object light limit, so this is for a few hero embers, not hundreds. On Quest, skip it and fake it with additive glow.
## 1.14 Trails
The **Trails** module draws a ribbon behind each particle (**Mode: Particles**) or connects particles in order (**Mode: Ribbon**). Settings: **Ratio**, **Lifetime**, **Minimum Vertex Distance**, **World Space**, **Die With Particles**, **Width over Trail**, **Color over Trail**, and **Inherit Particle Color**. Assign a **Trail Material** in the Renderer (Particles/Unlit, Additive). Trails on 5–10% of sparks look great; trails on all of them cost a lot.