Chapter 13

Delegates & Event Dispatchers

Unreal's observer pattern, the dynamic variant that Blueprint can see, and why it's the main tool for inverting a dependency.

In this chapter

  1. The Multicast Delegate
  2. Dynamic Delegates & Event Dispatchers
  3. Choosing a Delegate Macro
  4. When to Use Them
1

The Multicast Delegate

Unreal's idiomatic observer mechanism is the multicast delegate. An object broadcasts an event, any number of listeners bind to it and react, and the broadcaster doesn't know who is listening.

StepHow
DeclareDECLARE_MULTICAST_DELEGATE, or the _OneParam variants
FireBroadcast()
SubscribeAddUObject(...)

It's the engine-native replacement for hand-rolling a subject and observer list, and it handles subscriber lifetime cleanly when you bind UObjects.

Macro placement

The delegate declaration goes below .generated.h — which must always be the last include — and above the class that uses it:

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MyHealthComponent.generated.h"   // must be the LAST include

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHealthChanged, float, NewHealth);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UMyHealthComponent : public UActorComponent
{
    GENERATED_BODY()
};
This is a hard UHT error, not a style nit. UHT parses top-to-bottom and needs every other include seen before .generated.h, since its generated boilerplate depends on what's declared above it. The delegate declaration doesn't strictly have to sit right after .generated.h, but that's the near-universal convention — it matches Epic's own source.

2

Dynamic Delegates & Event Dispatchers

DECLARE_DYNAMIC_MULTICAST_DELEGATE... is the Blueprint-assignable variant. Dynamic means reflected, so it shows up as an assignable event in Blueprint.

An Event Dispatcher in Blueprint is a dynamic multicast delegate. They're the same mechanism, named differently on each side of the boundary.

Broadcasting and binding

// Broadcast from inside the component
void UMyHealthComponent::TakeDamage(float Damage)
{
    CurrentHealth -= Damage;
    OnHealthChanged.Broadcast(CurrentHealth);
}

// Bind from the owning actor's C++ (e.g. in BeginPlay)
if (UMyHealthComponent* HealthComp = FindComponentByClass<UMyHealthComponent>())
{
    HealthComp->OnHealthChanged.AddDynamic(this, &AMyCharacter::HandleHealthChanged);
}

UFUNCTION()
void AMyCharacter::HandleHealthChanged(float NewHealth) { /* ... */ }

Two easy misses: AddDynamic requires the handler to be a real UFUNCTION() — reflection-visible — and the parameter list must match the delegate declaration exactly. Blueprint binding needs no extra code once the property is BlueprintAssignable: drag off the component reference and search for "Assign On [DelegateName]".


3

Choosing a Delegate Macro

MacroUse when
DECLARE_DYNAMIC_MULTICAST_DELEGATE...Many listeners, Blueprint-assignable — this is what an Event Dispatcher is under the hood. Use when you need the Blueprint bridge
DECLARE_DYNAMIC_DELEGATE (singlecast)Only one thing can be bound, and it can return a value. Rarer, and not BlueprintAssignable the same way — used more as a callback function parameter
DECLARE_MULTICAST_DELEGATE[_OneParam] (non-dynamic)Pure C++, no UHT/reflection overhead. Bind with AddLambda / AddRaw / AddUObject. Use when you don't need Blueprint visibility — cheaper than the dynamic variants

4

When to Use Them

Use them for decoupled one-to-many communication: OnHealthChanged, OnDied, OnObjectiveComplete. The UI binds to update a bar, audio binds to play a sound, and neither is wired directly into the broadcaster.

Two payoffs:

They're also a primary tool for inverting dependency direction. Instead of the interested party holding a hard reference to the thing it cares about, the thing broadcasts and the interested party subscribes — which is how you break the reference graphs described in Chapter 7.

Interview drill

Answer out loud before re-reading the chapter.

  1. What are Event Dispatchers or delegates, and when would you use them?
  2. Dynamic multicast, dynamic singlecast, or non-dynamic multicast — how do you choose?
← Chapter 12 ↑ Index Chapter 14 →