Performance and Scaling
The plugin is built to hold 100,000+ live projectiles. Cost is kept bounded by budgets and two adaptive controllers, so frame time stays flat when the active count spikes.
Where the time goes
| Stage | Thread | Bounded by |
|---|---|---|
| Flight integration | Mass workers | Linear in active count (very cheap) |
| Collision traces | Mass workers | Collision Budget Ms + trace budgets (adaptive) |
| Tracer gather + upload | Game → render thread | Max Tracers Drawn Per Frame (adaptive cull radius) |
| Impact bursts | Game thread → GPU | Max Impact Events Per Frame, cull distance |
| Smoke puffs | Game thread → GPU | Max Smoke Samples Per Frame, smoke cull distance |
| Decals | Game thread | Max Decals Per Frame, Max Live Decals |
| Audio | Game thread | Flyby / impact sounds per frame + minimum intervals |
The two adaptive controllers
Both are on by default.
Collision (Collision Budget Ms, default 2 ms). When the measured collision pass exceeds the budget, trace budgets shrink and far projectiles trace less often (proj.FarTraceInterval stretches up to 16×). Their swept segments accumulate, so hits register a few frames later but nothing tunnels. Projectiles within proj.NearRadius (150 m) of the viewer keep a separate budget (proj.NearTraceBudget), so a distant swarm cannot starve the player's own shots.
Tracers (Max Tracers Drawn Per Frame, default 30,000). Every projectile simulates, but only the closest N are drawn. An adaptive cull radius converges on the budget, so game-thread copy, render upload and GPU spawn stay flat at 100k+ active.
Reading the numbers
proj.Stats prints a snapshot. Set Stats Log Interval (off by default) to get a periodic PERF line every that many seconds, category LogMassProjectiles. It reports:
PERF avg 118.4 FPS | active 20000 | drawn 20000 (cull 0 m) | traces/f 4096 (scale 1.00) | hits/f 312 | flybys total 12 | ... | sim 0.41 ms | collision 1.87 ms | push 0.62 ms | ...
| Field | Meaning |
|---|---|
| avg FPS | Average frame rate over the log interval |
| active | Live projectiles |
| drawn (cull) | Tracers handed to the visualizer, and the adaptive tracer radius in metres (0 = unlimited) |
| traces/f (scale) | Collision traces per frame, and the trace-budget scale (1 = full budgets; lower = collision ran over budget) |
| hits/f | Hits per frame |
| flybys total | Flyby sounds played this session |
| sim / collision / push ms | Flight integration, collision, and gather/visualize time |
| damage sites / holes / meshcuts | Used only by the optional Projectile Holes add-on (0 otherwise) |
Use Unreal Insights (-trace=cpu,gpu) for a full profile. Mass processors show up as ProjectileSimProcessor, ProjectileCollisionProcessor and ProjectileVisualizationProcessor.
Tuning checklist
| Symptom | Try |
|---|---|
| CPU spikes during big volleys | Lower Collision Budget Ms; lower proj.TraceBudget |
| Far hits register late | Expected under load; raise Collision Budget Ms if you have headroom |
| GPU cost from tracers | Lower Max Tracers Drawn Per Frame or set Tracer Cull Distance |
| Too much smoke overdraw | Lower Max Smoke Samples Per Frame, Smoke Lifetime, or Smoke Cull Distance |
| Decal cost / scene proxies | Lower Max Live Decals and Decal Lifetime |
| Physics cost from near misses | Lower Max Proximity Impulse Queries, or proj.ProximityImpulse 0 |
| Hitches on the first shot of a type | Add the type to Replicated Types (preloaded at level load) or call Register Type Config at BeginPlay |
Per-platform tuning
Put overrides in your project's Config/DefaultDeviceProfiles.ini:
[Android DeviceProfile]
+CVars=proj.CollisionBudgetMs=1.5
+CVars=proj.NearTraceBudget=1024
+CVars=proj.TraceBudget=512
+CVars=proj.FarTraceInterval=8
+CVars=proj.TracerBudget=8000
+CVars=proj.TracerCull=250000
+CVars=proj.SmokeBudget=256
[IOS DeviceProfile]
+CVars=proj.TracerBudget=8000
+CVars=proj.SmokeBudget=256
Watch the PERF line on the target device to see where the controllers settle.
Scalability
The plugin's Niagara components opt out of Niagara scalability culling (world-wide fixed bounds), because culling a shared system would hide every projectile at once. Control visual cost with the budgets above, and bind them to your scalability groups through device profiles or Scalability.ini CVars.