SwarmManager¶
namespace MassiveSwarmSystem.Runtime
The central component that owns the whole simulation: the agent data, the spatial grid, the
fixed-step simulation loop, target tracking, grounding, and blocking. You place one
SwarmManager in the scene (alongside a SwarmVisualManager) and everything else talks to it.
Setting it up in the Inspector
This page is the scripting view. For placing the manager, assigning a SwarmSettings
asset, and tuning the simulation in the Inspector, see
Swarm Manager.
Getting the manager¶
SwarmManager.Instance is the active manager — set on Awake, and when it isn't cached yet
(for example in the Editor before play) it falls back to a scene search. It's null when no
manager exists. TryGetManager wraps that lookup in a bool check, with an overload that logs an
error when none is found. To test for a manager without triggering a scene search, use Exists:
using MassiveSwarmSystem.Runtime;
if (SwarmManager.TryGetManager(out SwarmManager manager))
{
int alive = manager.ActiveAgentCount;
}
| Member | Description |
|---|---|
Instance |
The active manager, or null. Set on Awake; falls back to a scene search when not yet cached. |
Exists |
true when a manager is cached, without scanning the scene. |
TryGetManager(out SwarmManager manager) |
true if an active manager exists. Silent. |
TryGetManager(out SwarmManager manager, bool logError) |
Same, but logs an error when none exists. |
ActiveAgentCount |
How many agents are currently alive. |
Capacity |
Maximum agents (from SwarmSettings). |
Hold an agent with a handle, not a slot index
Per-agent methods take a SwarmAgentHandle — a small stable reference you get back from
TrySpawnAgent. It keeps pointing at the same agent after other agents despawn, and it stops
resolving once its own agent is gone, so a stale handle fails safely instead of acting on
whoever later fills the slot. A raw int slot — such as the index a bulk reader hands you — is
good only for the current step: a despawn swaps the last agent into the freed slot. Turn a slot
into a handle with GetAgentHandle(slot) when you need to keep the reference. A default handle
is invalid and never resolves; handle.IsValid reports whether it was ever minted.
Spawning agents¶
TrySpawnAgent adds one agent and returns false if the swarm is at capacity (or not set up).
The position is automatically grounded using the manager's grounding settings. The simulation
self-initializes on the first spawn, so you don't have to call Initialize() yourself.
// Spawn a ring of agents using a configured archetype.
for (int i = 0; i < 200; i++)
{
float a = i / 200f * Mathf.PI * 2f;
Vector3 pos = center + new Vector3(Mathf.Cos(a), 0f, Mathf.Sin(a)) * radius;
if (!manager.TrySpawnAgent(pos, Vector3.forward, archetype, out SwarmAgentHandle handle))
{
break; // at capacity
}
}
| Overload | Use it when |
|---|---|
TrySpawnAgent(pos, forward) |
Quick spawn with the default radius and profiles. |
TrySpawnAgent(pos, forward, out SwarmAgentHandle handle) |
Same, but you need a handle to the new agent. |
TrySpawnAgent(pos, forward, SwarmBehaviorProfile) |
Override the behavior profile. |
TrySpawnAgent(pos, forward, SwarmBehaviorProfile, SwarmMovementProfile) |
Override both profiles. |
TrySpawnAgent(pos, forward, SwarmBehaviorProfile, SwarmMovementProfile, out SwarmAgentHandle handle) |
…and capture a handle. |
TrySpawnAgent(pos, forward, SwarmAgentArchetype, out SwarmAgentHandle handle) |
Spawn from an archetype (prefab + radius + profiles + attack profile). When the archetype has no prefab, the SwarmVisualManager's Fallback Agent Prefab (the built-in MSS Default Agent) is used; spawning only fails if neither the archetype nor the fallback provides a prefab. |
TrySpawnAgent(in SwarmAgentSpawnState, pos, forward, out SwarmAgentHandle handle) |
Re-spawn from a state captured with TryGetSpawnState (see below). |
Despawning agents¶
manager.DespawnAgent(handle); // remove immediately
manager.DespawnAgentAsDying(handle, 1.5f); // play a 1.5s dissolve, then pool the visual
| Member | Description |
|---|---|
DespawnAgent(SwarmAgentHandle handle) |
Frees the agent's slot immediately. Returns false for a stale handle. |
DespawnAgentAsDying(SwarmAgentHandle handle, float dissolveDuration) |
Frees the slot now but keeps the visual on screen, writing a dissolve amount for dissolveDuration seconds before pooling it. Falls back to an immediate release when the visual can't dissolve (no VAT, no pool). |
Despawning is safe by handle
A despawn swaps the last agent into the freed slot, but handles ride along: DespawnAgent
resolves the handle to the agent's current slot, so a despawn never hits the wrong agent. If
you instead track agents by raw slot index, subscribe to AgentSlotChanged to mirror the swap
(see Advanced), or sweep the dense range from the highest slot down.
Moving agents and applying forces¶
Use these to teleport agents or push them around (knockback, explosions, wind):
// Knockback away from a blast.
Vector3 dir = (agentPosition - blastCenter).normalized;
manager.AddAgentImpulse(handle, dir * 12f, externalControlDuration: 0.3f);
// Teleport an agent and reset its motion.
manager.RelocateAgent(handle, spawnPoint.position, spawnPoint.forward);
| Member | Description |
|---|---|
RelocateAgent(SwarmAgentHandle handle, Vector3 position, Vector3 forward) |
Teleports the agent (grounded), clears velocity, steering, and external forces, and wakes it if dormant. A zero forward keeps the current facing. |
AddAgentImpulse(SwarmAgentHandle handle, Vector3 impulseVelocity) |
Applies an instantaneous velocity change (ForceMode.Impulse) using the default external-control duration. |
AddAgentImpulse(SwarmAgentHandle handle, Vector3 impulseVelocity, float externalControlDuration) |
Same, with an explicit duration the external velocity overrides steering. |
AddAgentForce(SwarmAgentHandle handle, Vector3 force) |
Applies a continuous force (ForceMode.Force). |
AddAgentForce(SwarmAgentHandle handle, Vector3 force, ForceMode forceMode) |
Choose the force mode. |
AddAgentForce(SwarmAgentHandle handle, Vector3 force, ForceMode forceMode, float externalControlDuration) |
Full control. A negative duration uses SwarmSettings.DefaultExternalControlDuration; the resulting velocity is clamped to MaxExternalVelocity. |
All of these return false for a stale handle, so a despawned agent quietly drops the call instead of moving another agent.
Reading agent state¶
Read a tracked agent's live simulation pose by handle. Each getter resolves the handle first, so a
despawned agent returns false instead of handing back whoever reused its slot.
if (manager.TryGetAgentPosition(handle, out Vector3 pos))
{
healthBar.transform.position = pos + Vector3.up * 2f;
}
| Member | Description |
|---|---|
TryGetAgentPosition(SwarmAgentHandle handle, out Vector3 position) |
Current simulation position. |
TryGetAgentForward(SwarmAgentHandle handle, out Vector3 forward) |
Current facing direction (normalized). |
TryGetAgentVelocity(SwarmAgentHandle handle, out Vector3 velocity) |
Current velocity. |
TryGetAgentRadius(SwarmAgentHandle handle, out float radius) |
Collision radius. |
TryGetAgentDormant(SwarmAgentHandle handle, out bool isDormant) |
Whether the agent has settled and stopped steering. |
TryGetAgentImportanceLod(SwarmAgentHandle handle, out SwarmImportanceLod lod) |
Which LOD tier the agent is simulating at. |
TryGetAgentTargetAwareness(SwarmAgentHandle handle, out float awareness) |
How aware it is of a target this step, from 0 to 1. |
Each returns false for a stale or invalid handle.
To process every agent in one pass — your own rendering, spatial queries, analytics — read the whole active range at once instead of resolving a handle per agent:
ReadOnlySpan<Vector3> positions = manager.ReadAgentPositions();
for (int i = 0; i < positions.Length; i++)
{
// positions[i] is one live agent
}
| Member | Description |
|---|---|
ReadAgentPositions() |
All live agent positions, in slot order. |
ReadAgentForwards() |
All live facing directions. |
ReadAgentVelocities() |
All live velocities. |
ReadAgentRadii() |
All live collision radii. |
Each returns a read-only, zero-copy ReadOnlySpan over the simulation's own array — no allocation,
no per-agent lookup. The four spans share an index, so positions[i] and forwards[i] are the same
agent. Two rules come with that speed: the span is valid only for the current frame, so don't store
it; and slot order is transient — a despawn swaps the last agent into the freed slot, so an index is
not a stable identity between frames. Hold a SwarmAgentHandle when you need to follow one agent over
time.
Targets¶
You normally don't call these — adding a SwarmTarget component registers
and unregisters automatically. Use the manager directly only for code-driven targets you don't
want to represent with a component.
| Member | Description |
|---|---|
RegisterTarget(Transform target) |
Registers a transform as a target. Returns true if it's now registered (idempotent). |
UnregisterTarget(Transform target) |
Removes a registered target. |
ClearTargets() |
Removes all targets and forces every agent to retarget. |
TryGetRegisteredTarget(int index, out Transform target) |
Reads a registered target by index. |
RegisteredTargetCount / ActiveTargetCount / MaxActiveTargetCount |
How many targets are registered, how many are currently being chased, and the cap. |
TargetTransform / TargetPosition |
The first registered, non-null target and its position. |
Advanced¶
For deeper customization and diagnostics. Exact semantics are in IntelliSense.
| Member | Description |
|---|---|
Settings |
Direct access to the SwarmSettings asset. To read per-agent state, use the handle getters and span readers above — the dense arrays are internal so a stray write can't corrupt the simulation. |
GetAgentHandle(int slot) |
A stable SwarmAgentHandle for the agent at a transient slot — for example while sweeping the active range with a span reader. Returns default for an empty slot. |
TryGetAgentSlot(SwarmAgentHandle handle, out int slot) |
Resolves a handle back to its current slot. false once the agent has despawned or been recycled. |
AgentSlotChanged |
Event raised after a despawn or pressure-recycle, for code that tracks agents by raw slot. The SwarmAgentSlotChange argument carries the removed slot, the previous last slot, whether a swap-back happened, and (on a recycle) the respawned slot. Code holding a handle can ignore it. ResetSimulation clears every agent at once without raising this event, so reset your own slot bookkeeping alongside it. |
TryGetSpawnState(SwarmAgentHandle handle, out SwarmAgentSpawnState) |
Captures an agent's prefab + profiles + radius so you can re-spawn an identical agent later. |
Initialize() |
Allocates buffers and binds the visual manager. Called automatically in Awake when Initialize On Awake is enabled (the default); falls back to the first spawn when that option is off. Call it manually only to pre-allocate. |
ResetSimulation() |
Releases every agent and clears registered profiles — back to an empty swarm. |
| LOD & dormancy counts | FullTierAgentCount, ReducedTierAgentCount, CheapTierAgentCount, DormantAgentCount — used by the stats overlay. |
Quick reference in your IDE
Key members carry a short XML summary, so hovering them in Visual Studio or Rider shows a one-line description. This page has the full detail.
Related¶
- Swarm Manager — Inspector setup and simulation tuning.
- SwarmTarget — the component-based way to add targets.
- Spawners — the built-in spawners call
TrySpawnAgentfor you. - Combat Integration — applying damage and reacting to agent death.