# 6. Particle Pitfalls Part of [[Particle Systems and Layered Effects]]. Requires Unity 6.3 LTS+ and URP. Previous: [[5.VFX Graph Comparison and Performance]]. | Symptom | Cause | Fix | |---|---|---| | Particles are pink/magenta | Built-in pipeline material (`Particles/Standard Unlit`) in a URP project | Use `Universal Render Pipeline/Particles/Unlit` or `Lit`. **Edit > Rendering > Materials > Convert** for old assets | | Soft particles do nothing | Depth Texture disabled | Enable **Depth Texture** on the URP Asset (or the Camera) | | Heat haze shows nothing / black | Opaque Texture off, or wrong surface type | Enable **Opaque Texture**; make the graph Transparent | | Fire emits sideways | Shapes emit along local **+Z** | Rotate the emitter (or `FX_Root`) to `(-90, 0, 0)` | | Smoke stays attached to a moving can | Simulation Space **Local** | Set **World** | | Scaling the prefab doesn't scale the effect | Scaling Mode Local/Shape | **Hierarchy** | | Layers flicker in front of each other | Transparent sorting by distance | Same Sorting Layer, distinct **Order in Layer**; Sorting Fudge | | Additive flames invisible in daylight | Additive adds to an already bright background | Add an alpha-blended "body" layer, or darker texture | | `ps.main.startColor = ...` won't compile | Modifying a member of a returned struct | `var main = ps.main; main.startColor = ...;` | | Setting `startSize` destroyed my curve | Assigning a float replaces the MinMaxCurve | Use `startSizeMultiplier` | | `OnParticleCollision` never called | Send Collision Messages off, Type Planes, or script on the wrong object | Type **World**, tick **Send Collision Messages**, script on the system or the hit collider | | Collisions miss thin geometry | Medium/Low quality voxel cache | High quality for that system, or thicker colliders, or Radius Scale | | Sparks hit their own effect | Collides With includes the FX layer | Exclude it from the mask | | Sub emitter emits nothing | Sub emitter isn't a child of the parent; or its own emission is 0 with no Burst | Parent it, give it a Burst | | Sub emitter plays on its own at start | Its Play On Awake / Looping on | Turn both off; sub emitters are driven by the parent | | Splash count explodes | Collision sub-emitter on every drop, chains | Emit Probability, shallow chains, Lifetime Loss 1 | | `OnParticleSystemStopped` never fires | Stop Action isn't Callback, system is Looping, or script isn't on the system's GameObject | Set Callback, call `Stop()` for loops, move the script | | Pooled effect replays old particles | Not cleared before replay | `Clear(true)` then `Play(true)` | | Pooled effect shows at wrong place for a frame | Activated before moving | Set the transform, then Play (as in `PlayAt`) | | Fire looks "empty" for the first second | No prewarm | Looping + **Prewarm** | | Big frame drop when a fire starts | Prewarm on a long Duration, or first-use instantiation | Short Duration, pool Prewarm | | Effect disappears when the camera looks away from the base | Bounds too small / culled | Check Show Bounds; Culling Mode; larger shape | | GC spikes from scripts | `new List` / arrays each call, `GetParticles` allocations | Cache buffers (see `EmberPopper`, `ParticleImpactSpawner`) | | Frame rate tanks on Quest near smoke | Overdraw | Fewer, smaller puffs; Max Particle Size; Camera Fading |