Skip to main content

05 Priority Event: Execution Order Matters

๐Ÿ“‹ Overviewโ€‹

In game logic, sequence matters. When multiple actions respond to a single event, their execution order can dramatically change the outcome. This demo demonstrates how visual Editor configurationโ€”without any code changesโ€”can turn a weak hit into a devastating critical strike.

๐Ÿ’ก What You'll Learn
  • Why execution order affects gameplay logic
  • How to configure listener priority in the Behavior Window
  • The "Buff-Then-Attack" pattern in action
  • How to debug order-dependent logic issues

๐ŸŽฌ Demo Sceneโ€‹

Assets/TinyGiants/GameEventSystem/Demo/05_PriorityEvent/05_PriorityEvent.unity

Scene Compositionโ€‹

UI Layer (Canvas):

  • ๐ŸŽฎ Two Attack Buttons - Located at the bottom of the screen
    • "Raise (Chaotic Hit)" โ†’ Triggers PriorityEventRaiser.FireChaoticSequence() (incorrect order)
    • "Raise (Ordered Hit)" โ†’ Triggers PriorityEventRaiser.FireOrderedSequence() (correct order)

Game Logic Layer (Demo Scripts):

  • ๐Ÿ“ค PriorityEventRaiser - GameObject with the raiser script

    • Manages turret aiming and projectile firing
    • Holds references to two events: OnChaoticHit and OnOrderedHit
    • Both events use the same GameEvent<GameObject, DamageInfo> type
  • ๐Ÿ“ฅ PriorityEventReceiver - GameObject with the receiver script

    • Has TWO listener methods bound to each event:
      • ActivateBuff - Enables critical damage mode
      • ResolveHit - Calculates damage based on current buff state
    • The order of these methods determines the combat outcome

Visual Feedback Layer (Demo Objects):

  • ๐ŸŽฏ SentryTurret - The attacker
    • Changes from grey to gold when buffed
    • Spawns particle aura effect when activated
  • ๐ŸŽฏ TargetDummy - The victim capsule
    • Has Rigidbody for knockback physics
  • ๐Ÿ’ฅ VFX Systems - Different effects for normal vs critical hits
    • Normal: Small smoke puff
    • Critical: Large explosion + camera shake
  • ๐Ÿ  Plane - Ground surface

๐ŸŽฎ How to Interactโ€‹

The Experiment Setupโ€‹

Both buttons fire the same physical projectile, but trigger different events with different listener order configurations.

Step 1: Enter Play Modeโ€‹

Press the Play button in Unity.

Step 2: Test the Wrong Order (Chaotic Hit)โ€‹

Click "Raise (Chaotic Hit)" (Left Button):

What Happens:

  1. ๐ŸŽฏ Turret aims and fires projectile
  2. ๐Ÿ’ฅ Projectile hits target
  3. ๐Ÿ”ด PROBLEM: Damage calculated FIRST (ResolveHit executes)
    • Result: -10 weak damage (grey text)
    • Effect: Small smoke VFX
  4. โœจ Buff activates SECOND (ActivateBuff executes)
    • Turret turns gold with particle aura
    • Too late! The damage was already calculated

Console Output:

[Receiver] (B) RESOLVE: No buff detected. Weak hit. (Check Priority Order!)
[Receiver] (A) BUFF ACTIVATED! Systems at 300% power.

Result: โŒ Normal hit because buff wasn't active when damage was calculated


Step 3: Test the Correct Order (Ordered Hit)โ€‹

Click "Raise (Ordered Hit)" (Right Button):

What Happens:

  1. ๐ŸŽฏ Turret aims and fires projectile
  2. ๐Ÿ’ฅ Projectile hits target
  3. โœจ CORRECT: Buff activates FIRST (ActivateBuff executes)
    • Turret turns gold with particle aura
    • Internal _isBuffActive flag set to true
  4. ๐Ÿ”ด Damage calculated SECOND (ResolveHit executes)
    • Checks buff flag: ACTIVE!
    • Result: CRIT! -50 (orange text, 5x damage multiplier)
    • Effect: Massive explosion VFX + camera shake

Console Output:

[Receiver] (A) BUFF ACTIVATED! Systems at 300% power.
[Receiver] (B) RESOLVE: Buff detected! CRITICAL EXPLOSION.

Result: โœ… Critical hit because buff was active when damage was calculated


๐Ÿ—๏ธ Scene Architectureโ€‹

The "Buff-Then-Attack" Problemโ€‹

This is a common pattern in game development:

โšก Event Raised: OnHit
โ”‚
โ”œโ”€ ๐Ÿฅ‡ 1st Action: [Priority 10]
โ”‚ โ””โ”€ ๐Ÿ›ก๏ธ ActivateBuff() โž” Sets `_isBuffActive = true` ๐ŸŸข
โ”‚
โ””โ”€ ๐Ÿฅˆ 2nd Action: [Priority 5]
โ””โ”€ โš”๏ธ ResolveHit() โž” If (_isBuffActive) ? ๐Ÿ’ฅ CRIT : ๐Ÿ›ก๏ธ NORMAL
โ”‚
๐ŸŽฏ Result: CRITICAL HIT (Logic resolved with updated state)

The Challenge: If ResolveHit runs before ActivateBuff, the flag hasn't been set yet, resulting in normal damage even though the buff is "attached" to the same event!


Event Definitionsโ€‹

Both events use the same type but have different behavior configurations:

Game Event Editor

Event NameTypeListener Order
OnChaoticHitGameEvent<GameObject, DamageInfo>โŒ ResolveHit โ†’ ActivateBuff (Wrong)
OnOrderedHitGameEvent<GameObject, DamageInfo>โœ… ActivateBuff โ†’ ResolveHit (Correct)
๐Ÿ”ง Same Type, Different Order

Both events are GameEvent<GameObject, DamageInfo>. The only difference is the listener execution order configured in the Behavior Window.


Behavior Configuration Comparisonโ€‹

The critical difference is in the Behavior Window configuration.

โŒ Wrong Order (OnChaoticHit)โ€‹

Chaotic Behavior

Execution Sequence:

  1. ResolveHit (Top position - executes first)
  2. ActivateBuff (Bottom position - executes second)

Result: Damage calculated before buff applied = Normal Hit

โœ… Correct Order (OnOrderedHit)โ€‹

Ordered Behavior

Execution Sequence:

  1. ActivateBuff (Top position - executes first)
  2. ResolveHit (Bottom position - executes second)

Result: Buff applied before damage calculated = Critical Hit

๐ŸŽฏ Drag & Drop Reordering

You can change the execution order by dragging the handle (โ‰ก) on the left side of each listener in the Behavior Window. This is a visual, no-code way to modify gameplay logic!


Sender Setup (PriorityEventRaiser)โ€‹

Select the PriorityEventRaiser GameObject in the Hierarchy:

PriorityEventRaiser Inspector

Event Channels:

  • Ordered Hit Event: OnOrderedHit (configured correctly)
    • Tooltip: "Apply Buff โ†’ Then Fire"
  • Chaotic Hit Event: OnChaoticHit (configured incorrectly)
    • Tooltip: "Fire โ†’ Then Apply Buff (Too late!)"

Settings:

  • Turret Head: SentryTurret/Head (Transform for aiming)
  • Turret Muzzle Position: Head/MuzzlePoint (projectile spawn)
  • Projectile Prefab: Projectile visual effect
  • Muzzle Flash VFX: Particle system for firing
  • Hit Target: TargetDummy (Transform)

Receiver Setup (PriorityEventReceiver)โ€‹

Select the PriorityEventReceiver GameObject in the Hierarchy:

PriorityEventReceiver Inspector

Visual Configuration:

  • Turret Root: SentryTurret (Transform)
  • Turret Renderers: Array of 1 renderer (the turret body)
  • Normal Mat: Grey material (default state)
  • Buffed Mat: Gold material (buffed state)
  • Buff Aura Prefab: Cyan particle effect for buff visualization

VFX Configuration:

  • Hit Normal VFX: Small smoke particle system
  • Hit Crit VFX: Large explosion particle system
  • Floating Text Prefab: Damage number display

Target References:

  • Hit Target: TargetDummy (Transform)
  • Target Rigidbody: TargetDummy (Rigidbody for knockback)

๐Ÿ’ป Code Breakdownโ€‹

๐Ÿ“ค PriorityEventRaiser.cs (Sender)โ€‹

using UnityEngine;
using TinyGiants.GameEventSystem.Runtime;

public class PriorityEventRaiser : MonoBehaviour
{
[Header("Event Channels")]
[Tooltip("Configured in Editor: Apply Buff -> Then Fire.")]
[GameEventDropdown] public GameEvent<GameObject, DamageInfo> orderedHitEvent;

[Tooltip("Configured in Editor: Fire -> Then Apply Buff (Too late!).")]
[GameEventDropdown] public GameEvent<GameObject, DamageInfo> chaoticHitEvent;

private GameEvent<GameObject, DamageInfo> _pendingEvent;

/// <summary>
/// Button A: Starts attack sequence that triggers the "Ordered" event.
/// </summary>
public void FireOrderedSequence()
{
if (orderedHitEvent == null) return;
_pendingEvent = orderedHitEvent;
_isAttacking = true;
Debug.Log("[Sender] Initiating ORDERED attack sequence...");
}

/// <summary>
/// Button B: Starts attack sequence that triggers the "Chaotic" event.
/// </summary>
public void FireChaoticSequence()
{
if (chaoticHitEvent == null) return;
_pendingEvent = chaoticHitEvent;
_isAttacking = true;
Debug.Log("[Sender] Initiating CHAOTIC attack sequence...");
}

private void FireProjectile()
{
// ... Projectile creation logic ...

shell.Initialize(hitTarget.position, 15f, () =>
{
DamageInfo info = new DamageInfo(10f, false, DamageType.Physical,
hitTarget.position, "Sentry Turret");

// Raise whichever event was queued (Ordered or Chaotic)
if(_pendingEvent != null)
_pendingEvent.Raise(this.gameObject, info);

Debug.Log($"[Sender] Impact! Event '{_pendingEvent?.name}' Raised.");
});
}
}

Key Points:

  • ๐ŸŽฏ Same Sender Code - Both events use identical raise logic
  • ๐Ÿ“ฆ Event Selection - _pendingEvent determines which event fires
  • ๐Ÿ”‡ Order Agnostic - Sender has no knowledge of listener order

๐Ÿ“ฅ PriorityEventReceiver.cs (Listener)โ€‹

using UnityEngine;
using System.Collections;

public class PriorityEventReceiver : MonoBehaviour
{
[SerializeField] private Renderer[] turretRenderers;
[SerializeField] private Material buffedMat;
[SerializeField] private ParticleSystem buffAuraPrefab;

private bool _isBuffActive; // The critical state flag

/// <summary>
/// [Listener Method A]
/// Activates the buff state and visual effects.
///
/// PRIORITY IMPACT:
/// - If configured ABOVE ResolveHit: Buff applies BEFORE damage calculation โ†’ CRITICAL HIT
/// - If configured BELOW ResolveHit: Buff applies AFTER damage calculation โ†’ NORMAL HIT
/// </summary>
public void ActivateBuff(GameObject sender, DamageInfo args)
{
_isBuffActive = true; // <-- THE CRITICAL STATE CHANGE

// Visual feedback: Gold material + particle aura
foreach (var r in turretRenderers)
if(r) r.material = buffedMat;

if (buffAuraPrefab != null)
{
_activeBuffEffect = Instantiate(buffAuraPrefab, turretRoot.position,
Quaternion.identity);
_activeBuffEffect.transform.SetParent(turretRoot);
_activeBuffEffect.Play();
}

Debug.Log("<color=cyan>[Receiver] (A) BUFF ACTIVATED! " +
"Systems at 300% power.</color>");
}

/// <summary>
/// [Listener Method B]
/// Calculates damage and spawns VFX based on CURRENT buff state.
///
/// LOGIC: Checks _isBuffActive at the EXACT MOMENT of execution.
/// For correct behavior, ActivateBuff must execute BEFORE this method.
/// </summary>
public void ResolveHit(GameObject sender, DamageInfo args)
{
float finalDamage = args.amount;
bool isCrit = false;
ParticleSystem vfxToPlay;

// Check the flag at THIS EXACT MOMENT
if (_isBuffActive)
{
// CRITICAL PATH
finalDamage *= 5f; // 5x damage multiplier
isCrit = true;
vfxToPlay = hitCritVFX;

StartCoroutine(ShakeCameraRoutine(0.2f, 0.4f));
Debug.Log("<color=green>[Receiver] (B) RESOLVE: Buff detected! " +
"CRITICAL EXPLOSION.</color>");
}
else
{
// NORMAL PATH
vfxToPlay = hitNormalVFX;
Debug.Log("<color=red>[Receiver] (B) RESOLVE: No buff detected. " +
"Weak hit. (Check Priority Order!)</color>");
}

// Spawn appropriate VFX
if (vfxToPlay != null)
{
var vfx = Instantiate(vfxToPlay, args.hitPoint, Quaternion.identity);
vfx.Play();
Destroy(vfx.gameObject, 2.0f);
}

// Apply physics and UI feedback
ApplyPhysicsKnockback(args, isCrit);
ShowFloatingText(finalDamage, isCrit, hitTarget.position);

StartCoroutine(ResetRoutine());
}

private IEnumerator ResetRoutine()
{
yield return new WaitForSeconds(1.5f);
_isBuffActive = false; // Reset for next attack
// ... Reset visuals ...
}
}

Key Points:

  • ๐ŸŽฏ State Dependency - ResolveHit behavior depends entirely on _isBuffActive flag
  • โฑ๏ธ Timing Critical - The flag must be set BEFORE damage calculation
  • ๐Ÿ”€ Order-Dependent Logic - Same code, different results based on execution order
  • ๐ŸŽจ Visual Feedback - Different VFX, text size, and effects for each path

๐Ÿ”‘ Key Takeawaysโ€‹

ConceptImplementation
๐ŸŽฏ Execution OrderListener order directly affects gameplay logic
๐ŸŽจ Visual ConfigurationDrag-and-drop in Behavior Windowโ€”no code changes
๐Ÿ”€ State ManagementOrder matters when listeners modify shared state
๐Ÿ› Debug PatternConsole logs help identify order-related bugs
๐Ÿ”„ Gameplay DesignEnable/disable order controls combo systems, buff stacking, etc.
๐ŸŽ“ Design Insight

Execution order is critical for:

  • Buff systems - Apply modifiers before calculating effects
  • Combo chains - Validate conditions before triggering next action
  • Shield mechanics - Check absorption before applying damage
  • Trigger sequences - Ensure prerequisites are met before executing dependent logic

Always test both orders to ensure your logic works as intended!


๐ŸŽฏ What's Next?โ€‹

You've mastered execution order. Now let's explore conditional event triggering to make events smarter.

Next Chapter: Learn about conditional logic in 06 Conditional Event