Crowd Actor
ACrowdToolkitActor (display name Crowd Toolkit Actor) is the one object you interact with.
It wraps the simulation library as an Actor: every tunable lives as a property in its details
panel, and every operation - spawn, order, query, walls - is a Blueprint-callable function.
Each actor owns its own independent simulation (created on BeginPlay, destroyed on
EndPlay), so any number of crowd actors can run side by side. All positions are in world
space; the simulation's X/Y maps directly to world X/Y, and Z is
tracked per agent.
Configuration properties
All config properties are EditAnywhere + BlueprintReadWrite. They are copied into the
simulation every tick (via ApplyConfig), so changing one at runtime takes effect
immediately. Clamp ranges below are the valid limits; the simulation re-clamps on apply.
Agent
Formation
See Formations.
Navigation
See Navigation.
Avoidance
See Avoidance. Always on - these only tune it.
Walls
See Wall Generation.
Ground
See Ground Snapping.
General
Debug
There is no master switch: each overlay is drawn every tick when its own flag is on, so
untick them all to draw nothing. All default to off and are compiled out of Shipping builds.
See
Functions
Simulation control
void SimTick(float DeltaSeconds);
Advance the simulation by DeltaSeconds (pushes the current config first). Called
automatically each frame when Auto Tick is on; call it yourself when you've turned Auto
Tick off and want to drive the step manually. DeltaSeconds is clamped and substepped
internally, so a frame hitch slows simulated time briefly instead of letting agents tunnel
through walls.
void ResetSimulation();
Clear all agents, walls and pathfinding/avoidance caches (config is kept). Pending spawned/removed event batches are discarded - the indices they named no longer exist.
void ApplyConfig() const;
Copy the actor's config properties into the simulation. Done automatically each tick; rarely called directly.
Spawning & removing
void SpawnAgent(const FVector& Position);
Spawn one agent at Position. Position.Z becomes its initial tracked Z;
the simulation itself takes only X/Y. Reported via OnAgentsSpawned.
void SpawnAgentGrid(const FVector& Origin, int32 Cols, int32 Rows, float Spacing);
Spawn a Cols × Rows grid of agents starting at Origin, Spacing apart.
void RemoveAgent(int32 Index);
Queue the agent at Index for removal. Removals are deferred: the simulation applies the
queued batch (sorted, swap-and-pop) at the start of the next tick, so indices stay stable
between ticks and any set of agents can be removed in any order. Until that tick the agent is
still present (count and getters include it). Out-of-range indices and re-queues of an
already-queued index are ignored. Reported via OnAgentsRemoved
before the removing tick, while the index is still valid.
int32 GetAgentCount() const;
Number of agents currently in the simulation (includes agents queued for removal until the next tick applies the removal).
Identity - indices & ids
See Indices & Stable IDs. Lookups that miss return -1.
int32 GetAgentId(int32 Index) const; // stable id at Index, -1 if out of range
int32 GetAgentIndex(int32 Id) const; // current index of Id, -1 if no such agent
TArray<int32> GetAgentsIds(const TArray<int32>& Indices) const; // id per index, -1 per out-of-range
TArray<int32> GetAgentsIndices(const TArray<int32>& Ids) const; // index per id, -1 per unknown
Batch forms - translate a whole selection in one call. Output is parallel to the input.
Per-agent getters
Each getter takes an optional Indices mask: pass an empty array to get every agent
in storage order; pass a list of indices to get exactly those, in mask order (an
out-of-range index yields a zeroed entry, so the output always lines up 1:1 with the mask).
All the getters below are parallel - element i of each refers to the same agent.
TArray<FVector> GetAgentPositions(const TArray<int32>& Indices) const;
World positions, including each agent's tracked Z.
TArray<FTransform> GetAgentTransforms(const TArray<int32>& Indices) const;
Render-ready transforms: location = agent position; rotation faces the travel direction (holding the last facing while standing still), tilted to the ground normal when Align Transforms To Ground; scale = Transform Scale on every axis. See Rendering Agents.
TArray<FVector> GetAgentGroundNormals(const TArray<int32>& Indices) const;
Last traced ground normal per agent (world up until a trace has hit). See Ground Snapping.
TArray<FVector> GetAgentTargets(const TArray<int32>& Indices) const;
Each agent's individual formation slot position.
TArray<FVector> GetAgentNavGoals(const TArray<int32>& Indices) const;
The shared formation anchor the group is routing toward.
TArray<FVector> GetAgentVelocities(const TArray<int32>& Indices) const;
Current velocities - use the magnitude to drive idle/walk/run animation.
TArray<bool> GetAgentSettled(const TArray<int32>& Indices) const;
Latched settle (arrived) state per agent.
TArray<float> GetAgentSpeeds(const TArray<int32>& Indices) const;
Effective max speed per agent - the per-agent override where set, otherwise the Speed config value.
Selection queries
Return indices into the position array. The screen-rect query is the marquee-select replacement; doing it per agent in a Blueprint loop costs a visible hitch at high counts.
TArray<int32> GetAgentsInRect(const FBox2D& Rect) const;
Every agent inside the world-space XY rectangle.
TArray<int32> GetAgentsInRadius(const FVector& Center, float Radius) const;
Every agent within Radius of Center on the XY plane (Center.Z ignored).
TArray<int32> GetAgentsInScreenRect(APlayerController* PlayerController,
const FVector2D& ScreenMin,
const FVector2D& ScreenMax) const;
Every agent whose position projects inside the screen-space rect [ScreenMin, ScreenMax]
(inclusive) for the given player - marquee selection in one call. Projection uses each
agent's tracked Z, so it matches what the player sees.
Orders
void SetAgentsTarget(const TArray<int32>& Indices, const FVector& Position);
Order Indices into a formation centred on Position. The
core movement call. Replacing an unfinished order fires
OnAgentsOrdersCanceled for the affected agents.
void SetAgentTarget(int32 Index, const FVector& Position);
Single-agent convenience - a formation of one, so the agent simply walks to the point.
Per-agent speed
On top of the global Speed default, you can cap individual agents.
void SetAgentsSpeed(const TArray<int32>& Indices, float Speed);
void SetAgentSpeed(int32 Index, float Speed);
Set the max-speed override. Speed < 0 resets the agents to the global Speed config
value; Speed >= 0 is an absolute per-agent cap (0 freezes them in place). Read the
effective values back with GetAgentSpeeds
TIP
Use this for mixed-speed groups (cavalry vs infantry), slows/buffs, or freezing units in place without removing them.
Walls
See Wall Generation.
void AddWallTile(int32 X, int32 Y);
Block the wall-grid tile at (X, Y). Idempotent - adding an existing tile is a no-op.
TArray<FIntPoint> GetWallTiles() const;
Every blocked wall tile, as grid coordinates.
int32 GenerateWallTiles();
Generate walls using the selected Wall Generation Method (None is a no-op). Called once
on BeginPlay; returns the number of tiles added.
int32 GenerateWallTilesFromNavMesh();
Rasterize the world's built navigation mesh into the wall grid (see Seeding walls from the nav mesh). Requires the nav mesh to be built; safe to call repeatedly. Returns the number of tiles added.
Events
Multicast delegates - bind in Blueprint or C++. Full timing and usage in Events.
Debugging & diagnostics
Tick any of the debug flags (Draw Walls, Draw Agents, Show Slots, Show Nav Goals, Show Velocity, Show Flow Field, Show A* Paths) to draw that overlay into the world. There is no master switch; untick them all to draw nothing.
Keep the overlays off when measuring performance - the agent overlays fetch every agent array and issue per-agent debug primitives, which dominates the frame at high agent counts. They are compiled out entirely in Shipping builds.
int32 GetFlowFieldCount() const; // number of cached flow fields
int64 GetFlowFieldBytes() const; // their total memory
The cache grows with the world region the agents span - handy for understanding memory under Flow Field navigation.