Skip to main content

Programmatic Flow

GES Β· Runtime API

Build runtime Triggers and Chains in C# with per-link delays, conditions, priorities, and argument transformers.

For programmers wiring event relationships at runtime, when the Visual Flow Graph cannot cover dynamically created objects.

Use AddTriggerEvent or AddChainEvent to connect events as runtime content is created. Trigger nodes start eligible branches, and Chain nodes advance through a sequence. Keep the returned handle to remove the corresponding node when its content unloads.


Core Concepts: Triggers vs. Chains​

Before coding, it is crucial to understand the difference between the two flow types handled by the internal managers (GameEventTriggerManager and GameEventChainManager).

Feature⚑ Triggers (Fan-Out)πŸ”— Chains (Sequence)
Execution ModeParallel (Fire-and-Forget)Sequential (Blocking)
Failure HandlingIndependent (If A fails, B still runs)Strict (If A fails, the chain stops)
TimingSynchronous (unless delay is used)Coroutine-based (supports wait & duration)
OrderingSorted by PriorityExecuted in Order of Addition
Use CaseVFX, Achievements, UI UpdatesCutscenes, Tutorials, Turn Logic

1. Triggers (Parallel Execution)​

Use AddTriggerEvent to make one event automatically fire others. All registered triggers execute immediately (or after their individual delay) when the source event is raised.

Basic Usage​

When onPlayerDeath fires, automatically fire onPlayDeathSound and onShowGameOverUI.

[GameEventDropdown] public GameEvent onPlayerDeath;
[GameEventDropdown] public GameEvent onPlayDeathSound;
[GameEventDropdown] public GameEvent onShowGameOverUI;

void Awake()
{
// These happen effectively at the same time
onPlayerDeath.AddTriggerEvent(onPlayDeathSound);
onPlayerDeath.AddTriggerEvent(onShowGameOverUI);
}

Advanced Configuration (Priority & Conditions)​

You can inject logic into the connection without modifying the events themselves.

// 1. High Priority: Heal first
onPotionUsed.AddTriggerEvent(
targetEvent: onRegenHealth,
priority: 100 // Higher numbers run first
);

// 2. Low Priority: Play sound after logic starts
onPotionUsed.AddTriggerEvent(
targetEvent: onPlaySound,
delay: 0.2f, // Optional delay
priority: 10
);

// 3. Conditional: Only trigger particle if graphics settings allow
onPotionUsed.AddTriggerEvent(
targetEvent: onParticleEffect,
condition: () => GameSettings.EnableParticles
);
Automatic Argument Passing

By default (passArgument: true), Triggers attempt to pass the data from the Source to the Target. If types match (e.g., int to int), it flows automatically. If types mismatch, you need a Transformer (see below).


2. Chains (Sequential Execution)​

Use AddChainEvent to build a strictly ordered execution list on a single event.

The Sequence Logic (The Queue)​

When you add multiple chain nodes to the same source event, they form a Queue. The system executes them one by one, waiting for the previous node's duration to finish before starting the next node.

This allows you to orchestrate a complex timeline (A β†’ Wait β†’ B β†’ Wait β†’ C) managed entirely by the source event, without linking B directly to C.

[GameEventDropdown] public GameEvent onTurnStart;
[GameEventDropdown] public GameEvent onDrawCard;
[GameEventDropdown] public GameEvent onRefreshMana;

void Awake()
{
// --- The "Turn Start" Timeline ---

// Step 1: Draw Card
// Setting 'duration' means: "Execute this, then WAIT 0.5s before processing the next item in the list."
onTurnStart.AddChainEvent(onDrawCard, duration: 0.5f);

// Step 2: Refresh Mana
// This runs automatically AFTER Step 1 finishes (and its 0.5s duration passes).
onTurnStart.AddChainEvent(onRefreshMana);

// Note: I attach both to 'onTurnStart'.
// I do NOT attach Step 2 to 'onDrawCard', because I don't want
// drawing a card from a spell to accidentally trigger mana refresh.
}

One-frame wait (waitForCompletion)​

Set waitForCompletion to pause for one frame after the node's duration. To sequence work after a coroutine or task, raise a separate event when that operation completes.

// The chain waits one frame after raising onPlayCutscene.
onLevelEnd.AddChainEvent(onPlayCutscene, waitForCompletion: true);

// The next event runs after that frame.
onLevelEnd.AddChainEvent(onLoadNextLevel);
Chain Breaking

If a condition returns false or an exception occurs in a Chain Node, the entire subsequent chain is halted. This is useful for conditional logic (e.g., "Stop combo attack if enemy blocked").


Data Flow & Transformers​

Use an argument transformer to convert a payload to the target event type or extract one value from it.

1. Complex to Void (Filter)​

Trigger a generic event only based on specific data.

// Source: Damage Event (float amount)
// Target: Critical Hit Event (Void)
onDamageTaken.AddTriggerEvent(
targetEvent: onCriticalHitEffect,
condition: (amount) => amount > 50f, // Only if damage > 50
passArgument: false // Target is void, don't pass the float
);

2. Simple Transformation (Type Casting)​

Map a complex object event to a simple primitive event.

  • Source: EnemyGameEvent (OnEnemyKilled)
  • Target: Int32GameEvent (OnAddXP)
[GameEventDropdown] public EnemyGameEvent onEnemyKilled;
[GameEventDropdown] public Int32GameEvent onAddXP;

void Awake()
{
// Extract the 'xpValue' from the Enemy object and pass it to the int event
onEnemyKilled.AddTriggerEvent(
targetEvent: onAddXP,
passArgument: true,
argumentTransformer: (enemy) => enemy.xpValue
);
}

3. Sender & Argument Transformation​

For GameEvent<TSender, TArgs>, the transformer receives both parameters.

// Source: Player picked up item (Sender: Player, Args: ItemData)
// Target: Notification (string)
onItemPickup.AddTriggerEvent(
targetEvent: onShowNotification,
passArgument: true,
argumentTransformer: (player, item) => $"{player.Name} found a {item.Rarity} item!"
);

Lifecycle Management​

Unlike standard listeners (AddListener), dynamic Triggers and Chains return a Handle. You must manage these handles to prevent memory leaks or unwanted logic persistence, especially when pooling objects.

Using Handles​

private TriggerHandle _triggerHandle;

void OnEnable()
{
// Save the handle
_triggerHandle = onDoorOpen.AddTriggerEvent(onLightOn);
}

void OnDisable()
{
// Use the handle to remove ONLY this specific link
if (_triggerHandle != null)
{
onDoorOpen.RemoveTriggerEvent(_triggerHandle);
_triggerHandle = null;
}
}

Bulk Cleanup​

If an object is being destroyed or returned to a pool, you can wipe all dynamic logic associated with an event.

void OnDestroy()
{
// Removes ALL dynamic triggers targeting this event
myEvent.RemoveAllTriggerEvents();

// Removes ALL dynamic chains targeting this event
myEvent.RemoveAllChainEvents();
}

API Summary​

Method SignatureReturnsDescription
Trigger RegistrationParallel / Fire-and-Forget
AddTriggerEvent(GameEventBase target, float delay, Func<bool> condition, int priority)TriggerHandleAdds a trigger to a Void event.
AddTriggerEvent(GameEventBase target, float delay, Func<T, bool> condition, bool passArg, Func<T, object> transformer, int priority)TriggerHandleAdds a trigger to a Typed event.
AddTriggerEvent(GameEventBase target, float delay, Func<TSender, TArgs, bool> condition, bool passArg, Func<TSender, TArgs, object> transformer, int priority)TriggerHandleAdds a trigger to a Sender event.
Chain RegistrationSequential / Blocking
AddChainEvent(GameEventBase target, float delay, float duration, Func<bool> condition, bool wait)ChainHandleAdds a chain step to a Void event.
AddChainEvent(GameEventBase target, float delay, float duration, Func<T, bool> condition, bool passArg, Func<T, object> transformer, bool wait)ChainHandleAdds a chain step to a Typed event.
AddChainEvent(GameEventBase target, float delay, float duration, Func<TSender, TArgs, bool> condition, bool passArg, Func<TSender, TArgs, object> transformer, bool wait)ChainHandleAdds a chain step to a Sender event.
CleanupRemoval
RemoveTriggerEvent(TriggerHandle handle)voidRemoves a specific trigger node.
RemoveChainEvent(ChainHandle handle)voidRemoves a specific chain node.
RemoveAllTriggerEvents()voidClears all dynamic triggers.
RemoveAllChainEvents()voidClears all dynamic chains.