Game Development

Godot UI, Save System, and Scene Transition Setup

Build a persistent Godot scene manager for UI, save data, and screen changes using Control nodes, autoloads, and separate scene containers. This guide shows how to keep menus, gameplay, and overlays organized without clearing the whole tree.

6 min read
The Godot editor scene tree is shown with GameController, World3D, World2D, GUI, and SplashScreenManager visible beside the Inspector panel.
The Godot editor shows a scene tree with GameController, World3D, World2D, and GUI nodes beside the Inspector panel. — footage: StayAtHomeDev

To manage UI, save data, and scene changes in Godot, create a root controller scene with separate 3D, 2D, and GUI containers, register it as an autoload, and write functions to swap in new scenes as needed. This keeps menus, gameplay, and overlays organized and makes it possible to manage persistent game state without clearing the whole scene tree.

What this scene controller does

This setup uses a single main scene as the persistent root of your project, containing three main child containers: one for 3D content, one for 2D content, and one for GUI elements. By loading, hiding, or removing scenes through the controller instead of deleting the entire scene tree, UI overlays and other stateful nodes remain in place across scene changes. You can switch individual layers (like the menu, game world, or overlays) independently, making save/load and transitions easier to reason about.

Create the game controller scene

steps

  1. Create a new scene and set a basic Node as the root. Name this root GameController.
  2. Attach a new script to the root node.
  3. Add three child nodes: a 3D node (name it World3D), a 2D node (name it World2D), and a Control node (name it GUI). These will act as scene containers for 3D, 2D, and UI layers.

Godot editor showing the Scene dock with GameController selected and child nodes World3D, World2D, GUI, and SplashScreenManager listed below.

The Godot editor shows a GameController scene tree with World3D, World2D, and GUI child nodes. — StayAtHomeDev

Register the controller as an autoload

prerequisites

  • Open Project Settings and find the Autoload tab.
  • Add the script you just attached to your GameController as a global singleton (autoload).
  • In a global script, store a reference variable for the game controller so it can be accessed project-wide.

Godot Project Settings window is open on the Autoload tab with a global script entry listed.

The Project Settings Autoload tab shows a global script added to the autoload list. — StayAtHomeDev

Store scene references for UI and gameplay

steps

  1. In your GameController script, add exported variables for the 3D, 2D, and GUI scene types. This lets you assign scenes from the Inspector.
  2. Set the export variables in the Inspector to your chosen initial scenes (such as a splash screen manager or main menu scene).
  3. Add variables to store the currently loaded scene for each of the three containers.

A code editor window displays exported variables for world_3d, world_2d, and gui along with current scene variables.

A code editor shows exported scene references for 3D, 2D, and GUI control scenes. — StayAtHomeDev

Write the screen change functions

When it is time to change which scene is in the GUI, 2D, or 3D layer, use specialized functions in your controller. These take care of hiding, deleting, or just removing (detaching) the previous scene as needed before adding the new one.

before after

Before

Old method (direct tree change):

get_tree().change_scene_to_packed(main_menu_scene)
After

With controller functions:

func change_gui_scene(scene_path: String, delete: bool, keep_running: bool):
    if current_gui_scene != null:
        if delete:
            current_gui_scene.free()
        elif keep_running:
            current_gui_scene.visible = false
        else:
            remove_child(current_gui_scene)
    var new_scene_resource = load(scene_path)
    var new_scene = new_scene_resource.instantiate()
    $GUI.add_child(new_scene)
    current_gui_scene = new_scene

# Repeat a similar function for change_2d_scene and change_3d_scene,
# adjusting container references as needed.

A dark-themed code editor window shows a function named change_gui_scene with parameters delete and keep_running.

A code editor displays the change_gui_scene function with delete and keep_running parameters. — StayAtHomeDev

Hook up the splash screen and main menu

steps

  1. In your splash screen manager or similar entry scene, look for where you switch to the next packed scene.
  2. Replace that code with a call to your controller's change function, passing the path to your main menu scene.

A code editor window shows a Godot script with an unhandled input function that calls get_tree().change_scene_to_packed(load_scene) when input is pressed.

A script in the GameController window changes the scene when input is pressed. — StayAtHomeDev

Also worth knowing

  • Reusing vs. recreating scenes: If you remove or hide scenes but still instantiate a new one every time, you will create new objects with fresh data, not reuse the old instance.
  • Controller assignment: The controller reference should be set in _init or checked before assignment to avoid overwriting an existing node.
  • Mouse event passthrough: The GUI container can block mouse clicks on Area2D nodes. Set its Mouse Filter to Ignore if you do not want this.
  • Restoring scenes: If you remove a scene from a container and want to show it again later, you need to keep its reference somewhere. Otherwise, the manager has no access to the old instance.
  • Cross-scene references: Exported references between objects in different scenes are cumbersome with this model; wire shared links through code or another global manager instead.
  • Remove targets: When removing scenes in the 2D or 3D switch functions, be sure you are affecting the correct container node.

FAQ

how to build a menu in Godot

Use a Control node as the root of your menu. Add Buttons and other UI elements as children. Then load the menu scene into the GUI container using your game controller's change function.

Godot save and load tutorial

Keep your save data in a global manager or autoload singleton so it can persist between scene changes. Save relevant data from loaded scenes before unloading them, and load data after re-instantiating scenes as needed.

Godot pause menu guide

A pause menu can be its own scene of Control nodes, loaded into the GUI container and shown or hidden as needed. You can pause the game and manage the visibility of the pause menu from your controller.

Godot scene transition tutorial

Use the change functions in your game controller to remove, hide, or free the current scene in a container and add a new scene. This enables smooth screen transitions without clearing the whole tree.

Godot settings menu with buttons

Create a settings menu as a separate Control scene with the necessary buttons. Load this menu into the GUI container when needed. Use buttons to change settings or call functions in your scripts.

Godot persistent game state

Store persistent game state in a singleton (autoloaded script or object), not in any specific gameplay or menu scene. This ensures your state survives as scenes load and unload independently in each container.


Official reference: Godot documentation.

Sources

Screenshots on this page are frames from the videos below, used to illustrate the steps they describe. All rights remain with their creators.

Checked against: