# 4. VFX GPU Events and Output Events
Part of [[VFX Graph]]. Previous: [[3. Worked Effect - Impact Sparks with One Graph]]. Next: [[5. VFX GraphicsBuffers and Audio-Reactive Effects]].
> [!note] Verification status
> GPU Events (Trigger Event blocks -> GPUEvent context -> child Initialize, Get Source Attribute / Inherit) from the 17.5 Events page. Output Event handlers (`VFXOutputEventAbstractHandler` with abstract `canExecuteInEditor` and `OnVFXOutputEvent(VFXEventAttribute)`, `outputEvent` field) from the 17.5 Output Event Handlers page and API reference; `VisualEffect.outputEventReceived` exists in the 6000.5 Scripting Reference (2026-11-10). The Cinemachine 3 call `CinemachineImpulseSource.GenerateImpulse(float)` is **not re-verified** for this note - check [[4.Impulse Noise and Extensions]]. **Code not compiled in Unity.**
## 4.1 Two directions of events
| Kind | From -> To | Runs on | Use |
|---|---|---|---|
| **GPU Event** | Particle -> another particle system in the same graph | GPU | Fireworks (burst on die), rain splashes (on collide), trails |
| **Output Event** | Spawn context -> C# | CPU | Play sound, shake camera, spawn gameplay objects when a VFX spawn happens |
GPU events never reach C#. Per-particle collisions can't call gameplay code - that's the main difference from Shuriken's `OnParticleCollision` (see [[3.Particles Spawning Particles on Contact]]).
## 4.2 GPU Events: sparks that burst into embers
1. In `VFX_Impacts`, in **Update Particle** of the spark system, add **Trigger Event On Die** (count 3).
2. Its output connects to a **GPUEvent** context, which connects to a new **Initialize Particle** (the child system).
3. Child Initialize: **Inherit Source Position**, **Inherit Source Color** (multiply darker), small upward velocity, lifetime 0.6-1.2 s.
4. Child Update: Gravity, Turbulence low. Output: tiny additive quads.
Child capacity must cover parent capacity x trigger count (4096 x 3 here) or embers silently stop spawning.
Rain splashes are the classic: rain system **Collide with Depth Buffer** -> **Trigger Event On Collide** -> splash system. Everything on the GPU.
## 4.3 Output Events: let the effect drive the feedback
In the graph: from the **Spawn** context that handles `OnImpact`, connect an **Output Event** context named `ImpactFx`. Every time that spawn fires, the component raises an output event to C# with the spawn's attributes (position, etc.).
### Script: camera shake from the effect
```csharp
// Assets/Scripts/VFX/VfxOutputEventImpulse.cs
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.VFX.Utility;
/// Listens to an Output Event on the same VisualEffect and fires a Cinemachine Impulse.
/// Set "Output Event" on the component to the event name in the graph (e.g. ImpactFx).
[RequireComponent(typeof(VisualEffect))]
public class VfxOutputEventImpulse : VFXOutputEventAbstractHandler
{
public CinemachineImpulseSource impulse;
public float force = 0.4f;
[Tooltip("Ignore impacts farther than this from the listener")]
public float maxDistance = 25f;
public Transform listener; // usually the camera or player
static readonly int kPosition = Shader.PropertyToID("position");
static readonly int kSize = Shader.PropertyToID("size");
// Required by the abstract base: shake only in Play mode.
public override bool canExecuteInEditor => false;
public override void OnVFXOutputEvent(VFXEventAttribute eventAttribute)
{
if (impulse == null) return;
Vector3 p = eventAttribute.GetVector3(kPosition);
float size = eventAttribute.HasFloat(kSize) ? eventAttribute.GetFloat(kSize) : 0.05f;
float falloff = 1f;
if (listener != null)
{
float d = Vector3.Distance(listener.position, p);
if (d > maxDistance) return;
falloff = 1f - d / maxDistance;
}
// Size encodes damage (part 3) - bigger hits shake harder.
impulse.GenerateImpulse(force * falloff * Mathf.Clamp(size / 0.05f, 0.3f, 3f));
}
}
```
### Script: audio from the effect
```csharp
// Assets/Scripts/VFX/VfxOutputEventAudio.cs
using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.VFX.Utility;
[RequireComponent(typeof(VisualEffect))]
public class VfxOutputEventAudio : VFXOutputEventAbstractHandler
{
public AudioClip[] clips;
[Range(0f, 1f)] public float volume = 0.8f;
public Vector2 pitchRange = new Vector2(0.92f, 1.08f);
static readonly int kPosition = Shader.PropertyToID("position");
// Required by the abstract base: no audio spam while scrubbing in the editor.
public override bool canExecuteInEditor => false;
public override void OnVFXOutputEvent(VFXEventAttribute eventAttribute)
{
if (clips == null || clips.Length == 0) return;
Vector3 p = eventAttribute.GetVector3(kPosition);
AudioClip clip = clips[Random.Range(0, clips.Length)];
// Replace with your pooled AudioSource service for production:
// see "2. Pooled AudioSource Service" in the Interactable Audio series.
AudioSource.PlayClipAtPoint(clip, p, volume);
}
}
```
`PlayClipAtPoint` creates a GameObject per call - fine for a demo, wrong for a shooter. Swap in [[2. Pooled AudioSource Service]] and route it through the SFX mixer group ([[1. Audio Mixer Groups and Routing]]); randomize pitch there.
`canExecuteInEditor` is abstract on the base class (it's `[ExecuteAlways]`), so every handler must override it. The `VFXEventAttribute` you receive is temporary - copy values out, don't store it.
**Why couple feedback to the VFX?** One gameplay call (`ImpactVfxService.Spawn`) now produces sparks, embers, shake and sound, all scaled by the same `size` value. The graph is the **subject**, the handlers are **observers** ([[Game Programming Patterns (Review)]]); designers can add or remove feedback channels without touching weapon code. The trade-off: feedback depends on the effect being active - if the VFX is disabled by the capability gate (part 1), wire the fallback to call the same handlers.
## 4.4 Exercise
Turn the feedback channels on one at a time in a playtest (VFX only; + audio; + shake; + hit-stop from part 2). Ask testers to rate how "hard" a single hit felt. Level Up!'s point about layered feedback shows up in the numbers.
## Sources
- Events and GPU Events: https://docs.unity3d.com/Packages/
[email protected]/manual/Events.html
- Output Event Handlers: https://docs.unity3d.com/Packages/
[email protected]/manual/OutputEventHandlers.html
- `VFXOutputEventAbstractHandler` API: https://docs.unity3d.com/Packages/
[email protected]/api/UnityEngine.VFX.Utility.VFXOutputEventAbstractHandler.html
- `VisualEffect.outputEventReceived`: https://docs.unity3d.com/6000.5/Documentation/ScriptReference/VFX.VisualEffect.html
- Observer chapter: https://gameprogrammingpatterns.com/observer.html