Unable to Queue Free Certain Object

I’ve been working on a game where there’s a large enemy that summons rockets that hunt down the player. The problem is the player can’t kill them when the code to queue_free() the node clearly runs and every relevant detail is the same as a enemy that can be freed.

This is the rocket that can’t be freed:

extends CharacterBody3D

var speed = 10
var falldistance = 20
func _process(_delta: float) -> void:
	var direction := gamemanager.player.global_position - global_position
	var new_basis := Basis.looking_at(direction)
	basis = basis.slerp(new_basis, 0.08)
	velocity = -transform.basis.z * speed
	hurt()
	move_and_slide()
func _on_detect_ground_body_entered(_body: Node3D) -> void:
	queue_free()
func get_fall_distance():
	return falldistance
func hurt():
	if gamemanager.player.p_range.get_collider()==self && gamemanager.player.animation_player.current_animation=="slice":
		if gamemanager.player.airtime>get_fall_distance():
			gamemanager.player.velocity.y = gamemanager.player.slamLaunchStr
			queue_free()


func _on_timer_timeout() -> void:
	self.queue_free()

And this is the enemy that spawns it and can be freed

extends CharacterBody3D

var speed = 4
@onready var player = gamemanager.player
enum states{ target, attack, sleep, rockets}
var CurrentState: states
var playerPosxy:Vector3
var falldistance = 40
var target: Vector3
var gravity = 1
var cooldown = false
var rockets = preload("res://scenes/projectiles/enemy/rocket.tscn")
var particle = preload("res://scenes/enemies/particle/golem_attack_particle.tscn")
@onready var startingPos = position
func _ready() -> void:
	CurrentState = states.target
func _process(_delta: float) -> void:
	playerPosxy = Vector3(player.position.x, position.y, player.position.z)
	match CurrentState:
		states.target:
			target = playerPosxy
			targeting()
		states.attack:
			attack()
		states.rockets:
			rocket()
	grav()
	hurt()
	move_and_slide()
func attack():
	if !cooldown:
		$"attack range/CollisionShape3D".disabled = false
		$cooldown.start()
		cooldown = true
		await  get_tree().create_timer(0.2).timeout
		$"attack range/CollisionShape3D".disabled = true
	CurrentState = states.target
func rocket():
	for i in range(4):
		var rocketsInstatiate: Node3D = rockets.instantiate()
		rocketsInstatiate.position = position
		get_parent().get_parent().add_child(rocketsInstatiate)
	CurrentState = states.target
func grav():
	velocity.y -= gravity
func get_fall_distance():
	return falldistance

func targeting():
	look_at(target)
	var targetVec: Vector3 = target - position
	velocity = lerp(velocity, targetVec.normalized() * speed, 0.05)
	if position.distance_to(player.position) < 4:
		CurrentState = states.attack
func hurt():
	if player.p_range.get_collider() == self && player.animation_player.current_animation == "slice":
		if player.airtime>get_fall_distance():
			player.velocity.y = player.slamLaunchStr
			die()

func die():
	queue_free()

func _on_timer_timeout() -> void:
	if position.distance_to(player.position) > 20 && CurrentState == states.target:
		CurrentState = states.rockets
	$Timer.start(randf_range(2, 5))


func _on_cooldown_timeout() -> void:
	cooldown = false


func _on_attack_range_body_entered(_body: Node3D) -> void:
	if !player.iFrames:
		var kbVec : Vector3 = (player.position - position).normalized()
		player.velocity += kbVec * 25
		player.velocity.y += 20
		var particleInst: Node3D = particle.instantiate()
		particleInst.global_transform = $"attack range/CollisionShape3D".global_transform
		get_parent().add_child(particleInst)

The collision layers that matter towards the player raycast are the same and in both instances the launching the player does run, the rocket just can’t be freed in the rocket’s case. With the rocket it also doesn’t work if I have it queue free a collision shape in there or hide it’s mesh

It’s very likely that your code just don’t reach to any queue_free() in your code.

Try to trace your code with breakpoints if it actually calls any queue_free() in your code.

1 Like

Putting a breakpoint on the queue free the breakpoint does stop godot.

Have you tried testing queue_free() by putting it into _process directly to make sure the issue is with queue_free() itself?

If queue_free() isn’t working, sometimes you can fix it by using call_deferred(“queue_free”). This will make sure the function is called when the game is next able to call it, instead of trying to call queue_free() right away. call_deferred() can introduce a delay to a function call, but it’s usually not noticeable.

Putting it in process does instantly delete it but call deferred in the if statement didn’t work. I’d assumed process would work because it also works when in timeout. I just don’t know why it’s having issues in that if statement.

My recommendation may sound bad but uh… perhaps in that if-statement try self.queue_free()?

I’ve checked my previous projects and noticed that I’ve always used self when I needed to queue_free() the node using the script attached to it. Perhaps that’s the issue?

How did you determine that queue_free() is “not working”?

Are there any errors reported by the engine?

I’ve tried both self.queue_free() and not working is it just doesn’t get freed, there no error.

Yeah, but by what means you determined it doesn’t get freed? Do you monitor the remote scene tree state at runtime or look at node count in the debugger?

It’d be quite impossible for the node to keep existing if queue_free() was indeed called on it.

Can you print the node path just before the queue_free() call, to determine it’s actually called on the node you expect?

1 Like

Can you walk us through your logical reasoning of how you know that queue_free is being called and how you know it is not freeing the object?

In put a breakpoint at the launch to see what the scene tree was then stepped it to a line after the queue free and the node was still in the scene tree. What’s effectively happening is the script runs into the if statement where it checks if the player is colliding and launches the player as expected, yet the object is still there despite queue_free being called in that if statement.

So it’s important for you to understand that this is not a valid test.

queue_free() is not run immediately. Instead it is queued until the end of the physics frame, and run after the current process and physics ticks have run. (Hence the queue part of the function name.)

:0 I knew about the queue part of it but forgot about it. When I made it reach a breakpoint after the queue free and a timeout await it didn’t stop or print the line it was supposed to. Weirdly the things in the script like targeting the player still run after it reaches the queue free and until it’s destroyed by an autostarted timer.

1 Like

You are aware that you instantiate 4 rockets at once at exactly the same position?

1 Like

I laughed so hard as I read that because as a matter of fact, no I didn’t. Thank you so much. I meant to add an offset to make it a burst shot but never did.