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.
- 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)
- "Raise (Chaotic Hit)" โ Triggers
Game Logic Layer (Demo Scripts):
-
๐ค PriorityEventRaiser - GameObject with the raiser script
- Manages turret aiming and projectile firing
- Holds references to two events:
OnChaoticHitandOnOrderedHit - 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
- Has TWO listener methods bound to each event:
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:
- ๐ฏ Turret aims and fires projectile
- ๐ฅ Projectile hits target
- ๐ด PROBLEM: Damage calculated FIRST (ResolveHit executes)
- Result:
-10weak damage (grey text) - Effect: Small smoke VFX
- Result:
- โจ 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:
- ๐ฏ Turret aims and fires projectile
- ๐ฅ Projectile hits target
- โจ CORRECT: Buff activates FIRST (ActivateBuff executes)
- Turret turns gold with particle aura
- Internal
_isBuffActiveflag set totrue
- ๐ด 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:

| Event Name | Type | Listener Order |
|---|---|---|
OnChaoticHit | GameEvent<GameObject, DamageInfo> | โ ResolveHit โ ActivateBuff (Wrong) |
OnOrderedHit | GameEvent<GameObject, DamageInfo> | โ ActivateBuff โ ResolveHit (Correct) |
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)โ

Execution Sequence:
ResolveHit(Top position - executes first)ActivateBuff(Bottom position - executes second)
Result: Damage calculated before buff applied = Normal Hit
โ Correct Order (OnOrderedHit)โ

Execution Sequence:
ActivateBuff(Top position - executes first)ResolveHit(Bottom position - executes second)
Result: Buff applied before damage calculated = Critical Hit
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:

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 effectMuzzle Flash VFX: Particle system for firingHit Target: TargetDummy (Transform)
Receiver Setup (PriorityEventReceiver)โ
Select the PriorityEventReceiver GameObject in the Hierarchy:

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 systemHit Crit VFX: Large explosion particle systemFloating 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 -
_pendingEventdetermines 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 -
ResolveHitbehavior depends entirely on_isBuffActiveflag - โฑ๏ธ 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โ
| Concept | Implementation |
|---|---|
| ๐ฏ Execution Order | Listener order directly affects gameplay logic |
| ๐จ Visual Configuration | Drag-and-drop in Behavior Windowโno code changes |
| ๐ State Management | Order matters when listeners modify shared state |
| ๐ Debug Pattern | Console logs help identify order-related bugs |
| ๐ Gameplay Design | Enable/disable order controls combo systems, buff stacking, etc. |
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
๐ Related Documentationโ
- Game Event Behavior - Detailed guide to listener configuration
- Best Practices - Patterns for order-dependent logic
- Listening Strategies - Advanced callback patterns