# 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.