How do I add and remove scenes as child nodes of another scene during runtime?

Godot Version

4.7 Beta 2

Question

Ask your question here! Try to give as many details as possible.

Hello! I’m new to Godot and most programming in general outside of the basics of syntax, so sorry if I say things that don’t make sense. I have been working on making a game for a few weeks now, but have a bit of a problem. I have been using the below code to switch scenes (directly taken from the Godot documentation):

extends Node

var current_scene = null

func _ready():
	var root = get_tree().root
	# Using a negative index counts from the end, so this gets the last child node of `root`.
	current_scene = root.get_child(-1)

func goto_scene(path):
	# This function will usually be called from a signal callback,
	# or some other function in the current scene.
	# Deleting the current scene at this point is
	# a bad idea, because it may still be executing code.
	# This will result in a crash or unexpected behavior.

	# The solution is to defer the load to a later time, when
	# we can be sure that no code from the current scene is running:

	_deferred_goto_scene.call_deferred(path)


func _deferred_goto_scene(path):
	# It is now safe to remove the current scene.
	current_scene.free()

	# Load the new scene.
	var s = ResourceLoader.load(path)

	# Instance the new scene.
	current_scene = s.instantiate()

	# Add it to the active scene, as child of root.
	get_tree().root.add_child(current_scene)

	# Optionally, to make it compatible with the SceneTree.change_scene_to_file() API.
	get_tree().current_scene = current_scene

but my problem is that it replaces the entire scene, while what I want is for it to replace the Node2D here:

image

I want a few things to always be in the scene tree unless I explicitly tell them to leave, so I was trying to get it so that I have a global script that I can call that will take two main inputs (the node/scene I want to replace and the scene I want to pull in to replace it) anywhere I want. My problem is I cannot actually find documentation about how to do what I want, probably because I don’t know what to actually look for. Whenever I find guides on how to do this they’re showing me how to replace the entire scene tree like in that documentation, but I do not want my GameController or its child nodes gone. It should be able to load everything the game uses other than autoloads inside of it but without them always being there and eating up memory. I don’t know how to achieve this and I cannot figure out what to even look up. Could anyone help me out and maybe show me if there’s documentation for how to do exactly this that I’m just not seeing? Thanks!

(And if anyone is wondering why I’m using 4.7, it’s because apparently there were issues with 2D Skeletons in 4.6 and my game will probably rely on 2D Skeletons since I want swap-able equipment without having to redo tons of animations over and over, that’s the only reason I’m on 4.7)

1 Like

Root is the root of the tree. GameController in your case.

What you want to replace is the child of a branch. You need to put it where it is in the scene tree.

# Drag from the node to the editor to get the actual path
@onready var scene_holder = "$SceneHolder"

scene_holder.add_child(current_scene)
1 Like

Thank you so much! Though I am a little confused, I thought Root is the root of the entire program, one level above the scene?

I’d suggest you not using root, as it’s harder than just making Main scene where you’ll load other scenes like levels, player, UI ect.

1 Like

I think that’s what I’m trying to do, I want total control over what I’m grabbing rather than relying on that root.get_child(-1). In my head I’d prefer just being able to input any path and replace any node I point it at.

Root is overall unintuitive, cuz it also stores singletons, + requires a lot of playing with, think smart, not hard - make main scene, add most important components to it, and use managers to call rest of functions, e.g you can connect start game function to spawning levels and then connect player’s death function to turning back to menu

1 Like

You aren’t wrong. I was being sloppy with my wording.

The screenshot you posted can also be considered a tree. The root of that tree is GameController.

You should avoid changing the root of any scene.

1 Like

Yes, I’m trying to change Main2D, not the root of the tree (GameController). Sorry if I’m misunderstanding. I want GameController and SceneHolder to always exist and never be removed or change, only Main2D should be replaced.

You aren’t misunderstanding.

My first post shows what to do - make Main2D a child of SceneHolder.

1 Like