Flukz — open-source shmup community resource open-source GPL devlog  ·  about
// indie shmup & game-dev resource

Game Programming · June 30, 2026

Procedural Enemy Wave Generation in Shmups: Patterns, Density, and Seeded Runs

The appeal of procedural wave generation is not randomness — it is infinite authored variety from finite design work. The challenge is that shmup waves are not just sequences of enemies; they are spatial and temporal compositions where position, timing, and bullet behaviour interact to produce readable patterns that can be learned and countered. Getting procedural generation to respect that composition logic is the hard part.

The wave descriptor format

Procedural wave generation works best when the generator operates on wave descriptors rather than individual enemies. A wave descriptor encodes the shape of a wave as a unit of authored intent, and the generator selects and combines descriptors rather than placing enemies freeform.

A minimal descriptor might include: entry formation (V-formation, column, flanking pair), entry side (left, right, top), delay before the wave fires its first shot, bullet pattern index, enemy count, and a difficulty weight used in the budget system. Storing descriptors in JSON or a simple scripting format makes it easy for non-programmers to add wave types without touching generation code.

The procedural layer then selects a sequence of descriptors per stage section, drawing from the descriptor pool according to the difficulty budget of the current section. This keeps the generator working at the level of compositions, not individual enemy placements, which makes it far easier to reason about what patterns can and cannot be generated.

Density budgeting across a stage

A procedurally generated stage needs a density envelope that rises toward a section climax and eases before the boss. Without an explicit budget, the generator can stack multiple high-density waves back-to-back and produce a section that is either brutalising or trivial depending on random draw.

Represent stage density as a curve of budget points across time. Each wave descriptor has a cost. The generator fills each time window with waves whose total cost fits the window budget. Tension builds as budgets increase toward a section peak, then the post-peak window has a very low budget to give the player breathing room before the boss transition.

Stage section Budget (arbitrary units) Allowed descriptor types
Opening10Simple columns, small flanking pairs
Mid-section ramp25–40V-formations, aimed patterns, flanking groups
Peak55–70All types including dense formations
Pre-boss relief8Simple waves only; no aimed fire
Boss intro0Boss spawner only

Seeded RNG and reproducible runs

A procedurally generated run that cannot be reproduced is a run that cannot be discussed, shared, or practised. Seeded RNG solves this: one seed value at the start of a run initialises all random choices throughout the run, so the same seed always produces the same sequence of waves, enemy positions, and item drops.

Displaying the seed to the player at the end of a run (or letting them enter a seed before starting) creates a lightweight sharing mechanic. Players can share seeds for challenging runs, and developers can reproduce any specific run for debugging without requiring a save state system.

The technical requirement is a self-contained RNG object that is seeded once and passed explicitly to every function that needs randomness. Avoid any use of the engine's global random functions, which may be called by other systems in unpredictable orders and would break reproducibility. A simple LCG or Xorshift implementation is sufficient for wave generation purposes.

Guardrails that keep generation sane

Unconstrained procedural selection can produce valid-but-terrible outcomes: three consecutive left-side formation waves that make the right side of the screen permanently empty, or a wave that fires aimed bullets at the player while another active wave has already created a wall of bullets from the opposite direction. These are technically within budget but spatially impossible to navigate.

Practical guardrails to add before shipping:

When to use procedural generation, and when not to

Procedural generation is valuable for replayability-focused games: roguelite shmups, endless modes, daily challenge runs. It is a poor fit for authored narrative campaigns where each stage tells a designed story and the boss introduction should feel like a crafted revelation rather than a statistical outcome.

A hybrid approach often works well: authored boss fights and key set-piece moments, with procedural waves filling the stage connective tissue between them. The authored sections carry the emotional and mechanical peaks; the procedural sections handle the volume of combat that would otherwise require enormous manual level design time.

The failure mode to avoid is using procedural generation to avoid doing the work of level design. Procedural systems still require the same careful thinking about enemy design, bullet behaviour, and difficulty tuning — they just apply that thinking at the descriptor level rather than the placement level. The generator is not a designer; it is a tool for a designer who has already made the important decisions.