Skip to main content

11 Chain Event: Sequential Execution Pipeline

GES ยท Examples

Run a five-step launch protocol where each node waits for the one before it, a condition node gates the sequence, and one failed check stops everything downstream.

For developers scripting ordered sequences: cutscenes, weapon charge cycles, tutorial steps.

Overviewโ€‹

The launch sequence checks the safety lock before it starts the timed steps. In the FlowGraph, each Chain node controls when its next step can run. Test both safety-lock states to see where the sequence stops or continues.

๐Ÿ”ˆ Hover for sound

Demo Sceneโ€‹

Assets/TinyGiants/GameEventSystem/Demo/11_ChainEvent/11_ChainEvent.unity

Scene Compositionโ€‹

Visual Elements:

  • ๐Ÿ”ด Turret_A (Left) - Red launcher
  • ๐Ÿ”ต Turret_B (Right) - Blue launcher
  • ๐ŸŽฏ TargetDummy - Center capsule target
  • ๐Ÿ“บ HoloDisplay - Status display panel
    • Shows "SAFELOCK READY" when safety is off
    • Shows "SAFELOCK ACTIVED" when safety is on

UI Layer (Canvas):

  • ๐ŸŽฎ Three Buttons - Bottom of the screen
    • "Launch A" โ†’ Triggers ChainEventRaiser.RequestLaunchA()
    • "Launch B" โ†’ Triggers ChainEventRaiser.RequestLaunchB()
    • "Toggle SafeLock" (Orange) โ†’ Triggers ChainEventReceiver.ToggleSafetyLock()

Game Logic Layer:

  • ๐Ÿ“ค ChainEventRaiser - Sequence initiator

    • Only references ONE entry point: 0_StartSequence
    • No knowledge of downstream pipeline steps
  • ๐Ÿ“ฅ ChainEventReceiver - Step executor

    • Contains 5 methods for each pipeline stage
    • Exposes IsSafetyCheckPassed property for condition validation
    • Contains isSafetyLockDisengaged flag (toggle-able)

How to Interactโ€‹

The 5-Step Launch Protocolโ€‹

One root event (0_StartSequence) triggers a sequential pipeline with validation, delays, and async waiting.


Step 1: Enter Play Modeโ€‹

Press the Play button in Unity.

Initial State:

  • Safety lock: DISENGAGED (default)
  • HoloDisplay: "SAFELOCK READY"
  • Both turrets idle

Step 2: Test Successful Launch (Safety Off)โ€‹

Current State Check:

  • Ensure HoloDisplay shows "SAFELOCK READY"
  • If not, click "Toggle SafeLock" to turn safety OFF

Click "Launch A":

Sequential Execution:

[Step 1: System Check] - Immediate

  • ๐Ÿ” Condition Node evaluates ChainEventReceiver.IsSafetyCheckPassed property
  • Property checks isSafetyLockDisengaged flag
  • Result: TRUE โœ…
  • Console: [Chain Step 1] Turret_A Checking...
  • Chain proceeds to Step 2

[Step 2: Charge]

  • VFX: A charging particle effect spawns at the turret
  • The chain waits for the configured 1.0s Duration before it fires
  • Console: [Chain Step 2] Turret_A Charging...
  • Graph waits exactly 1.0s before continuing
  • After delay, chain proceeds to Step 3

[Step 3: Fire] - Immediate

  • ๐Ÿš€ Projectile instantiated and launched toward target
  • Muzzle flash VFX at turret
  • Console: [Chain Step 3] Turret_A FIRED payload: 500
  • Projectile travels to target
  • Chain immediately proceeds to Step 4

[Step 4: Cool Down] - Delay, duration, and a one-frame wait

  • ๐Ÿ’จ Steam VFX particle system spawns
  • The node waits 0.5 seconds before dispatch, then holds for 1 second and one additional frame
  • Console: [Chain Step 4] Turret_A Cooldowning.
  • The node uses its configured duration and adds a one-frame wait.
  • The chain proceeds to Step 5 after the configured wait

[Step 5: Archive] - Immediate (Arguments Blocked)

  • ๐Ÿ“ Final logging step
  • PassArgument = FALSE in graph โ†’ receives default/null values
  • Console: [Chain Step 5] Archived. Data Status: CLEAN
  • Turret unlocked for next use
  • Chain completes successfully โœ…

Timeline:

0.0s โ†’ Step 1: System Check (instant)
0.0s โ†’ Step 2: Charge starts
1.0s โ†’ Step 3: Fire (after charge delay)
1.5s โ†’ Step 4: CoolDown starts
~2.5s โ†’ Step 5: Archive (after duration and one frame)
~2.5s โ†’ Sequence complete

Result: โœ… Full 5-step launch sequence executed successfully.


Step 3: Test Failed Launch (Safety On)โ€‹

Click "Toggle SafeLock":

  • Safety flag changes: isSafetyLockDisengaged = false
  • HoloDisplay updates: "SAFELOCK ACTIVED"
  • UI button color changes to orange (visual warning)
  • Console: [Chain Settings] Safety Lock Disengaged: False

Click "Launch B":

Sequential Execution:

[Step 1: System Check] - FAILS โŒ

  • ๐Ÿ” Condition Node evaluates ChainEventReceiver.IsSafetyCheckPassed
  • Property checks isSafetyLockDisengaged โ†’ finds FALSE
  • Property executes failure feedback:
    • ๐Ÿšจ Red alarm vignette flashes 3 times
    • Alarm sound plays
    • Console: [Chain Blocked] Safety Check Failed. Sequence stopped immediately.
  • Condition returns FALSE
  • ๐Ÿ›‘ CHAIN TERMINATES HERE

[Steps 2-5] - NEVER EXECUTE

  • โŒ No charging VFX
  • โŒ No projectile fired
  • โŒ No steam cooldown
  • โŒ No archive log

Result: โŒ Launch aborted at gate. Steps 2-5 never ran.

๐Ÿ”ด Critical Chain Behavior

When a Chain node's condition fails:

  1. Immediate Termination - Execution stops at that node
  2. No Downstream Execution - Subsequent nodes never run
  3. No Partial Completion - All-or-nothing behavior
  4. Early Cleanup - Resources unlocked immediately

This is fundamentally different from Trigger Events, where failed conditions just skip individual branches while others continue.


Scene Architectureโ€‹

Chain vs Trigger: The Fundamental Differenceโ€‹

Trigger Event (Parallel):

โšก Root Event: OnInteraction
โ”‚
โ”œโ”€ ๐Ÿ”ฑ Branch A: [ ๐Ÿ›ก๏ธ Guard: `HasKey == true` ]
โ”‚ โ””โ”€ ๐Ÿš€ Action: OpenDoor() โž” โœ… Condition Passed: Executing...
โ”‚
โ”œโ”€ ๐Ÿ”ฑ Branch B: [ ๐Ÿ›ก๏ธ Guard: `PlayerLevel >= 10` ]
โ”‚ โ””โ”€ ๐Ÿš€ Action: GrantBonusXP() โž” โŒ Condition Failed: Branch Skipped
โ”‚
โ””โ”€ ๐Ÿ”ฑ Branch C: [ ๐Ÿ›ก๏ธ Guard: `Always True` ]
โ””โ”€ ๐Ÿš€ Action: PlaySound("Click") โž” โœ… Condition Passed: Executing...
โ”‚
๐Ÿ“Š Summary: 2 Paths Executed | 1 Path Skipped | โšก Timing: Concurrent

Chain Event (Sequential):

๐Ÿ† Initiation: Root Event
โ”‚
โ”œโ”€ 1๏ธโƒฃ [ Step 1 ] โž” ๐Ÿ›ก๏ธ Guard: `Condition A`
โ”‚ โ””โ”€ โณ Status: WAIT for completion... โœ… Success
โ”‚
โ”œโ”€ 2๏ธโƒฃ [ Step 2 ] โž” ๐Ÿ›ก๏ธ Guard: `Condition B`
โ”‚ โ””โ”€ โณ Status: WAIT for completion... โœ… Success
โ”‚
โ”œโ”€ 3๏ธโƒฃ [ Step 3 ] โž” ๐Ÿ›ก๏ธ Guard: `Condition C`
โ”‚ โ””โ”€ โณ Status: WAIT for completion... โŒ FAILED!
โ”‚
โ””โ”€ ๐Ÿ›‘ [ TERMINATED ] โž” Logic Chain Halts
โ””โ”€ โญ๏ธ Step 4: [ SKIPPED ]
โ”‚
๐Ÿ“Š Final Result: Aborted at Step 3 | โณ Mode: Strict Blocking

When to Use Each:

PatternUse ChainUse Trigger
Cutsceneโœ… Sequential shotsโŒ Steps out of order
Combat SystemโŒ Rigid order not neededโœ… Parallel systems
Tutorialโœ… Must finish step 1 before step 2โŒ Steps can overlap
Weapon Chargeโœ… Charge โ†’ Fire โ†’ CooldownโŒ Order matters
AchievementโŒ Independent checksโœ… Multiple triggers

Event Definitionsโ€‹

Game Event Editor

Event NameTypeRoleStep
0_StartSequenceGameObjectDamageInfoGameEventRoot (Gold)Entry
1_SystemCheckGameObjectDamageInfoGameEventChain (Green)1
2_ChargeGameObjectDamageInfoGameEventChain (Green)2
3_FireGameObjectDamageInfoGameEventChain (Green)3
4_CoolDownGameObjectDamageInfoGameEventChain (Green)4
5_ArchiveGameObjectDamageInfoGameEventChain (Green)5

Key Insight:

  • Root raises the chain
  • Chain nodes auto-trigger sequentially
  • Code only calls .Raise() on rootโ€”graph handles rest

Flow Graph Configurationโ€‹

Click "Flow Graph" button to visualize the sequential pipeline:

Flow Graph Overview

Graph Structure (Left to Right):

Node 1: 0_StartSequence (Root, Red)

  • Entry point raised by code
  • Type: GameObjectDamageInfoGameEvent
  • Connects to first chain node

Node 2: 1_SystemCheck (Chain, Green)

  • โœ… Condition Node - Gate keeper
  • Condition: ChainEventReceiver.IsSafetyCheckPassed == true
    • Evaluates scene object property at runtime
    • If false โ†’ chain breaks immediately
  • Action: ChainEventReceiver.OnSystemCheck(sender, args)
  • Green checkmark icon indicates condition enabled
  • PassArgument: โœ“ Pass (full data forwarded)

Node 3: 2_Charge (Chain, Green)

  • โฑ๏ธ Delay Node - Timed pause
  • Delay: 1.0 seconds (shown as โฑ๏ธ 1s icon)
  • Action: ChainEventReceiver.OnStartCharging(sender, args)
  • Graph freezes here for exactly 1 second
  • PassArgument: โœ“ Pass

Node 4: 3_Fire (Chain, Green)

  • ๐ŸŽฏ Action Node - Standard execution
  • Action: ChainEventReceiver.OnFireWeapon(sender, args)
  • No delay, no condition
  • Executes immediately after previous step
  • PassArgument: โœ“ Pass

Node 5: 4_CoolDown (Chain, Green)

  • Wait adds one frame after the node's duration
  • Delay: 0.5s (minimum wait)
  • WaitForCompletion: โœ“ Checked (shown as โฑ๏ธ 1s icon
  • Action: ChainEventReceiver.OnCoolDown(sender, args)
  • PassArgument: โœ“ Pass

Node 6: 5_Archive (Chain, Green)

  • ๐Ÿ”’ Filter Node - Data sanitization
  • Action: ChainEventReceiver.OnSequenceArchived(sender, args)
  • PassArgument: ๐Ÿ”ด Static (argument blocked)
    • Even though previous nodes passed full data
    • This node receives default/null values
    • Demonstrates data firewall at end of chain
  • Final stepโ€”no downstream nodes

Connection Lines:

  • ๐ŸŸข Green "CHAIN" lines - Sequential flow
    • Each output port connects to next input port
    • Linear topologyโ€”no branching
    • Execution follows line left-to-right

Legend:

  • ๐Ÿ”ด Root Node - Entry point (raised by code)
  • ๐ŸŸข Chain Node - Auto-triggered in sequence
  • โœ… Checkmark Icon - Condition enabled
  • โฑ๏ธ Clock Icon - Delay or wait configured
  • ๐Ÿ”’ Static Icon - Arguments blocked
๐ŸŽจ Visual Pipeline Benefits

The Chain Graph provides instant understanding of:

  • Sequential Order - Left-to-right flow shows exact execution order
  • Validation Gates - Condition nodes act as checkpoints
  • Timing Control - Delay/wait icons show pause points
  • Data Flow - PassArgument toggles show where data is filtered
  • Failure Points - Condition nodes show where chain can break

This is infinitely cleaner than reading a coroutine with nested yield return statements!


Sender Setup (ChainEventRaiser)โ€‹

Select the ChainEventRaiser GameObject:

ChainEventRaiser Inspector

Chain Entry Point:

  • Sequence Start Event: 0_StartSequence
    • Tooltip: "The Start Node of the Chain Graph"
    • Only references the rootโ€”downstream is handled by graph

Turrets:

  • Turret A: Turret_A (GameObject), Head A (Transform)
  • Turret B: Turret_B (GameObject), Head B (Transform)

Targeting:

  • Hit Target: TargetDummy (Transform)

Critical Observation: Like Trigger demos, sender only knows about ONE event. The 5-step pipeline is completely abstracted into the graph.


Receiver Setup (ChainEventReceiver)โ€‹

Select the ChainEventReceiver GameObject:

ChainEventReceiver Inspector

Scene References:

  • Chain Event Raiser: ChainEventRaiser (for unlock callbacks)
  • Holo Text: LogText (TextMeshPro) - displays lock status

Target References:

  • Target Dummy, Target Rigidbody

VFX & Projectiles:

  • Projectile Prefab: Projectile (TurretProjectile)
  • Charge VFX: TurretBuffAura (Particle System) - step 2
  • Fire VFX: MuzzleFlashVFX (Particle System) - step 3
  • Steam VFX: SteamVFX (Particle System) - step 4
  • Hit Normal/Crit VFX, Floating Text Prefab

Audio:

  • Hit Clip, UI Clip, Alarm Clip

Screen:

  • Screen Group: AlarmVignette (CanvasGroup) - red flash on failure

Simulation Settings:

  • โœ… Is Safety Lock Disengaged: TRUE (default)
    • Controls whether Step 1 condition passes
    • Toggle-able via "Toggle SafeLock" button

Code Breakdownโ€‹

ChainEventRaiser.cs (Sender)โ€‹

using UnityEngine;
using TinyGiants.GES.Runtime;

public class ChainEventRaiser : MonoBehaviour
{
[Header("Chain Entry Point")]
[Tooltip("The Start Node of the Chain Graph.")]
[GameEventDropdown]
public GameObjectDamageInfoGameEvent sequenceStartEvent;

[Header("Turrets")]
public GameObject turretA;
public GameObject turretB;
// ... head transforms & hit target reference ...

private bool _isBusyA;
private bool _isBusyB;

/// <summary>
/// UI Button A: Request Launch for Turret A.
///
/// CRITICAL: Only raises the ROOT event.
/// The Chain Graph orchestrates all 5 downstream steps automatically.
/// </summary>
public void RequestLaunchA()
{
if (sequenceStartEvent == null) return;

Debug.Log("<color=cyan>[Raiser] Requesting Launch Protocol A...</color>");
_isBusyA = true;

// Build the data payload
DamageInfo info = new DamageInfo(500f, true, DamageType.Physical,
hitTarget.position, "Commander");

// THE MAGIC: Single .Raise() starts entire 5-step chain
// Graph automatically executes:
// 1. System Check (with condition)
// 2. Charge (1s duration)
// 3. Fire (immediate)
// 4. Cool Down (with wait-for-completion)
// 5. Archive (with blocked arguments)
sequenceStartEvent.Raise(turretA, info);
}

/// <summary>
/// UI Button B: Request Launch for Turret B.
/// Same logic, different turret.
/// </summary>
public void RequestLaunchB()
{
if (sequenceStartEvent == null) return;

Debug.Log("<color=orange>[Raiser] Requesting Launch Protocol B...</color>");
_isBusyB = true;

DamageInfo info = new DamageInfo(200f, false, DamageType.Physical,
hitTarget.position, "Commander");
sequenceStartEvent.Raise(turretB, info);
}

// Unlock methods called by receiver when sequence completes or fails
public void UnlockTurretA() => _isBusyA = false;
public void UnlockTurretB() => _isBusyB = false;
}

Key Points:

  • ๐ŸŽฏ Single Event Reference - Only knows root event
  • ๐Ÿ“ก Zero Pipeline Knowledge - No idea about 5 steps
  • ๐Ÿ”“ Unlock Callbacks - Receiver signals completion/failure
  • ๐ŸŽฌ Maximum Decoupling - All sequence logic in graph

ChainEventReceiver.cs (Listener)โ€‹

using UnityEngine;
using System.Collections;

public class ChainEventReceiver : MonoBehaviour
{
[Header("Simulation Settings")]
[Tooltip("If TRUE, passes check. If FALSE, chain breaks at Step 1.")]
public bool isSafetyLockDisengaged = true;

// ... scene references, VFX & audio fields (see demo source) ...

/// <summary>
/// Property accessed by '1_SystemCheck' Node Condition.
///
/// Graph configuration: Scene Object โ†’ Property โ†’ IsSafetyCheckPassed
///
/// CRITICAL: This is evaluated BEFORE the node action executes.
/// If this returns false, the chain terminates immediately.
/// </summary>
public bool IsSafetyCheckPassed
{
get
{
bool result = true;

if (!isSafetyLockDisengaged)
{
// FAIL PATH: Safety lock is engaged
result = false;

Debug.LogWarning(
"<color=red>[Chain Blocked] Safety Check Failed. " +
"Sequence stopped immediately.</color>");

// Visual feedback for failure
StopCoroutine(nameof(ScreenRoutine));
if (screenGroup) StartCoroutine(ScreenRoutine());
}

return result;
}
}

/// <summary>
/// Toggles the safety lock status. Bind this to UI Button.
/// </summary>
public void ToggleSafetyLock()
{
if (UIClip) _audioSource.PlayOneShot(UIClip);

isSafetyLockDisengaged = !isSafetyLockDisengaged;

// Update UI
string text = isSafetyLockDisengaged ? "SAFELOCK READY" : "SAFELOCK ACTIVED";
if (holoText) holoText.text = text;

Debug.Log($"[Chain Settings] Safety Lock Disengaged: {isSafetyLockDisengaged}");
}

/// <summary>
/// [Chain Step 1] System Check
/// Bound to '1_SystemCheck' chain node.
///
/// Note: This action runs AFTER the condition passed.
/// If condition failed, this method never executes.
/// </summary>
public void OnSystemCheck(GameObject sender, DamageInfo args)
{
bool isA = sender != null && sender.name.Contains("Turret_A");

// If execution reaches here, condition passed
// Still handle potential edge cases
if (!IsSafetyCheckPassed)
{
// Unlock turret since sequence failed
if (isA) chainEventRaiser.UnlockTurretA();
else chainEventRaiser.UnlockTurretB();
}

Debug.Log($"[Chain Step 1] {sender.name} Checking...");
}

/// <summary>
/// [Chain Step 2] Charge
/// Bound to '2_Charge'. The chain waits for its 1.0s duration after dispatch.
/// </summary>
public void OnStartCharging(GameObject sender, DamageInfo args)
{
if (chargeVFX)
{
var vfx = Instantiate(chargeVFX, sender.transform.position + Vector3.up * 1.5f,
Quaternion.identity);
vfx.transform.SetParent(sender.transform);
vfx.Play();
Destroy(vfx.gameObject, 1.2f);
}

Debug.Log($"[Chain Step 2] {sender.name} Charging...");
}

/// <summary>
/// [Chain Step 3] Fire
/// Bound to '3_Fire' chain node.
///
/// Spawns projectile and launches toward target.
/// This executes immediately after Step 2 completes.
/// </summary>
public void OnFireWeapon(GameObject sender, DamageInfo args)
{
// Spawn muzzle flash
if (fireVFX)
{
Vector3 spawnPos = sender.transform.position +
sender.transform.forward * 1.5f + Vector3.up * 1.5f;
var vfx = Instantiate(fireVFX, spawnPos, sender.transform.rotation);
vfx.Play();
Destroy(vfx.gameObject, 2.0f);
}

// Launch projectile
if (projectilePrefab != null)
{
var muzzlePos = sender.transform.Find("Head/Barrel/MuzzlePoint");
var shell = Instantiate(projectilePrefab, muzzlePos.position,
sender.transform.rotation);

shell.Initialize(args.hitPoint, 20f, () =>
{
// Impact callback
if (hitClip) _audioSource.PlayOneShot(hitClip);

// Spawn hit VFX, floating text, apply physics...
ParticleSystem vfxToPlay = args.isCritical ? hitCritVFX : hitNormalVFX;

if (args.isCritical)
StartCoroutine(ShakeCameraRoutine(0.2f, 0.4f));

// ... (VFX, physics, text logic) ...
});
}

Debug.Log($"[Chain Step 3] {sender.name} FIRED payload: {args.amount}");
}

/// <summary>
/// [Chain Step 4] Cool Down
/// Bound to '4_CoolDown' chain node with WaitForCompletion.
///
/// The graph waits for this coroutine to finish before proceeding to Step 5.
/// The configured chain duration determines when the sequence advances.
/// </summary>
public void OnCoolDown(GameObject sender, DamageInfo args)
{
if (steamVFX)
{
var vfx = Instantiate(steamVFX, sender.transform.position + Vector3.up,
Quaternion.Euler(-90, 0, 0));
vfx.Play();
Destroy(vfx.gameObject, 2.0f);
}

Debug.Log($"[Chain Step 4] {sender.name} Cooldowning.");
}

/// <summary>
/// [Chain Step 5] Archive
/// Bound to '5_Archive' chain node with PassArgument = FALSE.
///
/// CRITICAL: Even though previous steps passed full DamageInfo,
/// this node receives DEFAULT/NULL values due to graph configuration.
///
/// Demonstrates data firewallโ€”can sanitize sensitive data at end of chain.
/// </summary>
public void OnSequenceArchived(GameObject sender, DamageInfo args)
{
bool isA = sender != null && sender.name.Contains("Turret_A");

// Unlock turret for next use
if (isA) chainEventRaiser.UnlockTurretA();
else chainEventRaiser.UnlockTurretB();

// Check if data was successfully blocked
bool isClean = (args == null || args.amount == 0);
string logMsg = isClean ? "<color=cyan>CLEAN</color>" : "<color=red>LEAKED</color>";

Debug.Log($"[Chain Step 5] Archived. Data Status: {logMsg}");
}

private IEnumerator ScreenRoutine()
{
// Red alarm vignette flash animation
int flashes = 3;
float flashDuration = 0.5f;

for (int i = 0; i < flashes; i++)
{
if (alarmClip) _audioSource.PlayOneShot(alarmClip);

// Sine wave alpha animation
float t = 0f;
while (t < flashDuration)
{
t += Time.deltaTime;
float alpha = Mathf.Sin((t / flashDuration) * Mathf.PI);
screenGroup.alpha = alpha * 0.8f;
yield return null;
}

screenGroup.alpha = 0f;
yield return new WaitForSeconds(0.1f);
}
}
}

Key Points:

  • ๐ŸŽฏ 5 Independent Methods - Each handles one pipeline stage
  • โœ… Property for Condition - IsSafetyCheckPassed evaluated by graph
  • โฑ๏ธ Timing Agnostic - Methods don't know about delays
  • ๐Ÿ”’ Data Firewall - Step 5 receives sanitized data
  • ๐ŸŽฌ Completion Callbacks - Unlocks turrets on success/failure

Key Takeawaysโ€‹

ConceptImplementation
๐Ÿ”— Sequential ExecutionNodes execute one-by-one in strict order
โœ… Validation GatesCondition nodes terminate chain if failed
โฑ๏ธ Delay NodesFixed-time pauses between steps
๐Ÿ• Wait NodesOne additional frame after dispatch and duration
๐Ÿ”’ Data FilteringPassArgument controls data flow per node
๐Ÿ›‘ Early TerminationFailed condition stops entire chain
๐ŸŽฏ All-or-NothingChain completes fully or terminates early
๐ŸŽ“ Design Insight

Chain Events are perfect for:

  • Cutscenes - Shot 1 โ†’ Shot 2 โ†’ Shot 3 in exact order
  • Weapon Sequences - Charge โ†’ Fire โ†’ Cooldown โ†’ Reload
  • Tutorial Steps - Must complete step N before step N+1
  • Crafting Recipes - Sequential ingredient addition
  • Boss Phases - Phase transitions with validation
  • Spell Casting - Channeling โ†’ Cast โ†’ Effect โ†’ Recovery

Chain vs Coroutine: Instead of writing:

IEnumerator LaunchSequence()
{
if (!SafetyCheck()) yield break;
Charge();
yield return new WaitForSeconds(1.0f);
Fire();
yield return StartCoroutine(CoolDown());
Archive();
}

Use a Chain Graph where:

  • Timing is visible and editable by designers
  • Conditions are visual checkpoints, not hidden if statements
  • Async waits are configurable, not hardcoded
  • Entire pipeline is debuggable via graph visualization
โš ๏ธ Chain Gotchas
  1. Blocking Behavior: If Step 3 has a bug and never completes, Steps 4-5 never run
  2. Condition Timing: Conditions evaluate BEFORE node actionโ€”can't use action's side effects
  3. No Parallel Branches: Can't execute Step 2A and Step 2B simultaneously (use Trigger for that)
  4. Delay Stacking: Multiple delays add upโ€”3 nodes with 1s each = 3s total wait
  5. Early Exit Cleanup: Always unlock resources in condition failure paths