Queue_free(), unnatural behavior

Godot Version

4.2

Question

I don’t think this is a bug, but it’s really weird and feel unnatural.


Whenever I delete an element with this code, the total is not being reflected immediately.

func getTotal(sizing_details: VBoxContainer, mockup_details: VBoxContainer, material_details: VBoxContainer) -> void:
	var total_regex := RegEx.new()
	var total_amount : float = 0
	total_regex.compile("₱(\\d+)$")
	var sizing_parent: Array = sizing_details.get_children()
	var mockup_parent: Array = mockup_details.get_children()
	var material_parent: Array = material_details.get_children()
	
	if sizing_parent.size() > 0:		
		for wall: Variant in sizing_parent:
			total_amount += total_regex.search(wall.get_child(0).text).get_string(1).to_float()			
	if mockup_parent.size() > 0:		
		for wall: Variant in mockup_parent:
			total_amount += total_regex.search(wall.get_child(0).text).get_string(1).to_float()
	if material_parent.size() > 0:		
		for wall: Variant in material_parent:
			total_amount += total_regex.search(wall.get_child(0).text).get_string(1).to_float()	
	
	total.text = "Total: ₱" + str(total_amount)			
		
func _on_delete_wall_pressed(hbox: HBoxContainer) -> void :
	hbox.queue_free()
	getTotal(sizing_details, mockup_details, material_details)

I need to delete one more element for a change to happen, but it’s not the most up-to-date, the value is one step behind.

I think the reason why is because my getTotal can still reference a node that should be deleted with queue_free(). I think I understand the concept behind queue_free(), it schedules the deletion of the node. But using free(), which is supposed to be the immediate deletion alternative, doesn’t work and crashes my app.

And a get around with this is issue is by using this:

func getTotal(sizing_details: VBoxContainer, mockup_details: VBoxContainer, material_details: VBoxContainer) -> void:
	var total_regex := RegEx.new()
	var total_amount : float = 0
	total_regex.compile("₱(\\d+)$")
	var sizing_parent: Array = sizing_details.get_children()
	var mockup_parent: Array = mockup_details.get_children()
	var material_parent: Array = material_details.get_children()
	
	if sizing_parent.size() > 0:		
		for wall: Variant in sizing_parent:
			if !wall.is_queued_for_deletion():
				total_amount += total_regex.search(wall.get_child(0).text).get_string(1).to_float()			
	if mockup_parent.size() > 0:		
		for wall: Variant in mockup_parent:
			if !wall.is_queued_for_deletion():
				total_amount += total_regex.search(wall.get_child(0).text).get_string(1).to_float()
	if material_parent.size() > 0:		
		for wall: Variant in material_parent:
			if !wall.is_queued_for_deletion():
				total_amount += total_regex.search(wall.get_child(0).text).get_string(1).to_float()	
	
	total.text = "Total: ₱" + str(total_amount)			
		
func _on_delete_wall_pressed(hbox: HBoxContainer) -> void :
	hbox.queue_free()
	getTotal(sizing_details, mockup_details, material_details)

Which feels weird and unnatural because you have to keep in mind that a node that you want to be deleted, can still be accessed.

I made this topic in order for people like me who’s new to the engine and scratching their head because of this!

Exactly. queue_free doesn’t free the node immediately, it marks it for deletion and deletes it at the end of the frame.

However, you can use is_queued_for_deletion() to only make your total sum with the nodes that are not queued for deletion.

1 Like

This “end of the frame” is hard to wrap around my head, what is meant by this? Does this mean that the former code will work if I attach a timer that will slowdown the execution of the getTotal function?

I wouldn’t complicate it even more with a timer. Your code (adding is_queued_for_deletion) looks fine. Is it not working?

Copied from the documentation I just shared to you:

Queues this node to be deleted at the end of the current frame. When deleted, all of its children are deleted as well, and all references to the node and its children become invalid.

Unlike with Object.free, the node is not deleted instantly, and it can still be accessed before deletion. It is also safe to call queue_free multiple times. Use Object.is_queued_for_deletion to check if the node will be deleted at the end of the frame.

Note: The node will only be freed after all other deferred calls are finished. Using this method is not always the same as calling Object.free through Object.call_deferred.

1 Like