Scene Manager Systemautoload-root
// root-based screen & level controller · spawn-point aware · Godot 4 · GDScript
01 Overview
The Scene Manager is the root of the game and is never destroyed. It owns a single child slot called CurrentScene, where the active screen lives — the title screen, stage select, or a gameplay level. Switching screens means freeing the old child and adding a new one.
Because the manager persists across every screen, it is the one place that controls what is on-screen, where the player spawns, and (later) transitions and background loading.
change_scene_to_file() directly. Everything routes through SceneManager.go_to(path, spawn_name) so screen-switching logic stays in one place.
02 Scene Structure
Set the manager scene as the project's Main Scene. The whole game runs inside its CurrentScene slot.
Main (Node) # SceneManager.gd — root, never freed
└── CurrentScene (Node) # active screen added here as a child
└── # Title / StageSelect / Level_Town / ...
Each level scene is self-contained — it holds its own Player node and one or more Marker3D spawn points. Title and menu scenes simply have no player, so the spawn step is skipped automatically.
Level_Town (Node3D)
├── Player (CharacterBody3D) # in group "player"
├── from_castle (Marker3D) # entrance spawn point
├── from_select (Marker3D) # another entrance
└── # geometry, props, etc.
03 Switching Flow
A single call to go_to() runs this sequence:
If no spawn name is passed, the last two steps are skipped and the scene simply loads as placed in the editor.
04 Public API
go_to(path, spawn_name = "")
The only call you need. path is the res:// path to a .tscn; spawn_name is the optional name of a Marker3D in the new scene.
# Load a screen with no player (title, menus)
SceneManager.go_to("res://screens/title.tscn")
# Load a level and place the player at a named entrance
SceneManager.go_to("res://levels/town.tscn", "from_castle")
reload_current(spawn_name = "")
Reloads whatever scene is currently active — handy for a "restart level" action.
SceneManager.reload_current()
| Argument | Type | Meaning |
|---|---|---|
| path | String | res:// path of the scene to load |
| spawn_name | String | Marker3D name to place the player; empty = load as-is |
05 Spawn Points
After a level loads with a spawn name, the manager finds the player via the "player" group, finds the marker by name, and copies the marker's transform onto the player.
func _place_player_at_spawn(scene_root, spawn_name):
var players = get_tree().get_nodes_in_group("player")
if players.is_empty(): return # menu — no player, skip
var player = players[0]
var marker = scene_root.find_child(spawn_name, true, false)
if marker == null: return # no match, leave as placed
player.global_transform = marker.global_transform
Door integration
A door's job is just to call the manager with the destination and the entrance name the player should arrive at — mirroring the Door & Teleport system.
# inside a door's body_entered handler
SceneManager.go_to("res://levels/castle.tscn", "from_town")
06 Setup Checklist
- Create a
Main (Node)scene with one childCurrentScene (Node). - Attach
SceneManager.gdtoMain. - Set
Main.tscnas the project's Main Scene (Project Settings → Application → Run). - Set
FIRST_SCENEin the inspector to your title screen path. - Add each level's
Playernode to the groupplayer(Node → Groups tab). - Place
Marker3Dnodes in levels, named per entrance (e.g.from_castle). - Switch screens anywhere with
SceneManager.go_to(path, spawn_name).
Main is the root, reference it from any child via the scene tree, or register the manager as an autoload named SceneManager if you prefer a global handle. Keep one approach consistent.
07 To Do Later
Two upgrades are intentionally left as commented hooks in the code so the prototype stays simple now and you know exactly where to return.
Background / threaded loading
Instant load() is fine for prototyping but causes a brief freeze on large levels. The code marks the spot where ResourceLoader.load_threaded_request() and a progress loop should replace the instant load, so a loading screen can show.
Transition hooks
Two clearly-marked spots in go_to() — one before the swap (cover the screen) and one after (reveal) — are where the Shatter Transition system plugs in with await Transition.play_out() and await Transition.play_in().