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.