• Godot Help
  • get_parent().queue_free() not working as intended

Hi, I´m trying to delete an scene through the pause menu to return to the main menu, but if I tried to return to the level, it keeps the older elements in a lower layer or something like that.

Even that, sometimes it tells me that the player is a null element...

This is the code of the Pause Menu:

extends CanvasLayer

var is_paused = false setget set_paused


func set_paused(value):
	is_paused = value
	get_tree().paused = is_paused
	visible = is_paused
	


func _on_PlayerHUD_pause_button():
	self.is_paused = !is_paused


func _unhandled_key_input(event):
	if event.is_action_pressed("pause_game"):
		if $SettingsMenu.visible == true:
			return
		else:
			self.is_paused = !is_paused


func _on_ResumeGameButton_pressed():
	Select1.play()
	self.is_paused = !is_paused



func _on_ExitGameButton_pressed():
	Select2.play()
	self.is_paused = !is_paused
	get_parent().queue_free()
	BackgroundMusic.stream = load("res://assets/sounds/music/loops/Menus_Music.mp3")
	BackgroundMusic.playing = true
	
	get_tree().change_scene("res://menus/main_menus/MainMenu.tscn")


func _on_OptionsButton_pressed():
	Select1.play()
	$SettingsMenu.show()

And this is from the main level, it is still under development

extends Node

export(PackedScene) var enemy_ship_scene

signal game_over
# signal time_added

var total_enemies
var score
var lives

func _ready():
	BackgroundMusic.stream = load("res://assets/sounds/music/loops/Game_Music.mp3")
	BackgroundMusic.playing = true
	lives = GlobalVariables.difficulty_selected
	

func _on_PlayerHUD_start_game():
	score = 0
	total_enemies = 0
	
	$Player.start($PlayerSpawnLocation.position)
	$ScoreUpdateTimer.start()
	$GameTimerDuration.start()
	$PlayerHUD.update_lives(lives)
	$PlayerHUD.update_score(score)
	$PlayerHUD.update_timer_duration(floor($GameTimerDuration.time_left))
	$PlayerHUD.update_ammo($Player.ammo)
	$EnemyShipTimer.start()


func _on_ScoreUpdateTimer_timeout():
	score += 1
	$PlayerHUD.update_score(score)
	$PlayerHUD.update_timer_duration(floor($GameTimerDuration.time_left))


func _input(event):
	if event.is_action_pressed("shoot"):
		if $Player.ammo > 0:
			$PlayerHUD.update_ammo($Player.ammo -1)
		else:
			return


func _on_Player_reload_complete():
	$PlayerHUD.update_ammo($Player.ammo)

The pause menu is instantiated in the level.

  • xyz replied to this.
  • get_tree().root.add_child(bullet)

    ^ That adds bullets to the root, not to the level. So they won't be deleted when level is deleted

    what's the parent of the pause menu? if I'm reading that code right, you're deleting whatever it's attached to.

      Mario999 Look at remote scene three while running to see what nodes are created/deleted. May help you with debugging.

        xyz

        Hmm, some bullets never disappear, which is strange because most of them do, and you can see how you just exited the main menu, the elements disappear little by little, it gives the feeling that the level is still loaded, or at least the elements that were already present.

        I´m using a VisibilityNotifier2D on the projectiles for when they should delete.

        func _on_VisibilityNotifier2D_screen_exited():
        	queue_free()

        This Picture was made with 2 resets for the level, so its stacking bullets...

        But putting that aside, is that behavior normal? Maybe I should make a way for the user or the program to wait until every element is deleted perhaps? Or is the queue free not working correctly?

        samuraidan

        As seen in the last image, the "MainLevel" and the rest of the elements should be eliminated, at least thats the plan.

        • xyz replied to this.

          Mario999 Remote tree is not updated immediately so it's not fully realtime. The important thing to check is if it shows the desired state a few seconds after you trigger the scene replacement.

          There's no way for queue_free() to not work.

            xyz

            If I go back right after exiting the level, it's when there are still semi-transparent bullets in the background... And they can be seen on the remote, so the solution will be to somehow slow down the player's exit to the main menu, I guess

            • xyz replied to this.

              Mario999 If bullets are child of the level scene, they should be automatically removes. Otherwise you'll need to manually queue_free them as well.

                xyz

                If for some reason get_parent.queue_free() isn't working, I could try to send a signal that receives the level itself and maybe remove them that way? Or would it not work?

                The enemies are generated with this: (the level code is incomplete)

                func _on_EnemyShipTimer_timeout():
                	if total_enemies < 5:
                		total_enemies += 1
                		var enemy = enemy_ship_scene.instance()
                		var enemy_spawn_location = $MobPath/MobSpawnLocation
                		enemy_spawn_location.offset = randi()
                		var direction = enemy_spawn_location.rotation + PI / 2
                		enemy.position = enemy_spawn_location.position
                		direction += rand_range(-PI / 4, PI / 4)
                		enemy.rotation = direction
                		
                		add_child(enemy)
                	else:
                		return

                And the bullets are generated from the enemy_ship script with this:

                func _on_ShootTimer_timeout():
                	var random_number = randi() % 2 + 1
                	var bullet
                	for s in rotater.get_children():
                		if random_number == 2:
                			bullet = bullet_t1_scene.instance()
                		else:
                			bullet = bullet_t2_scene.instance()
                		get_tree().root.add_child(bullet)
                		bullet.position = s.global_position
                		bullet.rotation = s.global_rotation
                • xyz replied to this.

                  Mario999 Looks like bullets are direct children of root, so freeing the level will not remove them. They should probably be parented to level

                    xyz

                    const bullet_t1_scene = preload("res://game_elements/enemies/EnemyBulletT1.tscn")

                    Now that you mention it, it looks like that, the player for example does appear within the level node

                    I have a test level with a static enemy and the same thing also happens, so the problem is in how the bullets are generated, since the enemies also appear in the level node.

                    Writing the message I just realized the error, just in the last message you see this: get_tree().root.add_child(bullet)

                    It looks like that is right?

                    • xyz replied to this.
                      get_tree().root.add_child(bullet)

                      ^ That adds bullets to the root, not to the level. So they won't be deleted when level is deleted

                      Note that queue_free() is a request, and it will be removed at some later frame if it is safe to do so. I'm not sure which cases you have to look out for, but there could be times when it is not removed immediately or at all.

                      In general it is good to imagine the scene graph like a live action play or movie. Where you have a set, actors, props, etc. and design the hierarchy according to how it would work in real life.

                      For example, if you shot a bullet from a gun, the bullet is not a child of the gun. It is a separate object on the same layer as the gun (most likely the main level).

                        cybereality

                        In the end it was that, by changing the way to generate bullets in the enemy ship, now they are generated in the level.

                        I changed the original line with this:

                        get_parent().add_child(bullet)

                        At the end the get_parent_queue_free() was working 😅

                        Thanks everyone through!

                        cybereality

                        Currently it is already solved and it eliminates everything directly, at least in this case, but I will keep it in mind for the future, thanks!