Skip to main content

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 Enemy catches both Enemy.Killed and Enemy.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.Killed
  • Enemy.Stunned
  • Enemy.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:

FieldTypeUsage
InstigatorAActor*Actor that caused the event
ContextObjectUObject*Any UObject (weapon, item, component...)
ValuefloatNumeric data (damage, distance, etc.)
IntValueint32Integer 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.Boss is better than BossKilled
  • Keep Payloads Light: Use ContextObject for 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