Skip to main content

05 Priority Event: Execution Order Matters

๐Ÿ”ˆ Hover for sound

๐Ÿ“‹ 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 GameObjectDamageInfoGameEvent 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
OnChaoticHitGameObjectDamageInfoGameEventโŒ ResolveHit โ†’ ActivateBuff (Wrong)
OnOrderedHitGameObjectDamageInfoGameEventโœ… ActivateBuff โ†’ ResolveHit (Correct)
๐Ÿ”ง Same Type, Different Order

Both events are GameObjectDamageInfoGameEvent. 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 GameObjectDamageInfoGameEvent orderedHitEvent;

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

private GameObjectDamageInfoGameEvent _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