> For the complete documentation index, see [llms.txt](https://morningheartgames.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://morningheartgames.gitbook.io/docs/gameplay-ability-toolkit-ecs/start/swarm-demo.md).

# ECS Swarm demo

A hero in a night arena holds off a swarm of bats, from 2,000 up to 1,000,000. Every bat is a full actor with its own health, speed, bite cooldown, burns, slows and stuns, run by the ECS runtime. Meteors, a frost nova and a cataclysm hit thousands of actors in one frame, and the hero's sword waves carve through the swarm.

This page tells you how to play the demo, what its HUD reports, and which part of the demo shows which part of the runtime. Everything the demo does to its bats goes through the same requests and results you use in [Writing gameplay code](/docs/gameplay-ability-toolkit-ecs/build-with-it/gameplay-code.md), so it is a worked example as well as a showpiece.

**Before you start**

* Unity 6000.6 or newer, with Gameplay Ability Toolkit, this add-on and the Entities package installed. See [Requirements](/docs/gameplay-ability-toolkit-ecs/start/documentation.md#requirements).
* The Universal Render Pipeline (URP) and a graphics card with compute shader support. The demo draws its bats with a compute shader and indirect draw calls.
* The demo lives in `Demo/EcsSwarm/` in the add-on's folder. Nothing else in the add-on needs it, so you can delete the folder when you are done.

![The ECS Swarm demo at 2,000 enemies: a frost nova has frozen the ring of bats around the hero (pale blue), a firestorm burns part of the crowd (orange), and the damage floats up as numbers. On the right, the WHERE THE FRAME GOES panel shows the GAS runtime's share of the frame.](https://412654175-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDOGR3NzWFxmTboQuDITW%2Fuploads%2Fgit-blob-ca62825e75eaf94ae1252587b940ca81049e058c%2Fecs-swarm-demo.png?alt=media)

## Play it

1. Open **Tools > Gameplay Ability Toolkit > Dashboard** and go to the **ECS Start** page. Click **Open** on the **ECS Swarm demo** tile. Or open `Demo/EcsSwarm/Scenes/EcsSwarm.unity` yourself.
2. Press Play.

You should see the hero in the middle of a clear space, ringed by 2,000 bats, with the HUD around the edges. The demo plays in Unlimited Fire from the start: no ability costs anything or cools down, and the hero cannot be hurt. See [Unlimited Fire](#unlimited-fire).

### Controls

| Input                  | Does                                                                                                                                                                                                                                           |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| WASD or the arrow keys | Move.                                                                                                                                                                                                                                          |
| Mouse                  | Aim. The hero turns toward the pointer, and the strike, the flamethrower, Firestorm and Gravity Well follow it. The rune circle under the pointer is where Firestorm lands.                                                                    |
| Mouse wheel            | Zoom. Choosing a larger swarm zooms out on its own.                                                                                                                                                                                            |
| Left mouse button      | **Strike**: a sword swing toward the pointer that sends a sword wave through the swarm. Click again as the swing ends, or hold the button, for a three-hit combo. See [The sword combo](#the-sword-combo). A click on the HUD is not a strike. |
| 1 or Q                 | **Firestorm**: seven meteors fall around the aim point, the first on the point itself and the rest within 8 m of it. Each takes 0.45 s to land. Where it lands, it hits every bat within 3.2 m for 30, sets them burning and throws them back. |
| 2 or E                 | **Frost Nova**: chills (3 s, 35% speed) and stuns (1.5 s) every bat within 10 m of you.                                                                                                                                                        |
| 3 or R, held           | **Flamethrower**: burns every bat in a 50 degree arc up to 9 m in front of you while the key is held, and stops when you let go.                                                                                                               |
| 4 or F                 | **Overcharge**: for 4 s, your aura hits twice as hard and reaches 1.6 times as far.                                                                                                                                                            |
| 5 or Space             | **Cataclysm**: a blast that hits every bat within 32 m for 90.                                                                                                                                                                                 |
| 6                      | **Fireball Barrage**: a volley of 32 projectiles every 0.12 s for three seconds, each volley turned a little from the last.                                                                                                                    |
| 7                      | **Chain Lightning**: strikes the nearest bat within 18 m, then jumps to up to 47 more. Each strike deals 30 and stuns.                                                                                                                         |
| 8                      | **Gravity Well**: for four seconds, pulls every bat within 14 m toward the aim point, and damages them every half second.                                                                                                                      |

Hold any of keys 1, 2, 4 and 5 to keep casting. The flamethrower keeps burning for as long as its key is held. You can also click an ability's slot on the HUD, and the buttons above the ability bar for skills 6 to 8.

### What the bats do

Bats move toward you, push apart from each other, and bite when they are within 3.1 m and their bite is ready. They never come closer than 2.7 m, and bats that would stand between the camera and the hero are not drawn near it, so the hero is always in plain sight. Your aura hits every bat within 4 m for 6, twice a second. The hero regenerates mana and health.

A bat that dies plays its death animation where it fell, then comes back at the edge of the swarm at full health, so the population stays at the number you chose.

Damage floats up as numbers: white for a hit, gold for a heavy one and orange for a burn. The aura's ticks show none, since the aura touches every bat around you twice a second. Abilities, hits and deaths have sounds, and the swarm's wings grow louder as it grows.

### Choose how many bats

The buttons under **ENEMIES** at the top right set the population: 2K, 10K, 50K, 100K, 200K or 1M. Bats pour in from the edge, at most 25,000 a frame.

At 1M the camera rises to show the whole crowd from above, and the crowd renderer uses its normal distance levels of detail and culling. At that height each bat covers only a few pixels. Choose a smaller number to judge close-up detail: that brings the gameplay camera back.

![The demo at 1,000,000 enemies, seen from above. The whole population is one disc of bats around the hero.](https://412654175-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDOGR3NzWFxmTboQuDITW%2Fuploads%2Fgit-blob-92f4399cecde8e1ab6d46a5aa78c3add2cc8a27b%2Fecs-swarm-1000000.png?alt=media)

### The HUD

| Where      | Shows                                                                                                                                                                                                   |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Top left   | The number of **GAS ACTORS**, the **GAS runtime** time this frame, the **Frame** time and FPS, **Effects applied** per second, **Kills**, and how many bats are burning, chilled and frozen (stunned).  |
| Top right  | The **ENEMIES** buttons, the **UNLIMITED FIRE** badge, and the **WHERE THE FRAME GOES** panel.                                                                                                          |
| Top centre | When an ability reaches a crowd, a line such as `CATACLYSM 8,628 actors hit in one frame`. Cataclysm, Frost Nova and sword waves report this.                                                           |
| Bottom     | Health and mana (they read **INVINCIBLE** and **FREE CASTING** in Unlimited Fire), and the ability bar. Each slot shows the key, the name and the cooldown read from the ability's slot in the runtime. |

### Where the frame goes

The **WHERE THE FRAME GOES** panel splits each frame's time on the main thread into parts, in milliseconds and as a share of the frame:

| Part               | What it covers                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| GAS runtime        | The ability runtime: requests, effects, abilities, tags and cues, including its jobs.                                    |
| Swarm logic        | The demo's own systems: moving and separating the bats, spawning, finding who an ability reaches, deaths.                |
| Crowd upload       | Packing every bat into 32 bytes, copying them to the GPU, and setting up the culling and the draws.                      |
| Effects & sound    | Particles, damage numbers, sound, the hero and the camera.                                                               |
| HUD scripts        | The HUD's own scripts.                                                                                                   |
| Engine & rendering | Everything else: Unity's own work, handing the frame to the render thread, and waiting for the GPU or for vertical sync. |

Below the parts, the panel gives the render thread's and the GPU's times when Unity reports them. That needs **Frame Timing Stats** switched on in a player build, on a graphics API that supports it. Otherwise the panel says they are not reported.

Under **TURN OFF TO COMPARE** are switches for the costly parts of the demo: **Bat meshes**, **Bat animation**, **Swarm avoidance**, **Effects**, **Damage numbers**, **Sound**, **Post-processing** and **Shadows**. The simulation carries on whichever you choose, so each switch shows what its part costs. Turning the drawing off changes the frame time a great deal. The runtime's time stays where it was.

With Netcode for Entities in the project, the simulation steps at a fixed rate, not once a frame. A fast frame may run no step and a slow one several, so the panel counts the steps that ran during each frame.

### How fast it runs

[Performance](/docs/gameplay-ability-toolkit-ecs/reference/performance.md#controlled-rendered-combat-results) gives the rendered frame rate for every population from 2,000 to 1,000,000 enemies, with the hardware and the test conditions. They are controlled results for one machine, not a frame-rate promise for every game. To measure the demo on your own machine, see [Measure it on your machine](#measure-it-on-your-machine).

![The demo at 200,000 enemies, every one a GAS actor. A Gravity Well (the dark core with purple rings) pulls the crowd toward it.](https://412654175-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDOGR3NzWFxmTboQuDITW%2Fuploads%2Fgit-blob-506f61bf710df04543264eab4bac558718648ade%2Fecs-swarm-200000.png?alt=media)

## Things to try

* Click three times in rhythm for the combo. Then double-click `Demo/EcsSwarm/Montages/MT_Light_A.asset` to open it in the montage editor, move its **Hit Start** notify later, and play again. The blade lands later.
* Choose 200K, switch **Bat meshes** off, and watch the frame time fall while the GAS runtime's time stays the same.
* Press Frost Nova, then Firestorm on the frozen bats. Chilled bats are slower, and stunned ones cannot bite.
* Press Overcharge and watch the aura grow and kill faster. The ability's wait ends it four seconds later.
* Hold 3 and sweep the flame across the swarm, then tap it: it stops a moment after you let go. Select `Demo/EcsSwarm/Abilities/GA_SwarmFlamethrower.asset`, change its **Angle**, **Range** or **Interval**, and play again. What it hits and the flame you see both follow.
* Choose 200K, hold 1 and sweep the mouse across the swarm, then press Space.
* Click three times into a packed swarm. The first two waves carve crescents out of it, and the third a ring.
* Select `Demo/EcsSwarm/Effects/GE_SwarmBurn.asset` and change its duration, or `Demo/EcsSwarm/Attributes/SwarmAttributes.asset` and change a bat's speed, then play again. The SubScene bakes again when an asset it uses changes.

## How the demo is built

The demo is ordinary toolkit assets, one SubScene of authoring, and a set of systems and presentation scripts. All the scripts are in `Demo/EcsSwarm/Scripts/`.

### The authoring

Open `Demo/EcsSwarm/Scenes/EcsSwarm.unity`, stop Play mode, and tick the checkbox of `Swarm Entities (SubScene)` in the Hierarchy to open it for editing. It holds everything the runtime needs:

| GameObject                  | Components                                                                                | Shows                                                                                                           |
| --------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `GAS Catalog`               | `GasCatalogAuthoring`, with **Auto Collect Definitions** off and the demo's assets listed | A catalog that holds only its own game's assets.                                                                |
| `Player`                    | `GasEntityAuthoring` (`AS_SwarmPlayer` and nine abilities), `SwarmActorAuthoring`         | An actor, and a second baker that gives it a transform and marks it as the player.                              |
| `Enemy Template` (inactive) | `GasEntityAuthoring` (`AS_SwarmEnemy` and the bite), `SwarmActorAuthoring`                | An inactive GameObject bakes as a disabled entity, which the runtime ignores. The demo copies it for every bat. |
| `Swarm Config`              | `SwarmConfigAuthoring`                                                                    | The demo's tuning: radii, meteors, the burn's damage, how close bats crowd. Change them here.                   |

### The assets

| Asset                                                                      | Shows                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SwarmAttributes`, `AS_SwarmPlayer`, `AS_SwarmEnemy`                       | One attribute set definition, and two sets that override its base values: the player has 400 health, a bat 60. `Health` is clamped to `MaxHealth`.                                                                                                                                                                                                                                                                                                                                          |
| `GE_SwarmBurn`                                                             | A periodic duration effect whose damage is **set by caller**. It grants `State.Burning` and raises the `Cue.Swarm.Burn` cue on every tick.                                                                                                                                                                                                                                                                                                                                                  |
| `GE_SwarmChill`, `GE_SwarmStun`                                            | A Multiply modifier on `MoveSpeed`, and a pure tag grant. Both carry the `Effect.Debuff` asset tag.                                                                                                                                                                                                                                                                                                                                                                                         |
| `GE_SwarmMeteorDamage`, `GE_SwarmCataclysmDamage`                          | Instant damage that raises the `Cue.Swarm.Impact` cue, which the demo turns into a flash, a flinch and a damage number.                                                                                                                                                                                                                                                                                                                                                                     |
| `GE_SwarmAuraDamage`                                                       | Instant damage with its own cue, `Cue.Swarm.Aura`: a flinch without a number. It doubles at level 2, through its level curve.                                                                                                                                                                                                                                                                                                                                                               |
| `GE_SwarmStrikeDamage`, `GE_SwarmStrikeMana`                               | The strike's damage, 22, 30 or 50 by the level it is applied at, and the mana a landed swing gives back.                                                                                                                                                                                                                                                                                                                                                                                    |
| `GE_SwarmRegeneration`                                                     | An infinite periodic effect: the player's regeneration, applied once.                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `GE_SwarmRespawn`                                                          | An Override to the target's own `MaxHealth`, through an attribute-based magnitude, that also removes every effect tagged `Effect.Debuff`.                                                                                                                                                                                                                                                                                                                                                   |
| `GE_SwarmUnlimitedFire`                                                    | An infinite effect that grants a tag and holds two attributes. See [Unlimited Fire](#unlimited-fire).                                                                                                                                                                                                                                                                                                                                                                                       |
| `GA_SwarmFirestorm`, `GA_SwarmFrostNova`, `GA_SwarmCataclysm`              | `GasEntityAbility` assets with a cost effect and a cooldown and no effects of their own. The game decides what their success does.                                                                                                                                                                                                                                                                                                                                                          |
| `GA_SwarmFlamethrower`, `GE_SwarmFlameDamage`, `GE_SwarmFlameBurn`         | A `GasDeliveryAbility` made only of data. While it runs it hits every bat in an arc in front of the hero, as it starts and then every 0.15 s, with the two hit effects. An `InputReleased` wait ends it when the key is let go, and it owns `State.Flaming` meanwhile. The burn keeps its period running when it is applied again, so a bat held in the flame still takes its ticks. See [Abilities that reach other actors](/docs/gameplay-ability-toolkit-ecs/build-with-it/delivery.md). |
| `GA_SwarmOvercharge`                                                       | A `GasEntityAbility` that stays active. Its activation owns `State.Overcharged`, and a four-second `Delay` wait ends it.                                                                                                                                                                                                                                                                                                                                                                    |
| `GA_SwarmStrike`                                                           | A `GasEntityAbility` that waits on gameplay events: `Event.Swarm.StrikeHit` applies an effect, `Event.Swarm.StrikeEnd` ends it, and a delay ends it if neither comes. It owns and is blocked by `State.Striking`.                                                                                                                                                                                                                                                                           |
| `GA_SwarmBite`                                                             | A bat's ability. It is blocked while the bat is `State.Stunned`, and refused when its target holds `State.UnlimitedFire`.                                                                                                                                                                                                                                                                                                                                                                   |
| `GA_SwarmFireballBarrage`, `GA_SwarmChainLightning`, `GA_SwarmGravityWell` | The three extra skills on keys 6, 7 and 8. See [Skills 6 to 8](#skills-6-to-8).                                                                                                                                                                                                                                                                                                                                                                                                             |

### Unlimited Fire

Unlimited Fire changes no code path in the runtime. It is an effect and a tag, and the demo is always in it:

* `GE_SwarmUnlimitedFire` is an infinite effect that grants the player `State.UnlimitedFire`. `SwarmPlayerSystem` applies it whenever the player lacks the tag: at the start, and again should anything remove it.
* While the player has the tag, `SwarmPlayerSystem` asks for every ability at level 2. Each ability's cooldown curve and each cost effect's magnitude are zero at level 2, so the runtime's ordinary cost and cooldown checks let every press through.
* `GA_SwarmBite` lists `State.UnlimitedFire` among its target's blocked tags, so the runtime refuses a bite whose target is the player.
* The effect also carries two Override modifiers: `Health` to the player's `MaxHealth`, and `Mana` to 100. While it lasts, both current values are held there whatever happens to the base values underneath, so the bars stay full.

### The sword combo

The strike is an ability timed by an animation, the way Unreal's montages drive abilities. The timeline runs in the ECS runtime, and the ability's rules are in its data:

1. A click asks the runtime to activate `GA_SwarmStrike` at the combo's step, which is the level it runs at. The ability owns `State.Striking` while it runs, and the same tag blocks it, so a second strike cannot start over the first.
2. The ability lists three montages: `MT_Light_A`, `MT_Light_B` and `MT_Light_C`. The runtime starts the one for its level on the player. `GasMontageSystem` advances it each frame and reports every notify it passes as a `GasMontageEvent`.
3. On the **Hit Start** notify, `SwarmStrikeSystem` sends a sword wave from the hero toward the aim. `SwarmWaveSystem` flies it: each frame the wave's leading edge moves on, and every bat in the band it swept since the last frame, inside its arc, is hit once for the strike's damage at the swing's level and thrown back. The crescent on screen is drawn from the same origin, radius, depth and arc, so a bat is hit exactly when the wave is seen to pass through it. The wave's first hit sends the ability `Event.Swarm.StrikeHit`, which its wait turns into mana.
4. Between the **Combo Start** and **Combo End** notifies, a click, or the button held, ends this swing with `Event.Swarm.StrikeEnd` and asks for the next step. The next montage replaces this one. A swing played to its end sends the same event, as the ability's **Montage Ended Event**, and the next click starts again from the first swing.
5. The hero is drawn in the pose of the montage's clip at the runtime's montage time, so the blade lands on screen in the frame the hit lands in the simulation.
6. The swings are full-body montages with root motion. The catalog bakes each swing's lunge from its clips' motion curves, the runtime reports how far it moved the hero each frame as `GasMontageRootMotion`, and `SwarmPlayerSystem` moves the hero by that, turned the way the hero faces, instead of by the keys.

The three swings:

| Swing | Damage | Wave                                          |
| ----- | ------ | --------------------------------------------- |
| 1     | 22     | A crescent 120 degrees wide, out to 14 m.     |
| 2     | 30     | A wider crescent, 150 degrees, out to 15.5 m. |
| 3     | 50     | A ring all the way round, out to 17 m.        |

![The third swing of the combo: its sword wave hits more than a hundred bats in one frame, and the hits float up as numbers.](https://412654175-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDOGR3NzWFxmTboQuDITW%2Fuploads%2Fgit-blob-8322a9058f0bd75a880fd71c61872df57d5b067c%2Fecs-swarm-combo.png?alt=media)

The casts work the other way round. Firestorm, Overcharge, Chain Lightning, Frost Nova, Cataclysm, Gravity Well, the flamethrower and the barrage each play a montage in the **UpperBody** slot: `MT_SwarmCast`, `MT_SwarmCastBig` or `MT_SwarmFlame`. `MT_SwarmFlame` loops until its ability ends and stops it. The hero is drawn with that slot over the chest, arms and head only, weighted by the montage's own blend in and out, so the hero casts while its legs keep running.

To change when the blade lands or how long the combo window stays open, move the notifies in the montage editor. To change the damage, the mana or what may interrupt a strike, edit the ability and its effects. Neither side needs the other's numbers. The presenter only reports the mouse button. The combo's state, `SwarmCombo`, lives on the player entity.

The runtime plays the timeline itself, from the same `AnimationMontage` assets the toolkit uses, because a MonoBehaviour that times notifies against an Animator, such as the toolkit's `MontagePlayer`, runs outside the simulation. With Netcode for Entities it could not be predicted or rolled back, and the pose it shows and the time the simulation acts on could drift apart. Whatever draws the character, the ability sees only gameplay events and montage events, so the same ability works with any animation system. See [Cues and presentation](/docs/gameplay-ability-toolkit-ecs/build-with-it/presentation.md#posing-a-character-from-its-montage).

### Skills 6 to 8

Keys 6, 7 and 8 are samples of custom gameplay written against the runtime's queues. Each has an ordinary ability asset with its own cooldown, and is started by an ordinary activation request. The code is in `SwarmShowcaseSystems.cs`, and the input and visuals are in `SwarmShowcasePresenter.cs`. Change them freely.

* **Fireball Barrage** creates each volley of 32 projectiles in one batch, as ordinary `GasProjectile` entities. The stock delivery pipeline moves them and hits with the ability's hit effects. The ability asset sets speed, range, hit radius, pierce, damage and burn.
* **Chain Lightning** picks up to 48 distinct targets in a Burst job. The first is the nearest bat within 18 m. Each jump stays within 9 m of the last bat and skips bats less than 2 m from it. The damage and the stun are ordinary effect requests.
* **Gravity Well** pulls the bats in a Burst job, and queues the aura's damage effect for the bats inside its radius every half second. Cancelling the ability stops the field.

`SwarmShowcaseResultSystem` reads successful activations before `SwarmResultSystem` clears the result queue, so a refused activation never starts a barrage, a chain or a field. `SwarmShowcaseTickSystem` reads the ability's slot and stops the volleys or the pull when the ability ends or is cancelled. Fireballs already in flight finish their delivery normally. `SwarmShowcasePresenter` only reads the world: it draws batched fireball billboards and trails, pooled lightning lines and the gravity vortex, and contains no damage or movement logic. For abilities that hit other actors without code, see [Abilities that reach other actors](/docs/gameplay-ability-toolkit-ecs/build-with-it/delivery.md).

### The simulation

The demo's systems run in `GasRequestSystemGroup` (before the runtime) or `GasResultSystemGroup` (after it). Everything that visits every bat is a Burst job that reads the runtime's data read-only. The main thread only turns the few bats something happened to into requests.

| System                                  | Group              | Shows                                                                                                                                                                                                                                    |
| --------------------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SwarmSetupSystem`                      | request            | Looking up every index by name once, into a `SwarmIndices` component, and applying a passive effect once.                                                                                                                                |
| `SwarmSpawnSystem`                      | request            | Spawning actors in bulk by copying the template, at most 25,000 a frame, across the disc the swarm will fill.                                                                                                                            |
| `SwarmPlayerSystem`                     | request            | Reading an attribute read-only. Asking for abilities at the level a tag decides. Applying an effect when a tag is missing. Turning the hero toward the aim, and writing whether the flamethrower's key is held into its `GasInputState`. |
| `SwarmEnemySystem`                      | request            | Burst jobs that sort the bats into a grid of cells, work out how neighbours push each other apart, move each bat toward the player and back from explosions, keep it out of the player's clear space, animate it, and decide who bites.  |
| `SwarmWaveSystem`                       | request            | Flying the sword waves and hitting the bats in the band each one swept this frame, once each, at the swing's level.                                                                                                                      |
| `SwarmAuraSystem`                       | request            | Applying an effect at a level chosen from a tag the player owns, to the bats a parallel job found in range.                                                                                                                              |
| `SwarmImpactSystem`                     | request            | Landing the meteors and blasts whose time has come, all in one pass over the swarm: damage, a meteor's burn with one shared set-by-caller value, and a push.                                                                             |
| `SwarmStrikeSystem`                     | result             | Reading the runtime's montage events. On Hit Start it sends a sword wave. Combo Start and Combo End open and close the window for the next swing.                                                                                        |
| `SwarmResultSystem`                     | result             | Reading activation results, turning a success into area effects or queued impacts, and recording refusals for the HUD.                                                                                                                   |
| `SwarmCueSystem`                        | result             | Reading cue events in a Burst job and clearing them. An impact cue makes a bat flash and flinch, and the health it lost since its last cue becomes a damage number.                                                                      |
| `SwarmDeathSystem`                      | result             | Finding the bats whose health reached zero with a change-filtered Burst job, then sending each to the swarm's edge with a respawn effect instead of destroying it, and recording where it fell for its body.                             |
| `SwarmVisualStateSystem`                | result             | A change-filtered Burst job that turns each bat's tags into how it is drawn.                                                                                                                                                             |
| The systems in `SwarmMeasureSystems.cs` | around the runtime | Time the runtime and the demo's own systems, and count requests, for the HUD.                                                                                                                                                            |

### The presentation

| Type                                                      | Shows                                                                                                                                                                                                                                                                                                                                                                        |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SwarmPresenter`                                          | The only bridge from input to the world, and from the world to the screen. It writes `SwarmPlayerInput`, moves the camera, poses and draws the hero from the world's state, times the frame's parts for the HUD, and hands the rest to the types below. It reads the world with read-only access and writes nothing in it but the player's input and the HUD's measurements. |
| `SwarmHeroAsset`, `SwarmHeroRenderer`, `SwarmHero.shader` | The hero without an Animator: one mesh, the sword included, skinned on the GPU from bone matrices stored in a texture, and blended between two poses. During a swing the pose is the runtime's montage clip and time. After a cast it is the cast's clip. Otherwise it is idle, jog or sprint by the hero's speed.                                                           |
| `SwarmCrowdRenderer`                                      | Every bat in a handful of draw calls. A Burst job packs each bat into 32 bytes, a compute shader culls them against the camera and picks each one's level of detail on the GPU, and each level is one indirect draw.                                                                                                                                                         |
| `SwarmCrowdAsset`, `SwarmCrowd.shader`                    | The bat's animations stored in textures, one row a frame, which the shader reads to pose each bat on its own. A bat's colour comes from its gameplay state: embers for burning, a cold rim for chilled, ice for stunned, a hot edge for a hit.                                                                                                                               |
| `SwarmDamageNumbers`, `SwarmNumbers.shader`               | Damage numbers from the digits of Unity's built-in font, every character one instanced quad. Numbers that would land on the same spot of the screen at once are thinned out.                                                                                                                                                                                                 |
| `SwarmAudio`                                              | A small pool of voices, and loops for the wind, the swarm's wings and the flamethrower. A frame with thousands of deaths plays one louder sound, not thousands.                                                                                                                                                                                                              |
| `SwarmEffects`, `SwarmSlash.shader`                       | Explosions, shockwaves, scorch marks, falling meteors, the flamethrower's jet, flashes of light, the sword waves and the camera's shake, from the events the simulation raises. Everything is pooled and drawn in bulk, and a frame's full explosions and death bursts are capped.                                                                                           |
| `SwarmHud`, `SwarmHud.uss`                                | The HUD in UI Toolkit, with the **WHERE THE FRAME GOES** panel and its switches. Its look is in the style sheet.                                                                                                                                                                                                                                                             |
| `SwarmShowcasePresenter`                                  | The input and the visuals for skills 6 to 8.                                                                                                                                                                                                                                                                                                                                 |
| `SwarmBenchmark`                                          | The `-swarmBenchmark` mode. See [Measure it on your machine](#measure-it-on-your-machine).                                                                                                                                                                                                                                                                                   |

## Using it in your own project

The demo is safe to delete. Nothing else in the add-on depends on it, and it carries its own art, montages and sounds. It needs nothing from the toolkit's own demos either: the sword swings, their clips and the ability icons are copies kept in its folder, so the toolkit's `Demo` and `Tutorial` folders can be deleted without breaking it.

If you keep the demo while you build your own ECS game, note that a catalog with **Auto Collect Definitions** on also collects the demo's assets. See [Performance](/docs/gameplay-ability-toolkit-ecs/reference/performance.md#keep-the-catalog-to-what-the-game-uses).

In a project with Netcode for Entities, Netcode creates a server world and a client world in Play mode. The demo runs in the default world, which is the server world. It is not a networked game. The [Netcode for Entities](/docs/gameplay-ability-toolkit-ecs/build-with-it/netcode-for-entities.md) page covers the networked setup.

## Measure it on your machine

The demo includes a benchmark that plays the scene by itself and writes the frame times to files.

1. Build a release player that contains `Demo/EcsSwarm/Scenes/EcsSwarm.unity`.
2. Close other GPU and CPU workloads, then run the player from a command line:

   ```
   EcsSwarm.exe -screen-width 1920 -screen-height 1080 -screen-fullscreen 0 -swarmBenchmark -swarmBenchmarkOut run-1.txt
   ```
3. Wait. The benchmark ignores the keyboard and the mouse, tests each population in turn, and quits when it is done. For each one it measures the bats moving, then a mixed combat of Firestorm, Cataclysm, the flamethrower, the barrage, Chain Lightning and Gravity Well. It turns VSync off and removes the frame cap.
4. Read `run-1.txt`. It has one line per measurement: the average FPS, the mean and 95th percentile frame times, and the GAS runtime's median time. `run-1.json` holds the full report, including the Unity version, CPU, GPU, graphics API and quality level. `run-1.csv` holds every sampled frame.

Run it in three fresh player processes when you compare hardware or settings, and report the CPU, GPU, resolution, quality level and graphics API with your results. The arguments:

| Argument                        | Does                                                                                                                                                                                                                         |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-swarmBenchmark`               | Starts the benchmark.                                                                                                                                                                                                        |
| `-swarmBenchmarkOut <file>`     | Where to write the text report. The JSON and CSV go beside it. The default is `SwarmBenchmark.txt` in the player's persistent data folder.                                                                                   |
| `-swarmBenchmarkCounts <list>`  | Tests only these populations, comma separated, for example `2000,50000`. Each must be between 1 and 1,000,000.                                                                                                               |
| `-swarmBenchmarkOffscreen`      | Renders the gameplay camera to a 1920 x 1080 texture every frame and waits for the GPU, so a hidden window is still measured. The wait and a one-pixel readback are in the times. It saves a PNG after the last measurement. |
| `-swarmBenchmarkShots <folder>` | Saves a screenshot of each measurement, after its timed window.                                                                                                                                                              |

The benchmark measures the whole game, rendering included. The one-million-actor tests in [Performance](/docs/gameplay-ability-toolkit-ecs/reference/performance.md) measure the runtime alone, and are a separate workload. Do not present them as a frame rate for rendered characters.

## Next steps

* [Writing gameplay code](/docs/gameplay-ability-toolkit-ecs/build-with-it/gameplay-code.md): the requests and results the demo's systems use.
* [Abilities that reach other actors](/docs/gameplay-ability-toolkit-ecs/build-with-it/delivery.md): make area, arc, nearest-target and projectile abilities from data, as the flamethrower and the barrage do.
* [Cues and presentation](/docs/gameplay-ability-toolkit-ecs/build-with-it/presentation.md): turn cue events into visuals and sound, and pose a character from its montage.
* [Performance](/docs/gameplay-ability-toolkit-ecs/reference/performance.md): the measured numbers, and the habits that keep a game fast.
