Unity's next-generation effect tool "VFX Graph (Visual Effect Graph)" is an extremely powerful function that utilizes the GPU to process and simulate tens of thousands of particles at high speed. In game development, when you want to play an effect in synchronization with some gameplay event, such as a hit spark when a player slashes an enemy, or a particle of light when a magic circle is activated, it is common to control an event by calling an event like `VisualEffect.SendEvent("OnPlay") from a C# script. However, even though there are no errors in the script and the calling code is executed correctly, have you ever encountered a mysterious problem such as "No particles are emitted during the game" or "It only works when you press the 'Send Event' button for manual testing in the asset inspector"? In this article, we will explain the root cause of this poor event coordination using the analogy of a "radio station and a DJ", and explain the type-safe and fast C# implementation and the correct procedure for constructing nodes in the VFX Graph.

1. Understand with an analogy: ``Mismatched radio frequencies and unplugged power outlet''

In order to intuitively understand the bug where playback commands do not reach VFX Graph from C#, let's consider the relationship between ``a radio station (C# script) and a DJ in a receiving studio (VFX Graph)'' as an example.

Suppose you are sending a radio wave from a radio station's transmitter saying, "Play some music! (SendEvent)." But the studio radio receiver remains silent. When I investigated, I found two discrepancies.

The first is "frequency mismatch (typo)." I thought the transmitter was transmitting on ``FM 80.0 (OnPlay),'' but the receiver was waiting on ``FM 80.1 (onPlay).'' A single uppercase or lowercase letter, or even an unnecessary space, causes the radio waves to completely disappear into thin air and the receiver remains silent.
The second problem is that the receiver is disconnected from the outlet (wrong connection). The frequencies matched perfectly, but the receiver's power cord was connected to a storage shelf (inappropriate context) instead of to the "turntable" (Initialize block). Even if a radio wave is received, no signal is sent to the speaker (Spawn context), so no matter how long it takes, the song will not play and no particles will fly out.

This is the true nature of 'event failure bug' in VFX. It is necessary to completely match the spelling on the C# side and the definition of the Event node in VFX, and to correctly pipe the received event to the "device that generates particles (Spawn)".

2. Root causes: String mismatch and context structure issues

Technically, the reasons why `SendEvent` from C# is ignored can be summarized into the following three points.

Cause 1: Inconsistent case/spelling of event string

If the event name defined in the `Event` node of the VFX Graph (e.g. `OnStart`) and the argument string passed in the C# script (e.g. `onstart`) differ by even one character, the event will be completely ignored. Unity doesn't log friendly errors like "Event not found" to the console, which makes debugging very difficult.

Cause 2: Incorrect context to which the Event node is connected

In the VFX Graph structure, in order to generate particles, the Spawn context (or the event port connected to `Spawn`) must receive the event signal. If the Event node bypasses the `Spawn` context and is directly connected to a "particle initialization/update" block such as `Initialize` or `Update`, the state of already existing particles will be updated, but the essential action of ``spawning new particles'' will not be triggered.

Cause 3: Bind error in event attribute (VFXEventAttribute)

When sending an "event with parameters" such as occurrence coordinates and emission direction from C#, if the property ID of the attribute class is not set correctly, the value will be `(0, 0, 0)` even if the event is fired. As a result, the effect appears small at the origin at the bottom of the ground and cannot be seen on the screen.

3. Step-by-step procedure for solving the problem.

Step 1: Correct node wiring in the VFX Graph

  1. Open the VFX Graph editor and create an Event node (e.g. `OnPlay`) that will be the origin of the effect.
  2. Make sure to connect the output of this Event node to the Start (or play trigger) input port at the top of the Spawn context.
  3. Make sure the wires are flowing the output of the Spawn context (`Spawn Event`) to the input port of the following Initialize context.

Step 2: Type-safe and fast event sending implementation on the C# side

When sending an event from C#, if you pass the string (`"OnPlay"`) as it is each time, not only will overhead of string comparison occur, but it will also be a breeding ground for spelling mistakes. In practice, be sure to use `Shader.PropertyToID` and adopt a description that converts it to a hash ID before sending.

using UnityEngine;
using UnityEngine.VFX;

[RequireComponent(typeof(VisualEffect))]
public class VfxEventTrigger : MonoBehaviour
{
    private VisualEffect vfxComponent;
    
    // ID the event name in advance and cache it (preventing typos and speeding up)
 private static readonly int PlayEventId = Shader.PropertyToID("OnPlay");
 private static readonly int StopEventId = Shader.PropertyToID("OnStop");

 void Awake()
 {
 vfxComponent = GetComponent();
 }

 // Call from an in-game event (button click or attack animation)
 public void TriggerEffect()
 {
 if (vfxComponent == null) return;

 // Securely send an event by specifying an ID
 vfxComponent.SendEvent(PlayEventId);
 Debug.Log("VFX OnPlay event sent.");
 }

 public void StopEffect()
 {
 if (vfxComponent != null)
 {
 vfxComponent.SendEvent(StopEventId);
 }
 }
}

4. Advanced C# code to send dynamic parameters (coordinates/color) synchronously

If you want to send the coordinates of the source of the effect (e.g. enemy hit location) at the same time as the event, use `VFXEventAttribute`.

public void PlayHitEffectAt(Vector3 hitPosition, Color sparkColor)
{
    if (vfxComponent == null) return;

    // 1. Create an empty frame for the event attribute
 VFXEventAttribute eventAttribute = vfxComponent.CreateVFXEventAttribute();

 // 2. Set the coordinate information and color information in the attribute
 eventAttribute.SetVector3(Shader.PropertyToID("position"), hitPosition);
 eventAttribute.SetColor(Shader.PropertyToID("color"), sparkColor);

 // 3. Send event with attribute (data) attached
 vfxComponent.SendEvent(PlayEventId, eventAttribute);
}