"When the player picks up an item, update the inventory, notify the UI, and play a sound effect." Write that the obvious way and the item's Blueprint ends up knowing about the inventory, the UI, and the audio system. Then you want to change one thing about the sound, and the file you open is the item Blueprint.
The tool that untangles this is the Event Dispatcher. The item just rings a bell to say "I was picked up," and the listeners decide what to do about it. This article walks through Dispatcher implementation as a three-step rhythm, covers the crash you get from forgetting to Unbind, and finishes with delegates in C++.
What You'll Learn
- What a Dispatcher really is: complete separation of source and handler (the observer pattern)
- The three-step Blueprint rhythm: create (the Dispatcher) → bind → call
- The crash caused by forgetting Unbind, and how to prevent it
- Multicast delegates in C++ (
BlueprintAssignable/AddDynamic)- Hands-on: build a notification network for item pickups
Why You Need It: Named Call Chains Fall Apart
An Event Dispatcher is UE's headline implementation of a delegate, a mechanism for notifying everyone who cares that an event happened. It's the observer pattern. The division of labor looks like this.
| Role | What it does | What it doesn't know |
|---|---|---|
| Source (Caller) | Rings the bell (Call) | Who is listening |
| Handler (Listener) | Registers with the bell (Bind) | What's inside the source |
| Event Dispatcher | The bell that mediates between them | What's inside either one (which is why it breaks the dependency) |

With named direct calls, every new reaction means modifying the source. With a Dispatcher, a new listener just binds, and the source Blueprint doesn't change by a single character.
Hands-On: Building a Notification Network for Item Pickups
Let's actually build the "pick it up and three things happen" scenario from the intro. Gathering materials in an RPG, opening a chest in a roguelike, grabbing a power-up in a shooter: "several systems react at the moment of acquisition" is the same shape in every genre. The implementation goes in three steps.

Setup to reproduce this: place one BP_Item and one BP_InventoryManager in the level. Give the Manager a variable TargetItem (type BP_Item, with Instance Editable checked) and point it at the item in the level (Instance Editable lets you pick the target per placed instance → Intro to Blueprint variables). Remembering how tedious it is to "point at each one by hand" is what makes the central Dispatcher in the later bonus feel like a relief.
Step 1: Create the Bell (Define the Dispatcher)
In BP_Item, create OnItemPickedUp under "Event Dispatchers" in the My Blueprint panel. To add the Inputs, select the Dispatcher you created and use the + in the Inputs section of the Details panel. Put only the minimum information the listeners need to know. Here, that's a reference to the player who picked it up (Instigator).
Step 2: Listeners Register (Bind)
On the receiving side, say in the BeginPlay of BP_InventoryManager, wire a Bind Event to OnItemPickedUp node from the item reference and connect a Custom Event to the Event pin.
BP_InventoryManager (listener)
Event BeginPlay → Bind Event to OnItemPickedUp (Target: reference to the item)
Event pin ← Custom Event: HandleItemPickup (receives Instigator)
HandleItemPickup → add to inventory → update UI

Binding is done by pulling from the red square event pin on the right of Bind Event to OnItemPickedUp and connecting a Custom Event. This registers "when this bell rings, call this process." The UI widget and the audio handler bind the same way. You can have as many listeners as you like.
Step 3: Ring It When It Happens (Call)
The source, BP_Item, just rings the bell the moment it's picked up.
BP_Item (source)
On Component Begin Overlap → (player check) → Call OnItemPickedUp (Instigator: the overlapping Actor) → Destroy Actor

All the source does is the single Call OnItemPickedUp node. It rings the bell without knowing a thing about who's listening, then destroys itself. That lightness is the essence of loose coupling.

Hit Play and pick up an item. The inventory, the UI, and the sound effect all react at once.
To confirm what's happening, disconnect the audio handler's Bind. Only the sound stops. Everything else keeps working. Put the Bind back and then bind a brand-new "achievement system" as well. You just added a reaction without touching BP_Item at all. You can add reactions without changing the source. That's the reason to use a Dispatcher.
Two things to take away.
- Keep the arguments minimal: put only "who" and "what" on the bell. If a listener needs a lot of data, it's cleaner for it to fetch that data through a reference than to bloat the signature
- Name events after what happened:
OnItemPickedUp,OnHealthChanged. Name them after a past fact. Naming them after what should be done (UpdateUIand the like) means the source knows about the handler's business, which is a tightly coupled name
The Most Important Rule: Bind and Unbind Come as a Pair
This is the one Dispatcher rule that can actually take your game down: when a listener disappears, its registration must disappear too.

Binding also means the source holds a reference to the listener. If a listener disappears without unbinding (widgets are the usual culprit since they're created and destroyed frequently), a reference to a destroyed object lingers, and the next Call causes an Accessed None error or a crash. For widgets, call Unbind Event from XXX before destruction (around Remove from Parent, or in Destruct). For Actors, call it in EndPlay.
Note: when the pair has matching lifetimes, like an Owner binding to a Dispatcher on its own Component, they're destroyed together so this is effectively a non-issue (see the Blueprint Component article). The danger is binding to something with a different lifetime.
Implementation in C++: Multicast Delegates
In C++, an Event Dispatcher is really a dynamic multicast delegate. Adding BlueprintAssignable exposes it as a Dispatcher that Blueprints can bind to as well.
// Item.h
// Declare the delegate type (two-parameter version; the macro name changes with the parameter count)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(
FOnItemPickedUpSignature, AItem*, PickedUpItem, APlayerCharacter*, Instigator);
UCLASS()
class AItem : public AActor
{
GENERATED_BODY()
public:
// Expose it as a Dispatcher that Blueprints can bind to
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnItemPickedUpSignature OnItemPickedUp;
};
// Item.cpp — the ringing side
void AItem::OnOverlapBegin(/* ... */)
{
// Ring the bell (equivalent to Call in Blueprint)
OnItemPickedUp.Broadcast(this, Cast<APlayerCharacter>(OtherActor));
}
// InventoryManager.cpp — the listening side
void AInventoryManager::BeginPlay()
{
Super::BeginPlay();
if (AItem* Item = GetItemReference())
{
// Register (equivalent to Bind in Blueprint)
Item->OnItemPickedUp.AddDynamic(this, &AInventoryManager::HandleItemPickup);
}
}
void AInventoryManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (AItem* Item = GetItemReference())
{
// Unregister (equivalent to Unbind). Always pair this with AddDynamic
Item->OnItemPickedUp.RemoveDynamic(this, &AInventoryManager::HandleItemPickup);
}
Super::EndPlay(EndPlayReason);
}
The mapping is straightforward: Call = Broadcast, Bind = AddDynamic, Unbind = RemoveDynamic. The basics of writing C++ in UE are covered in your first step from Blueprint to C++.
Bonus: Good to Know for Later
- The "I want to Bind but I can't get a reference" problem: having the UI individually bind to items that spawn at runtime is painful. The standard answer is to hang the bell somewhere everyone knows about. Create
OnAnyItemPickedUpon the GameState or GameInstance, have items ring it there, and have the UI bind there (see the GameState article)

- A Button's OnClicked works the same way: the
OnClickedon a UMG Button is an Event Dispatcher the engine provides. You've been using Dispatchers without knowing it - Don't overuse them: if every piece of communication is a broadcast, "who called this?" becomes hard to trace. A fixed one-to-one relationship is fine as a direct call. For the full picture of when to use what, see the overview of the three communication methods
Summary
- An Event Dispatcher is a bell that separates source from handler. The source just rings, the listeners just register
- The implementation is three steps: create → Bind → Call. In C++: Broadcast / AddDynamic / RemoveDynamic
- The iron rule is "Bind and Unbind come as a pair." Binding across different lifetimes is where accidents happen
- Adding and removing reactions leaves the source untouched. That's the practical payoff of loose coupling
The other half of loose coupling, the letter with no addressee, gets a deeper treatment in the Blueprint Interface article.
In your game, which moment should the most systems react to? Defeating an enemy? Leveling up? Hang your first bell right there.