Is this a valid solution or is it a workaround?

Godot Version

4.5

Question

Since the load function doesn’t have an instantiate method in Godot 4.5, the following solution worked for me. Am I correct in understanding that the free() function doesn’t free the entire sub-scene, but only its root node, and that I’m simply adding the object to the current window?

extends Node

# Called when the node enters the scene tree for the first time.

func _ready() → void:

pass # Replace with function body.



#var scene01 = get_tree().change_scene_to_file("res://node_3d.tscn")

var scene01 = load("res://node_3d.tscn")

var pasce = PackedScene.new()

pasce.pack($Node3Dscene01)

#pasce.pack(scene01.get_node)

#var tscene = SceneTree.new()



var dfdsfsd = get_node($Node3Dscene01.get_path())

var dfgmkfmfdl = dfdsfsd.get_parent()

print(dfdsfsd.get_parent())

print(dfdsfsd.get_index())



#$Node3D.hide()

$Node3Dscene01.free()



print(dfdsfsd)



#pasce.cancel_free()

#scene01.cancel_free()



pasce.instantiate()

#get_tree().change_scene_to_packed(scene01)

#get_tree().change_scene_to_packed(pasce)

#get_tree().change_scene_to_file("res://main.tscn")

	#var scene01 = preload("res://node_3d.gd")

# dfgmkfmfdl.add_child(dfdsfsd)

get_tree().root.add_child(dfdsfsd)

print(get_tree().root)



#add_child()

#get_tree().change_scene_to_file("res://node_3_dscene_02.tscn")

# Called every frame. ‘delta’ is the elapsed time since the previous frame.

func _process(delta: float) → void:

pass

What exactly are you trying to achieve?

2 Likes

“Unloading a scene from memory and loading it at the right moment, I was trying to write a level loader.”

Or point me to a ready piece of code on how to programmatically load a scene into the current SceneTree and display it without using get_tree().change_scene_to_path.

There you go:

const TEST = preload("res://test.tscn")

func _ready() -> void:
	var test = TEST.instantiate()
	get_tree().root.add_child.call_deferred(test)

The “call_deferred” part might not be needed, depends on when you are setting this up. I had to add it, because I did it in the Autoload.

PS: Your piece of code is super confusing and I have no idea what purpose it serves. But if you just want to load a scene into the SceneTree, then here you go.

5 Likes

Here’s my relevant code from a state machine:

var level_path: String #actual file path that is passed in via signal
var current_level: Node

#This function doesn't exist  in my code. There it's in another state.
func load_level() -> void
	ResourceLoader.load_threaded_request(level_path)


func _process(delta: float) -> void:
	var status = ResourceLoader.load_threaded_get_status(level_path)
	if status == ResourceLoader.THREAD_LOAD_LOADED:
		set_process(false)
		await get_tree().create_timer(0.5).timeout
		_start_level()

func _start_level() -> void:
	var scene = ResourceLoader.load_threaded_get(level_path)
	var new_level = scene.instantiate()
	add_child(new_level)
	if new_level != current_level and current_level != null:
		current_level.queue_free()
	current_level = new_level

If you want to see the full code check out my Game Template. It handles a lot of pesky things like that for me. Basically, in Game I define the starting level, and from then on the level the character is in is just saved where ever they go. It also pops up a loading bar every time a level is loaded. There are examples (and instructions) for 2D and 3D games and it’s a plugin so you can play with it. It’s open source so feel free to borrow what works and leave the rest. I am actively updating it every week or so.

Though TBH @wchc 's response is much simpler.

1 Like

My code is an attempt to understand the engine using llm. It feels like they haven’t been trained on GitHub or forums.

Don’t use LLMs to learn Godot. They can’t tell the difference between different versions, and they will make up fake function names.

It does explain why your code looks so poor.

4 Likes
extends Node

var scene_path = "res://node_3_dscene_01.tscn"
var packed_scene : PackedScene

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	pass # Replace with function body.
	packed_scene = load(scene_path)
	var scene_instance = packed_scene.instantiate()
	$".".add_child(scene_instance)
	print($".".get_children()[1])
	var test_child = $".".get_child(1)
	#test_child.free()
	
	scene_path = "res://node_3_dscene_02.tscn"
	packed_scene = load(scene_path)
	scene_instance = packed_scene.instantiate()
	$".".add_child(scene_instance)
	
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass

Valid code for me

You have a number of unnecessary lines in your code. As well as unnecessary references. This is the same code, much more compact:

extends Node

const PACKED_SCENE = preload("res://node_3_dscene_01.tscn")
const PACKED_SCENE_2 = preload("res://node_3_dscene_02.tscn")

func _ready() -> void:
	var scene_instance = PACKED_SCENE.instantiate()
	add_child(scene_instance)
	var test_child = get_child(1)
	print(test_child)
	#test_child.free()
	
	scene_instance = PACKED_SCENE_2.instantiate()
	add_child(scene_instance)
2 Likes

“Yes, that’s exactly what I needed. This is just a test version of the code for now. I really liked your state machine plugin, and I’ll take more time to understand it better later.”

1 Like

I just didn’t understand why Godot doesn’t have method return a node from the add_child() function, and whether the game might behave unpredictably when using get_child() if there are many instances

What would you expect it to return? To add a node, you have to have a reference to the node you’re adding, and you’re running the function on the node that it’s being added to.

That function requires an index and retrieves the nodes in order starting at 0. If you want a specific node, use a loop.

var label: Label
for node in get_children():
	if node is Label:
		label = node
		break
2 Likes