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

Game Programming · June 30, 2026

Scene and State Management in Shmups: Title, Gameplay, Pause, and Game Over

The scene flow of a finished shmup involves more states than most developers plan for at the start: title with attract mode demo, character or ship select, stage intro, gameplay, boss warning, mid-game pause, death animation, continue countdown, game over with high score entry, ending cutscene, and credits. These states connect in a graph with specific valid transitions, and implementing that graph poorly is one of the most common sources of hard-to-reproduce bugs in indie shmups.

Mapping the state graph early

Before writing a single scene transition, draw the complete state graph on paper or a whiteboard. Each node is a scene or mode; each directed edge is a valid transition trigger. The most important step is identifying which transitions can be initiated by the player and which are driven by game events, because these have different input-handling requirements.

Player-initiated transitions (pressing Start on the title screen, pressing Pause during gameplay) need input debouncing and must be blocked during transition animations to prevent double-triggering. Event-driven transitions (death animation completing, continue timer expiring) fire from game logic and must not be blocked by input state.

Common transitions that get overlooked at the planning stage:

Scene vs overlay: when not to change scenes

Not every game mode requires a full scene change. The pause menu, for example, should almost never unload and reload the gameplay scene. Instead, pause is a state overlaid on an existing scene: the game world freezes (or runs at reduced time scale), a pause overlay UI is instantiated on top, and unpausing removes the overlay and restores time scale. Implementing pause as a full scene transition requires serialising and restoring the entire game state on every pause/unpause, which is expensive and error-prone.

The rule of thumb: if the underlying game world needs to be visible behind the new mode, implement it as an overlay, not a scene change. The death screen, boss health bar, and continue countdown all qualify as overlays. The title screen, character select, and credits are genuinely different scenes that should not retain gameplay state.

Resource cleanup and the audio leak trap

The most common bug in shmup scene transitions is audio that survives a scene change. A music track that was started in the gameplay scene continues playing through a different audio player object in the title screen scene, running underneath the title music and producing a layered mess. This happens when audio players are not parented to scene nodes and are created as persistent singletons.

The correct approach is explicit audio lifecycle management during every scene transition: before unloading a scene, stop and free all audio players that belong to it. The audio bus architecture described in audio and music integration helps here because all combat audio is bus-routed and can be silenced by stopping the bus rather than hunting individual players.

Similar leaks occur with timers, tween animations, and coroutines. Any time-based process started in a scene should be explicitly cancelled on scene exit. The cleanest implementation registers all active timers and tweens with the scene on creation and cancels them all in the scene's exit handler rather than tracking them individually across the codebase.

Pause implementation details

What should the pause state freeze? The answer depends on whether you want visual activity to continue during pause (animated backgrounds, particle systems winding down) or a complete halt. The simplest implementation freezes the entire game time scale to zero, which stops everything: physics, animations, particle emission, bullet movement. This is correct for most shmups and requires no special handling of individual systems.

Edge cases that need explicit handling even with a frozen time scale:

The continue countdown

The continue countdown (typically 10 seconds, counting down while the player decides whether to continue) is a simple but frequently broken system. Common failures:

All three of these failures share the same root cause: the countdown timer is not owned by the state machine and is not destroyed when the state it belongs to exits. Ensuring every timer is scoped to its state and destroyed on state exit eliminates this entire class of bug.

Testing the state graph

Automated testing of scene transitions is possible but requires effort; manual testing is practical and important. The key test cases are the boundary conditions: transitions that happen at unusual times, such as the player dying at the exact frame the boss dies, or pressing pause during the death animation, or reaching game-over during the boss intro cutscene. These edge cases expose state machine violations where two states are simultaneously active or a transition fires during a period when transitions should be locked.

Building a debug overlay that always shows the current game state makes these edge cases much easier to catch during development and saves significant debugging time later.