Events
The crowd actor broadcasts multicast delegates you can bind in Blueprint or C++ to react to movement and population changes without polling every tick. Every event delivers an array of agent indices that are valid at the moment it fires.
Movement lifecycle
Fired right after each simulation tick.
Agents that reached their slot this tick (their settle latch flipped on). Fires once per arrival - the typical place to switch a unit to its idle animation or trigger "arrived" gameplay.
Ordered agents that made no progress for StuckTimeout seconds. Re-fires every
interval while they stay stuck. The standard reaction is to issue a fresh SetAgentsTarget
to re-path them.
Set StuckTimeout = 0 to disable stuck events entirely.
Agents whose unfinished order was replaced by a new SetAgentsTarget call. Useful for
cleaning up any per-order state you were tracking for those units.
Population changes
These fire before the simulation tick that applies the change, so the indices they carry are still valid in your handler - the agents are still in the arrays and queryable.
The indices of every agent spawned via SpawnAgent / SpawnAgentGrid since the last
broadcast. Use it to grow your renderer's instance buffer or attach per-agent gameplay state.
The indices queued by RemoveAgent since the last broadcast, fired just before the tick
that actually removes them - so you can read a removed agent's final position/transform, or
look up its stable id to clean up id-keyed data, while it still
exists. After the tick, those indices are gone.
Timing model
┌─────────────────────────── one frame ───────────────────────────┐
│ │
Flush spawns/removes ──► SimTick (applies removals, advances) ──► broadcast movement events
│ │ │
OnAgentsSpawned agents actually OnAgentsReachedGoal
OnAgentsRemoved added / removed here OnAgentsStuck
(indices still valid) OnAgentsOrdersCanceled
So spawn/remove events lead the change (indices still valid), and movement events trail the tick (reporting what just happened).
Binding an event
// C++
Crowd->OnAgentsReachedGoal.AddDynamic(this, &AMyController::HandleReachedGoal);
void AMyController::HandleReachedGoal(const TArray<int32>& Indices)
{
// Indices are valid right now; resolve to ids if you need to remember them.
}
In Blueprint, select the crowd actor and use the matching red event node under the Crowd Toolkit category, or Bind Event to On Agents Reached Goal.
Indices in an event are valid at broadcast time only. If you store any of them for later, convert them to stable ids first.