Unreal's observer pattern, the dynamic variant that Blueprint can see, and why it's the main tool for inverting a dependency.
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.
| Step | How |
|---|---|
| Declare | DECLARE_MULTICAST_DELEGATE, or the _OneParam variants |
| Fire | Broadcast() |
| Subscribe | AddUObject(...) |
It's the engine-native replacement for hand-rolling a subject and observer list, and it handles subscriber lifetime cleanly when you bind UObjects.
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()
};
.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.
DECLARE_DYNAMIC_MULTICAST_DELEGATE... is the Blueprint-assignable variant. Dynamic means reflected, so it shows up as an assignable event in Blueprint.
// 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]".
| Macro | Use 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 |
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.
Answer out loud before re-reading the chapter.