Scene flow: title, level, game over, win, restart
A small Unity 6 example of the loop most first games need: Title → Level → Game Over or Win → Retry or Menu. It goes with the wiki page unity-scene-management.
| Script | Job |
|---|---|
SceneFlow.cs | Public methods for UI buttons: StartGame, LoadMenu, Restart, LoadNextLevel, RetryLastLevel, LoadScene(string), Quit. Every load resets Time.timeScale to 1. |
GameOverTrigger.cs | A 2D trigger that ends the level as a win or a loss after a short delay. |
GameSession.cs | Persistent object (DontDestroyOnLoad, duplicate guard) that remembers the last level and subscribes to SceneManager.sceneLoaded. |
PauseMenu.cs | Escape key or a button toggles Time.timeScale between 0 and 1 and shows a pause panel. |
LoadingScreen.cs | LoadSceneAsync with a progress bar and allowSceneActivation. |
The scripts are compile-checked against the Unity 6000.3 assemblies (UnityEngine, uGUI, Input System) but have not yet been play-tested in the Editor; report anything that misbehaves.
Requirements
- Unity 6 (6000.x), 2D (URP) template or any 2D project.
- Input System package (installed and active by default in new Unity 6 projects).
PauseMenureadsKeyboard.current; in a project that uses only the old Input Manager, delete theUpdatemethod and use a Pause button instead. - uGUI (Canvas, Button, Slider), included in the default templates.
Scene setup (about 20 minutes)
- Create four scenes in
Assets/Scenes: Title, Level1, GameOver, Win. - Open File > Build Profiles. With each scene open in turn, press Add Open Scenes in the Scene List (or drag the four scene assets into the list). Put Title at the top so it is build index 0, then Level1, GameOver, Win.
- Title scene:
- Empty GameObject
SceneFlowwithSceneFlow.cs(defaults: menuTitle, first levelLevel1). - Empty root GameObject
GameSessionwithGameSession.cs. Keep it at the root of the Hierarchy, not inside another object. - Canvas with a Play button and a Quit button. Play: On Click () →
SceneFlow.StartGame. Quit: On Click () →SceneFlow.Quit. - Optional loading screen: add
LoadingScreen.csto the Canvas, make a full-screen Panel with a Slider (Min 0, Max 1, Interactable off), assign both, and switch Play’s On Click () toLoadingScreen.LoadWithProgresswithLevel1typed in the box.
- Empty GameObject
- Level1 scene:
- A player sprite tagged Player with a
Rigidbody2Dand aCollider2D(any movement script, for example from unity-input). - A hazard sprite with a
BoxCollider2D(Is Trigger ticked) andGameOverTrigger.cs, Outcome Lose. - A goal sprite with a trigger collider and
GameOverTrigger.cs, Outcome Win. - A
SceneFlowobject; drag it into each trigger’s Scene Flow slot. - A Canvas with a hidden
PausePanelholding Resume (PauseMenu.TogglePause), Restart (SceneFlow.Restart) and Menu (SceneFlow.LoadMenu). PutPauseMenu.cson the Canvas and assign the panel.
- A player sprite tagged Player with a
- GameOver and Win scenes: a
SceneFlowobject and a Canvas with Retry (SceneFlow.RetryLastLevel) and Menu (SceneFlow.LoadMenu).SceneFlow.LoadNextLevelbelongs on a button inside a level (for example a level-complete panel), because it adds 1 to the active scene’s build index; from the Win scene it would load the scene after Win. - Open Title and press Play.
What to try
- Lose, press Retry, and watch the Console:
GameSessionlogs each scene load and the attempt count keeps rising because it survived the loads. - Return to the Title scene. A second
GameSessionis loaded with the scene and destroys itself inAwake, so the Hierarchy’s DontDestroyOnLoad section still shows exactly one. - Frozen restart bug: comment out
Time.timeScale = 1f;inSceneFlow.LoadByIndex, open Level1 directly (so noGameSessionexists), press Play, pause with Escape and press Restart. The level reloads but nothing moves, becauseTime.timeScaleis a global setting that loading a scene does not reset. Put the line back. - Remove the duplicate guard from
GameSession.Awake, then go Title → Level1 → Menu twice. Count theGameSessionobjects under DontDestroyOnLoad and the duplicated log lines. - Set the hazard’s Delay Seconds to 2, walk into it and press Escape straight away. Check whether the Game Over scene waits until you resume. It should, because
Invokeis widely reported to count scaled time; the Scripting API page does not state this, so note what you see. - Untick Level1 in the Scene List and press Play from Title. The load fails with a Console error, which is the most common reason a scene “will not load”.