# 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