Using queue_free() on list of nodes doesn't delete the node

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By zachstarnes

I am trying to create a save system following the docs for godot 4. I have the loop that searches for all the nodes of a particular group and calls queue_free() on them. However, once the loop exits the node still exists.

This is causing an issue with the save system because later in the function, it adds a new node with the save data and it’s adding it with the wrong name. Any help here would be great. Here is the code and the output when I added some print statements for debugging.

var save_nodes = get_tree().get_nodes_in_group("Persist")
for node in save_nodes:
	node.queue_free()
	print(get_node(node.get_path()))
	
print(get_tree().get_nodes_in_group("Persist"))

this outputs the following, when it should be null and an empty array

ScoreLabel:<Label#51355060533>
[ScoreLabel:<Label#51355060533>]
:bust_in_silhouette: Reply From: jgodfrey

As documented, queue_free() queues a node for deletion at the end of the current frame. Since all of your posted code will execute in the same frame, the nodes won’t yet have been deleted when that final print statement is called.

okay I see, so I figured out that using node.get_parent().remove_child(node) works and deletes it right away. Any issues that you see in doing this instead?

zachstarnes | 2023-05-02 22:35

If you’re actually trying to DELETE the node, note that remove_child() doesn’t do that.

Based on the fact that you say it’s working for case, I can only assume that a child that’s been removed from the tree must no longer be returned by the get_nodes_in_group() call…

jgodfrey | 2023-05-02 22:44

Okay I see. Then is there a way that I can delete the node right then and there on that line? It needs to be gone by the time the function gets to the bottom for when it adds a new one with the saved data.

This is right from the docs on godot 4 for saving - does the example need to be updated since this isn’t working?

zachstarnes | 2023-05-02 23:15

What does your full (relevant) code here look like?

jgodfrey | 2023-05-02 23:27

It’s identical to the code in this post, Saving games — Godot Engine (stable) documentation in English - you will see the above lines of code (minus the debug print statements) in the load game method on that page. Figured it was easier to use the link since the code is the same. The only thing I changed is the save method’s JSON object to match the attributes I wanted to save

zachstarnes | 2023-05-02 23:37

In the OP, you mention this:

it adds a new node with the save data and it’s adding it with the wrong name

Where exactly does it go wrong?

jgodfrey | 2023-05-02 23:44

At the bottom of the same function that does the queue_free(), where it does

var new_object = load(node_data["filename"]).instantiate()
get_node(node_data["parent"]).add_child(new_object)

When it adds the child, the first one isn’t removed yet - so It adds it with a @2 at the end of the name so when other scenes try to reference it without the @2 it fails.

zachstarnes | 2023-05-02 23:47

Ah, got it. So, the real problem happens later based on the fact that you’re referencing the node names elsewhere.

I guess that’s a reason for this comment in the code:

# We need to revert the game state so we're not cloning objects
# during loading. This will vary wildly depending on the needs of a
# project, so take care with this step.

While I’m not sure it’s the best way, I guess you could do:

remove_child(node)
queue_free(node)

Based on your earlier findings, that should allow the names to work as expected and free the nodes at the end of the frame…

jgodfrey | 2023-05-02 23:59

Yep, that worked - thank you!

zachstarnes | 2023-05-03 00:00