# How to Implement Custom Events Using ImplementableEventAttribute in Ceres

> Learn to implement custom events with ImplementableEventAttribute in Ceres. Generate graph-editable nodes for visual scripting and runtime event integration.

- Repository: [AkiKurisu/ceres](https://github.com/akikurisu/ceres)
- Tags: how-to-guide
- Published: 2026-02-24

---

**Mark any C# method with `[ImplementableEvent]` to generate a graph-editable event node that routes `EventBase<T>` payloads through Ceres-Flow, enabling visual scripting integration for custom runtime events.**

The **ImplementableEventAttribute** bridges compiled C# code with Ceres-Flow visual graphs through compile-time source generation. When applied to a method, this attribute triggers the Ceres source generator to create an `ExecutableEvent` node that can be placed in a Flow Graph, along with a matching `CustomExecutionEvent` type that routes runtime `EventBase<T>` instances to that node. This architecture enables developers to fire events from code and handle them visually in the graph, or override them programmatically at runtime.

## Core Concepts and Architecture

Understanding how Ceres-Flow processes custom events requires familiarity with the components that connect attributed methods to graph execution.

| Component | Purpose | Source File |
|-----------|---------|-------------|
| **ImplementableEventAttribute** | Marks methods for source-generation of graph-editable event nodes | [`Runtime/Flow/Annotations/ImplementableEventAttribute.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Annotations/ImplementableEventAttribute.cs) |
| **CustomExecutionEvent** | Abstract base for generated event nodes; maintains ID-to-name mapping for event routing | [`Runtime/Flow/Models/Nodes/Core/CustomExecutionEvent.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/Nodes/Core/CustomExecutionEvent.cs) |
| **ProcessEvent** | Helper method that fires events through the graph's execution pipeline | [`Runtime/Flow/Models/FlowGraph.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraph.cs) (lines 33-86) |
| **OverrideEventImplementation** | Runtime extension that replaces default graph logic with custom delegates | [`Runtime/Flow/Models/FlowGraph.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraph.cs) (lines 606-608) |

The source generator creates concrete `ExecutableEvent_{ClassName}` nodes for every `EventBase<T>` type used with `[ImplementableEvent]`, as implemented in the template at `Runtime/SourceGenerators/Source~/Ceres.SourceGenerator/Generators/CustomEventGeneratorTemplate.cs`.

## Step-by-Step Implementation Guide

### Define a Custom Event Type

Create a class derived from `EventBase<T>` to serve as your event payload. This type identifies the event within the Ceres event system and carries data to graph nodes.

```csharp
using Chris.Events;

public sealed class TestGlobalEvent : EventBase<TestGlobalEvent>
{
    // Optional payload fields
    public int Value;
}

```

When the project compiles, the **Ceres.SourceGenerator** emits a concrete `ExecutableEvent_TestGlobalEvent` node deriving from `CustomExecutionEvent<TestGlobalEvent>`.

### Mark Methods with ImplementableEventAttribute

Add a method in a MonoBehaviour or any class implementing `IFlowGraphRuntime` and annotate it with `[ImplementableEvent]`.

```csharp
using Ceres.Graph.Flow;
using Ceres.Graph.Flow.Annotations;
using UnityEngine;

public class MyFlowBehaviour : MonoBehaviour, IFlowGraphRuntime
{
    public GameObject gameObject => this.gameObject; // required by IFlowGraphRuntime
    public FlowGraph Graph => /* obtain your compiled FlowGraph instance */;

    [ImplementableEvent]                     // ← Attribute triggers source generation
    private void OnTestGlobalEvent()
    {
        // This body executes when the custom event is raised
        Debug.Log("Custom event received – custom logic runs here.");
    }
}

```

The source generator detects the attribute, creates the corresponding `ExecutableEvent_TestGlobalEvent` node, and inserts a delegate port that calls `OnTestGlobalEvent` during graph execution.

### Fire Events from Code

Invoke the generated event using `ProcessEvent<T>()` or `SendEvent()` from any C# method.

```csharp
using Ceres.Graph.Flow;

public void Trigger()
{
    // Create a pooled instance of the custom event, fill payload if needed
    using var evt = TestGlobalEvent.GetPooled(42);
    // Send it to the graph – this locates the generated ExecutableEvent node
    this.SendEvent(evt);               // alternative: this.ProcessEvent<TestGlobalEvent>();
}

```

`SendEvent` routes the `EventBase` to the `CallbackEventHandler`, which looks up the event name via `CustomExecutionEvent.GetEventName(eventBase.EventTypeId)` and executes the matching graph node.

### Override Implementations at Runtime (Optional)

Replace the default graph node behavior with custom logic using `OverrideEventImplementation<TEvent>()`.

```csharp
using Ceres.Graph.Flow;

public void Init()
{
    // Replace the generated node with a custom callback
    this.OverrideEventImplementation<TestGlobalEvent>(evt =>
    {
        // Custom handling – no graph node runs
        Debug.Log($"Override: received {evt.Value}");
        // Prevent default graph execution if desired
        evt.PreventDefault();
    }).AddTo(this); // disposes automatically with the MonoBehaviour
}

```

This uses `FlowGraphRuntimeExtensions.OverrideEventImplementation<TEvent>` as implemented in [`Runtime/Flow/Models/FlowGraph.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraph.cs) (lines 606-608). The override remains active only while the returned `IDisposable` is alive.

## Complete Working Example

The following MonoBehaviour demonstrates the full workflow: event definition, method annotation, event firing, and runtime override.

```csharp
// 1️⃣ Custom event definition
using Chris.Events;

public sealed class TestGlobalEvent : EventBase<TestGlobalEvent>
{
    public int Value;
}

// 2️⃣ Behaviour exposing the event
using Ceres.Graph.Flow;
using Ceres.Graph.Flow.Annotations;
using UnityEngine;

public class TestFlowBehaviour : MonoBehaviour, IFlowGraphRuntime
{
    // Provide the runtime graph (omitted here for brevity)
    public FlowGraph Graph => /* your compiled FlowGraph instance */;
    public GameObject gameObject => this.gameObject;

    // Generated node will call this method when the event occurs
    [ImplementableEvent]
    private void OnTestGlobalEvent()
    {
        Debug.Log("Graph‑triggered custom event executed.");
    }

    // Fire the event from anywhere
    public void Send()
    {
        using var evt = TestGlobalEvent.GetPooled(123);
        this.SendEvent(evt);               // or this.ProcessEvent<TestGlobalEvent>();
    }

    // Optional runtime override
    private void Awake()
    {
        this.OverrideEventImplementation<TestGlobalEvent>(e =>
        {
            Debug.Log($"Override received value {e.Value}");
            // Stop the default graph node from running
            e.PreventDefault();
        }).AddTo(this);
    }
}

```

When `Send()` is called, the following occurs:

1. **`CallbackEventHandler`** receives `TestGlobalEvent`.
2. It queries `CustomExecutionEvent.GetEventName(eventBase.EventTypeId)` → `"ExecutableEvent_TestGlobalEvent"`.
3. The matching **`ExecutableEvent_TestGlobalEvent`** node executes, forwarding to the delegate port → **`OnTestGlobalEvent`**.

If an override was registered, the override runs instead, and the graph node is skipped because `PreventDefault()` is called.

## Key Source Files and Implementation Details

| File | Purpose | Direct Link |
|------|---------|-------------|
| [`ImplementableEventAttribute.cs`](https://github.com/akikurisu/ceres/blob/main/ImplementableEventAttribute.cs) | Defines the attribute that triggers source generation for graph-editable events | <https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Annotations/ImplementableEventAttribute.cs> |
| [`CustomExecutionEvent.cs`](https://github.com/akikurisu/ceres/blob/main/CustomExecutionEvent.cs) | Base class for generated custom events; maintains the mapping between event type IDs and graph node names | <https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/Nodes/Core/CustomExecutionEvent.cs> |
| [`FlowGraph.cs`](https://github.com/akikurisu/ceres/blob/main/FlowGraph.cs) (runtime extensions) | Contains `ProcessEvent` helpers, `OverrideEventImplementation`, and event lookup logic | <https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraph.cs#L33-L86> |
| [`TestFlowGraphObject.cs`](https://github.com/akikurisu/ceres/blob/main/TestFlowGraphObject.cs) (sample) | Demonstrates `[ImplementableEvent]`, `ProcessEvent`, and override usage in a test scenario | <https://github.com/akikurisu/ceres/blob/main/Tests/Scripts/TestFlowGraphObject.cs> |
| Source generator template | Shows how the generator creates `ExecutableEvent_{ClassName}` nodes from attributed methods | <https://github.com/akikurisu/ceres/blob/main/Runtime/SourceGenerators/Source~/Ceres.SourceGenerator/Generators/CustomEventGeneratorTemplate.cs> |

## Summary

- **ImplementableEventAttribute** bridges C# methods and Ceres-Flow graph nodes through compile-time source generation.

- Define event payloads by inheriting from `EventBase<T>`; the generator creates matching `ExecutableEvent` nodes automatically.
- Annotate methods with `[ImplementableEvent]` to expose them as graph entry points that receive runtime events.
- Fire events via `SendEvent()` or `ProcessEvent<T>()` to trigger graph execution from code.
- Use `OverrideEventImplementation<T>()` to replace generated graph logic with custom runtime delegates when needed.

## Frequently Asked Questions

### How does ImplementableEventAttribute differ from standard Unity events?

**ImplementableEventAttribute** generates a graph-editable node at compile time, whereas standard Unity events like `UnityEvent` are purely runtime constructs without visual representation. The attribute triggers the Ceres source generator to create a `CustomExecutionEvent` subclass that maps the event ID to a specific graph node name, allowing the event to be handled visually in the Flow Graph while maintaining type-safe C# integration through the `CallbackEventHandler` system.

### Can I pass custom data with ImplementableEventAttribute?

Yes, by defining a payload class that inherits from `EventBase<T>`. For example, `TestGlobalEvent : EventBase<TestGlobalEvent>` can include public fields like `int Value`. When firing the event via `SendEvent()` or `ProcessEvent<T>()`, the payload travels through the `CallbackEventHandler` to the generated `ExecutableEvent` node, where it can be accessed by ports in the Flow Graph or by the annotated C# method.

### Where is the code generated by ImplementableEventAttribute located?

The source generator creates concrete event node classes during compilation and injects them into the assembly. The generation logic resides in `Runtime/SourceGenerators/Source~/Ceres.SourceGenerator/Generators/CustomEventGeneratorTemplate.cs`, while the runtime base classes are in [`Runtime/Flow/Models/Nodes/Core/CustomExecutionEvent.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/Nodes/Core/CustomExecutionEvent.cs). The generated nodes follow the naming convention `ExecutableEvent_{ClassName}` and derive from `CustomExecutionEvent<T>`.

### How do I prevent the default graph node from executing?

Call `PreventDefault()` on the event instance within an override callback registered via `OverrideEventImplementation<TEvent>()`. This method, located in [`Runtime/Flow/Models/FlowGraph.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraph.cs) (lines 606-608), returns an `IDisposable` that maintains the override scope. When `PreventDefault()` is invoked, the event system skips the standard graph node execution, allowing your custom delegate to handle the event exclusively.