Performance¶
This page covers the levers for keeping 1000+ agents at frame rate on your target hardware.
A measured number to start from
3,848 agents at 63 FPS (15.8 ms) in the Pyramid demo scene, with every one of them on screen. Windows release build, not the editor. Unity 2022.3.62f2 on an Intel i7-10700K with an NVIDIA RTX 3080, at 1440p on the High Fidelity tier with dynamic shadows and camera effects on. The simulation itself took 9.6 ms of the 33.3 ms fixed step.
The Pyramid demo is a free download and prints those same counters on screen. Check the figure on your own machine.
Why it scales¶
Three architectural choices do most of the work:
- No per-agent
Update. A singleSwarmManagerticks the whole swarm in oneFixedUpdateloop, with no per-GameObject scheduling overhead. - Data and visuals are decoupled. Simulation runs on flat arrays in
SwarmAgentData; visual GameObjects are pooled and follow data. You can swap prefabs, change materials, or use VAT without touching simulation code. - Importance LOD. Distant agents pay less per step. The tier controls simulation cost, meaning behavior recompute cadence, personal space sampling and body-blocking frequency, not how agents look. Visual quality follows the camera independently (see Visibility LOD).
See Concepts for the full mental model.
How to measure¶
Before tuning, measure. Two tools help:
Stats Overlay¶
On SwarmManager (Debug section), turn on Show Stats Overlay. The Game-view HUD shows:
- Frame rate and frame time.
- Active agent count against capacity.
- On-screen and off-screen counts.
- Tier counts (Full / Reduced / Cheap / Dormant), plus Surrendered whenever surrender is on, which it is by default.
- The fixed timestep in milliseconds, and the current time scale.
A Body Blocking block joins them when Show Body Blocking Stats is on in the same inspector section. This is the diagnostic for a crowd that overlaps when it should not, and it beats the Profiler marker for the job, because it gives you counts rather than milliseconds. Each of the four counted rows shows a total and a per-agent average.
| Row | What it tells you |
|---|---|
| Pairs examined | Overlapping agent pairs the pass looked at this step |
| Pairs resolved | How many it actually pushed apart. A large gap between examined and resolved means the budget is being spent on pairs that get thrown away |
| Reject - symmetric | Pairs skipped because the other agent already handled this pair. Expected, and roughly half of the examined count in a healthy run |
| Reject - cell miss | Pairs dropped because the neighbour was not in a cell this agent scans. A big number here points at Grid Cell Size being wrong for your agent radius |
| Max neighbors / agent | The worst-case neighbour count any single agent saw |
| Agents at pair cap | How many agents hit Body Blocking Max Pairs Per Agent and stopped resolving. If this is not near zero in a crowd that overlaps, that cap is your problem, not the correction strength |
| Cells visited | Spatial-hash cells touched by the pass |
Watch Agents at pair cap first. That one number distinguishes "the pass is too weak" from "the pass ran out of budget", and they need opposite fixes.
Enable Show Stats Overlay System Info to append the hardware block: device, model, CPU and core count, GPU with its vendor and graphics API, system RAM and GPU memory. Useful when validating across devices.
For finer numbers, switch the inspector to Advanced and turn on Track Step Timings. The overlay then shows the rolling average per step. You can leave it on in a shipped scene: samples are only taken while the overlay itself is visible, so it costs nothing until someone opens the HUD.
Moving and restyling the HUD¶
Turning the overlay on adds a SwarmStatsOverlay component to the manager GameObject, and that component carries the appearance settings. Go there when the HUD lands under a phone notch, comes out unreadably small on a 4K monitor, or colors your timings wrong for the hardware you are targeting.
- Screen Offset and Box Size place and size the panel. Auto Fit Height grows it to the content, so the height in Box Size is a starting point rather than a limit.
- Line Height, Timing Row Height, Timing Side Panel Min Width and Timing Side Panel Gap lay out the rows and the timings column beside them. Min Displayed Timing Ms hides rows below a threshold so noise does not fill the panel.
- Refresh Interval is how often the text is rebuilt,
0.25seconds by default. The HUD is not meant to be read frame by frame. - The DPI group handles the mobile-versus-desktop size problem automatically. Scale With Screen Dpi is on by default and works from Reference Dpi (
160, Android's baseline), a Scale Multiplier of0.7, and Min Scale / Max Scale clamps of0.7and4. The Stats Overlay Scale slider on the manager multiplies on top of all that, so reach for the slider first and only come here if the automatic result is wrong on a device rather than merely not to taste. - Timing Colors sets the thresholds a step time has to cross to turn yellow, orange, then red, at
0.25,0.75and1.5ms by default, along with the four colors themselves. Those defaults are tuned for a 30 Hz simulation step; on a tighter budget, lower them so the colors still mean something.
Unity Profiler¶
The simulation runs inside the manager's FixedUpdate. Look for the Swarm StepSimulation sample; every per-pass marker nests under it with a Swarm prefix (for example Swarm Importance LOD, Swarm Integrate Agents, Swarm Body Blocking). If a single behavior dominates, that is your tuning target.
Tuning levers¶
Capacity¶
Set Max Agents in SwarmSettings to the highest count you actually spawn. Higher capacity allocates more arrays at startup; it does not cost per-step time, but it does cost memory.
Fixed timestep and the catch-up spiral¶
Apply Fixed Timestep On Initialize ships on. It pins the swarm to Fixed Timestep (default 0.0333, 30 Hz) and caps Maximum Allowed Timestep so a slow frame cannot snowball: without the cap, one long frame makes Unity run several catch-up simulation steps the next frame, each heavier under load, which drops the frame rate further and queues even more steps. The cap lets in-game time slow down instead. If a swarm collapses to single-digit fps under load, check this toggle first.
Raising the simulation rate (a smaller Fixed Timestep) multiplies every per-step cost. At 1000+ agents, 30 Hz looks the same as 50 Hz and costs noticeably less. See Swarm Settings: Timing & Visuals.
Importance LOD¶
The single biggest lever. Three tiers, classified by planar distance to the agent's target and by whether the agent is being drawn:
- Full: closest agents. All behaviors run every step.
- Reduced: mid-range agents. Personal Space runs less frequently and with fewer neighbor samples. Approach & Press steering is recomputed on a throttled interval; cached results fill the skipped steps.
- Cheap: distant agents. Personal Space runs even less often. Approach & Press throttling is more aggressive.
Tune in SwarmSettings → Importance LOD:
- Lower Reduced Quality Start Distance to shrink the Full-tier band around the target, so agents drop to Reduced sooner.
- Lower Cheap Quality Start Distance to push more agents into the Cheap tier.
- Set Full Quality Agent Cap to a fixed number so a pile-on never explodes the budget.
See Swarm Settings: Importance LOD for fields, and Behaviors → Importance LOD and behavior cost for the per-behavior table.
Dormancy¶
Stuck agents in dense crowds stop steering entirely until space opens up, and the saving compounds with crowd density. This ships on, so the lever here is tuning the thresholds in SwarmSettings, or turning it off if you need every agent computing every step.
Grounding Execution Mode¶
Ground probes are one of the heaviest passes at high counts, and how they run is an opt-in choice in SwarmSettings. Immediate is the shipped default and the reference path: one Physics.SphereCast per agent on its scheduled step, on the main thread.
Batched runs the same casts together as batch jobs on worker threads inside the same fixed step. Results are ready before anything reads them, so the outcome is identical and only the main-thread cost drops.
Batched Deferred schedules the batch on one step and applies the results on the next, so the main thread never waits. It is the cheapest of the three and costs one fixed step of grounding lag, which render interpolation hides.
Try Batched first when the profiler shows grounding high on a large swarm. Go to Batched Deferred if the main thread is still the bottleneck and nothing in your game reads exact ground height on the same frame it changes. Switch back to Immediate to rule the pass out while debugging.
The other half of the grounding bill is how often the probe runs at all, and that is a separate pair of settings. Ground Probe Interval (default 8) is the stride for Full and Reduced agents, so at a 30 Hz step an agent refreshes its ground height roughly every 0.27 seconds. Ground Probe Interval Cheap (default 60) does the same for the far, off-camera tier, around a 1.2 second window, and it is clamped to at least the base interval so the cheap tier can never probe more often than the near one. Cost scales close to 1 / interval, which makes these the bluntest grounding lever available.
Both defaults assume terrain that stays where you left it. Raise the numbers on a flat level to buy back time. Lower Ground Probe Interval if agents on moving platforms, lifts, or fast vertical terrain visibly lag the surface, and expect to pay for it. Ground Probe Max Hits Per Agent (default 8) is the third of the trio and multiplies straight into per-agent cost in layered geometry.
See Swarm Settings.
Hard Agent Separation: budget before enabling¶
Hard Agent Separation (SwarmSettings → Body Blocking) holds spacing in a crushing crowd, but it is a cost lever, not a saving. Unlike soft Body Blocking, it runs on every awake agent every step with no Importance LOD throttle, so its cost scales with the awake count rather than the visible count. Turn it on only for scenes that visibly pile up, watch the Swarm Body Blocking profiler marker after enabling it, and leave it off elsewhere.
See Swarm Settings: Hard Agent Separation.
Render Smoothing: cheap, on by default¶
Enable Render Smoothing (SwarmSettings → Timing & Visuals) runs a small per-agent filter during the visual sync: about 77 nanoseconds per agent, roughly 0.08 ms per frame at 1000 agents, with no allocations. It runs only for visible, non-dormant agents, so it scales with the on-screen count rather than the total. It is not worth turning off for performance. Disable it only if you want raw, unfiltered render positions. The cost is identical at any cutoff value.
See Swarm Settings: Timing & Visuals.
Off-screen animation: check the box, keep the saving¶
Update Only When Visible on SwarmVatAnimation or SwarmAgentAnimation is where most of the animation budget goes in a big crowd. With it on, an agent Unity has stopped drawing stops refreshing its animation once Visibility Grace Time runs out, and the visual manager skips it entirely rather than paying a throttled call that does nothing. The saving scales with how much of the crowd is undrawn, which in a 1000+ swarm is usually most of it. An agent that is off camera but still casting a shadow into frame counts as drawn and keeps animating, so the saving comes from the crowd behind you, not from anything you can see.
It ships checked. Leave it that way unless something specific needs off-screen agents animating, because the pose costs nothing to recover: VAT playback advances on elapsed time, so a returning agent picks up where it would have been, and SwarmAgentAnimation agents are already stopped by Cull Completely.
If you do uncheck it, the agent goes back on the Off Screen Animation Refresh stride in SwarmSettings → Visibility LOD. That stride ships at 4. Do not drop it toward the minimum of 1, or you go back to paying Animator.SetFloat calls and MaterialPropertyBlock writes every sync for agents nobody is looking at.
See Swarm Settings: Visibility LOD and Animation: Camera-keyed animation refresh.
Behavior weighting: disable, don't zero¶
Several behaviors cost query budget even when their weight is 0. If a behavior is not contributing, disable it on the profile. Setting the weight to 0 is not the same as disabling.
See Behaviors → Runtime blending.
Spawning bursts¶
A long pause followed by a burst can produce a one-frame spike. Cap it with Max Spawn Per Frame on the Swarm Spawner Director.
Mobile¶
The dominant mobile cost is almost always the agent prefab, not the swarm simulation. On a test device at 1000 agents, the simulation cost ~10 ms; replacing a SkinnedMeshRenderer agent with a simple capsule raised frame rate from ~16 fps to ~60 fps on the same device.
Mobile fix priorities: apply in order
- Switch to VAT animation instead of
SkinnedMeshRenderer+Animator. - Use a single material per agent (no multi-material mesh).
- Keep the mesh low-poly.
- Disable shadow casting on the agent renderer.
- If you are using
SwarmAgentAnimation, leave Animator Culling Mode onCull Completely. That is the shipped default and the component applies it to the Animator for you, so this one is a check, not a change.
See Animation for the full VAT setup guide, and VAT Render Quality Tiers for choosing the right shader tier for mobile vs. desktop.
Surface Flow Field cost¶
The flow field bake scales with grid cell count, not agent count.
Once baked, the per-agent runtime cost depends on the tier. Cheap agents do a single cell lookup. Full and Reduced agents blend the direction across the four cells around them, because Enable Surface Flow Bilinear Direction ships on and keeps headings from stepping at every cell border. It is cosmetic and does not change the route, so turning it off is a real saving on the two expensive tiers if you can live with the stepping. Congestion bias, off by default, adds candidate-cell scoring on top of whichever path you are on.
Bake throughput is controlled by Surface Flow Concurrent Bakes (low values, 1 to 2, avoid traffic spikes) and the per-tier sample intervals. See Navigation: Performance guidance for details.
Quick reference¶
| Symptom | First lever to pull |
|---|---|
| Frame rate collapses to single digits under load | Confirm Apply Fixed Timestep On Initialize is on; keep Fixed Timestep at 0.0333. |
| Frame drops, simulation-bound | Lower Reduced Quality Start Distance and set Full Quality Agent Cap. |
| Frame drops, render-bound | Switch the archetype to VAT. Use a low-poly single-material mesh. |
| One-frame spikes when many agents spawn | Lower Max Spawn Per Frame on the director. |
| Dense crowd thrashes | Tune the Dormancy thresholds. It is already on by default. |
| Dense crowd interpenetrates despite spacing | Enable Hard Agent Separation, which costs more (every awake agent, every step). |
| Grounding high in the profiler at high counts | Switch Grounding Execution Mode to Batched, then Batched Deferred. |
| Large off-screen crowd still costs animation time | Confirm Update Only When Visible is on in the agent's animation component. |
| Mobile is much slower than desktop | Replace the agent prefab. See the Mobile section above. |
Related¶
- Concepts: the architecture that makes the levers work.
- Swarm Settings: where Capacity, LOD, Dormancy live.
- Animation: VAT, Animator-based animation, mobile prefab checklist.
- Troubleshooting: performance issues and fixes.