Quick answer: Niagara’s Collision module only writes events when the emitter runs on CPU, when Generate Collision Event is enabled and named with a channel that matches a receiving Event Handler, and when the per-frame event budget is not exhausted. If you need GPU-driven collisions and event output, route the data through a CPU helper emitter that reads particle attributes from the GPU emitter.
You build a particle effect for an explosion: sparks bounce off walls and floor, and each bounce should spawn a small smoke puff. You add a Collision module to the spark emitter, set its physics asset, hit the play in editor button, and watch the sparks bounce nicely — but the smoke puff emitter does nothing. The Event Handler on the smoke puff emitter never fires. The collision is happening, the bounce is happening, but the chain that converts “particle hit something” into “spawn another particle” has a missing link.
The Symptom
You enable a Collision module on a Niagara emitter and set it to bounce particles off the world. The bounces work. You add a second emitter with an Event Handler intended to spawn particles when the first emitter reports collisions. Nothing spawns. The Niagara Debugger shows zero events for the channel.
Variants you may see:
- Events fire on a small system but stop firing once you increase the spawn rate.
- Events fire in editor preview but not in PIE.
- Events fire when Sim Target is CPU but go silent when you switch to GPU.
- Events fire correctly but the Event Handler reads zeros for all attributes (position, normal, etc.).
What Causes This
1. GPU sim cannot emit CPU events directly. The Niagara event bus runs on the CPU. A GPU compute simulation has no way to push events back across the GPU/CPU boundary mid-frame. Many people switch to GPU sim for performance and then discover their carefully wired Event Handler chain stops working.
2. Generate Collision Event is off. The Collision module has two related toggles — one to actually perform collision response, and one to publish a collision event payload. They are independent. Bouncing works without the event being published.
3. Event channel name mismatch. The publishing emitter writes to a named channel (often left at the default). The Event Handler on the consuming emitter must reference the same source emitter and the same channel. A typo or stale reference silently produces no events.
4. Per-frame event budget exhausted. Each Event Handler has a Max Events Per Frame setting (often 100 by default). High-rate collisions burst past this and the rest are dropped. The debugger shows them as “dropped events.”
5. Collision quality is too low. The Collision module has accuracy settings. For thin walls or fast particles, the default sample rate can miss the collision entirely — in which case the bounce happens via tunneling fallback but no event is generated.
The Fix
Step 1: Confirm the simulation target. Open the emitter, check the Emitter Properties panel, and look at Sim Target. For event-driven workflows, use CPUSim on the publisher.
// In the Niagara emitter Stack:
Emitter Properties:
Sim Target: CPUSim // REQUIRED for direct event publishing
Particle Update:
Collision:
Collision Mode: World
Collision Quality: High
Generate Collision Event: TRUE
Event Channel: "SparkHit"
Restitution: 0.5
Step 2: Wire the receiving emitter to listen on the same channel. Add an Event Handler that points at the publisher and the channel name.
// On the SmokePuff emitter:
Emitter Properties:
Add Event Handler:
Source: "SparkEmitter"
Event Name: "SparkHit" // must match exactly
Execution Mode: SpawnedParticles
Max Events Per Frame: 200
Spawn Number: 1 // puffs spawned per event
Particle Spawn:
Receive Collision Event:
// Reads CollisionPosition, CollisionNormal, CollisionVelocity
// from the published event payload.
Position: From CollisionPosition
Velocity: Reflect(CollisionVelocity, CollisionNormal) * 0.3
Step 3: Enable Generate Spawn Info on the Collision module. The Generate Spawn Info sub-toggle bundles position, normal, surface material, and incident velocity into the event payload. Without it, the receiver gets an event but the data fields are zero.
Step 4: Use the GPU compute collision pattern when you need GPU sim. If your particle count requires GPU, the workflow is different: GPU collisions output to a Particle Attribute Reader, and a second CPU emitter reads that attribute each frame to publish events.
// SparksGPU emitter (high count, Sim Target: GPUCompute)
Particle Update:
GPU Collision:
Collision Mode: SceneDepth // or GlobalDistanceField
Write Hit Data To Attribute: TRUE
Hit Position Attribute: "GPUHitPos"
Hit Normal Attribute: "GPUHitNormal"
// SparkHitListener emitter (Sim Target: CPUSim, low count)
Particle Spawn:
Spawn Burst Instantaneous: 1 // keep one persistent reader
Particle Update:
Read Particle Attribute:
Source Emitter: SparksGPU
Attributes: GPUHitPos, GPUHitNormal
Generate Custom Event:
Channel: "SparkHit"
Position: GPUHitPos
Normal: GPUHitNormal
Step 5: Open the Niagara Debugger to verify counts. Use Window > Niagara > Niagara Debugger. Expand your system, find the emitter, and look at the Events row. You will see published events per frame and dropped events. If both numbers are zero, the publisher is not running. If published is high but received is much lower, raise the listener’s Max Events Per Frame.
Step 6: Tighten collision quality for thin walls. Switch Collision Mode from SceneDepth to World if you need accurate collisions against thin or transparent geometry. Increase Collision Radius on small fast particles to avoid tunneling.
Why This Works
Niagara’s event system is a CPU-side message bus. Each frame, every emitter publishes any events it generated to named channels, and every Event Handler reads from those channels at the start of the next frame. The bus has bounded buffers so it cannot blow up under load — which is also why exceeding the per-frame budget silently drops events instead of stalling the simulation.
GPU simulations live in compute shader land. They cannot synchronously hand data to the CPU because that would force a GPU stall every frame. The intermediary CPU emitter pattern works around this by sampling GPU attributes at a known frequency and republishing them as proper events.
The Generate Collision Event toggle is separate from the bounce response so you can use the Collision module purely for physics without paying the cost of building event payloads on every hit.
"Bouncing and event-publishing are two different jobs in the same module. Niagara will happily bounce particles off everything in your level without ever telling another emitter about it — because that is what was checked in the inspector."
Related Issues
If your Niagara mesh renderer particles are not visible at all, see Fix: Unreal Niagara Mesh Renderer Not Drawing. For GPU particles disappearing at distance, check Fix: Unreal Niagara GPU Particle Disappears at Distance.
CPU sim publishes events. GPU sim writes attributes. Pick the right one.