Quick Wave Spawner Pro
A deterministic wave director for tower-defense, survivor and horde games: an editor timeline that plots every spawn before Play, and a pooled runtime that provably executes the same plan.
by Krykftn | Advanced Editor Tools & Systems
Price History +
Every wave spawner can spawn things. The question is whether you can see the encounter before you press Play - and whether what you saw is what actually happens.
This one plots it. Open the Wave Timeline window, pick a schedule, and every spawn of the whole encounter is drawn on one strip: what spawns, when, from which entry. The same code computes that strip and drives the game, and the package's tests step the runtime against the plan spawn for spawn to prove they match. It is a preview in the literal sense.
AUTHOR WAVES AS ASSETS
A WaveSchedule is a ScriptableObject: waves of entries, each entry saying which prefab, how many, and how they are spread - Burst (all at once), Stream (evenly over a duration) or Stagger (evenly with seeded jitter). Waves advance by one of three rules:
• AfterDelay - fully timed, fully plottable
• WhenCleared - the next wave waits until everything is dead; player-paced pressure
• ClearedOrDelay - whichever comes first; reward fast clears without letting slow ones stall
DETERMINISTIC, PROVABLY
The same seed always produces the same plan, on every platform - randomness comes from an explicit generator, not System.Random, whose algorithm is not guaranteed stable. Waves are seeded independently, so editing wave three never changes wave one's plan. Set a different seed per run when you want variety.
DRIFT-FREE TIMING
Scheduled transitions anchor to their scheduled moments, not to the frame that noticed them. Without that rule, each wave boundary adds up to a frame of lateness and the error accumulates - measured at 216 ms of drift by the second wave before the rule existed. With it: across deliberately ugly random frame times, no spawn ever fires early and none is later than one frame.
ONE CALL TO INTEGRATE
When something dies in your game, call runner.Despawn(instance). That single call returns the object to its pool and lets clear-based waves advance. Everything else - pooling, timing, spawn points, events - is the runner's job.
POOLED, COUNTED
Spawned objects come from per-prefab pools with prewarm. The counters are public, because "it pools" is a claim: in the sample encounter, 43 spawns are served by 24 objects, all created up front, with nothing instantiated after.
IT NAMES YOUR MISTAKES
The timeline window checks the schedule and the runner, and says what it finds in words: a WhenCleared wave that spawns nothing and would therefore wait forever, a prefab index the runner does not have, a stream over zero seconds, a spawn group with no usable points. A correct setup produces no findings.
THE SAMPLE
Four waves, three enemy types, all three advance rules, a live HUD counting alive / spawned / objects-ever-created, click-to-kill, and an auto-killer so the encounter also completes unattended. Rebuild it any time from the menu.
MEASURED
The numbers here come from the automated checks that ship with the package, run on Unity 2022.3.62f2: runtime-versus-plan parity across random 5-125 ms frames with worst lateness 115 ms and zero accumulation; a hand-computed stream of five over eight seconds landing exactly on 0, 2, 4, 6, 8; 43 spawns from 24 pooled objects. Each suite was verified by deliberately breaking the code and confirming the tests caught it.
REQUIREMENTS
• Unity 2022.3 LTS or newer
• Pure C#, no dependencies, no accounts, any render pipeline
LIMITS
Spawn counts and timings are fixed per schedule; difficulty scaling is yours to apply by choosing a schedule or editing counts before Play. WhenCleared relies on your Despawn calls - if nothing reports deaths, the wave waits by design, and the inspection names the one case where that is certain. One runner runs one schedule at a time; use several runners for overlapping encounters.