Event Bus Guide
The event bus is a decoupled messaging system built on GameplayTags. Actors broadcast events without knowing who listens, and listeners subscribe without needing direct references.
Core Concepts
- Tags as Events: Every event is a
FGameplayTag(e.g.,Enemy.Killed,Item.Collected) - Payload: Optional data attached to an event (
FERPEventPayload) - Parent Matching: Listening to
Enemycatches bothEnemy.KilledandEnemy.Stunned - WorldSubsystem: One bus per world, automatically created
Broadcasting Events
Simple Broadcast (No Payload)
UERPEventBusSubsystem* Bus = GetWorld()->GetSubsystem<UERPEventBusSubsystem>();
Bus->BroadcastSimple(FGameplayTag::RequestGameplayTag("Area.Secured"));
Broadcast with Payload
FERPEventPayload Payload;
Payload.Instigator = DamageDealer;
Payload.Value = DamageAmount;
Payload.IntValue = HitCount;
Payload.ContextObject = WeaponUsed;
Bus->Broadcast(FGameplayTag::RequestGameplayTag("Enemy.Killed"), Payload);
Blueprint
Call BroadcastSimple or Broadcast on the ERPEventBusSubsystem node.
Listening for Events
Blueprint (Global Listener)
Bind to OnEventReceived on the ERPEventBusSubsystem. This fires for all events on the bus — filter by tag in your handler.
C++ (Per-Tag Listener)
// Listen for a specific tag
FDelegateHandle Handle = Bus->ListenNative(
FGameplayTag::RequestGameplayTag("Enemy.Killed"),
FERPOnEventReceivedNative::FDelegate::CreateUObject(this, &AMyClass::OnEnemyKilled)
);
// Handler
void AMyClass::OnEnemyKilled(FGameplayTag EventTag, const FERPEventPayload& Payload)
{
// EventTag is the exact tag broadcast (could be a child of what you listened for)
AActor* Killer = Payload.Instigator;
// ...
}
// Unlisten when done
Bus->UnlistenNative(FGameplayTag::RequestGameplayTag("Enemy.Killed"), Handle);
Parent Tag Matching
If you listen for Enemy, your callback fires for:
Enemy.KilledEnemy.StunnedEnemy.Spawned- Any tag under
Enemy.*
This is useful for broad category listeners (e.g., a statistics system listening to all Enemy events).
Event Payload
The FERPEventPayload struct provides flexible data attachment:
| Field | Type | Usage |
|---|---|---|
Instigator | AActor* | Actor that caused the event |
ContextObject | UObject* | Any UObject (weapon, item, component...) |
Value | float | Numeric data (damage, distance, etc.) |
IntValue | int32 | Integer data (count, index, etc.) |
For complex payloads, use ContextObject with a custom UObject subclass.
Use Cases
Kill Counter
Bus->ListenNative(EnemyKilledTag, ...OnEnemyKilled);
void OnEnemyKilled(...) { KillCount++; UpdateUI(); }
Achievement System
// Listen to broad categories
Bus->ListenNative(FGameplayTag::RequestGameplayTag("Achievement"), ...OnAchievement);
Area Triggers
// In your trigger volume
Bus->Broadcast(FGameplayTag::RequestGameplayTag("Area.Entered"), Payload);
Cross-System Communication
// Quest system broadcasts completion
Bus->BroadcastSimple(FGameplayTag::RequestGameplayTag("Quest.Completed.MainStory"));
// Dialogue system listens and unlocks new dialogues
// Music system listens and changes track
// Neither system knows about the other
Best Practices
- Use Hierarchical Tags:
Enemy.Killed.Bossis better thanBossKilled - Keep Payloads Light: Use
ContextObjectfor complex data - Unlisten on Destroy: Always clean up native listeners in
EndPlay - Don't Over-Broadcast: Only broadcast events that something might listen to
Next Steps
- Quest System Guide — How quests consume events
- API Reference — Full
UERPEventBusSubsystemAPI