2
\$\begingroup\$

Consider this setup:

* KinematicBody2D (Player)
|- Sprite (icon.png)
|- VisibilityNotifier2D

With Player.gd:

class_name Player extends KinematicBody2D
signal unvisible

onready var visibility_notifier :VisibilityNotifier2D = $VisibilityNotifier2D

func _ready() -> void:
    visibility_notifier.connect("screen_exited", self, "__on_screen_exited")

func __on_screen_exited() -> void:
    emit_signal("unvisible")

Now I have a template Level.gd:

* Node2D (Level)
|- Player
extends Node2D

onready var player :Player = $Player

export var debug_no_enemy  = false
export var debug_no_player = true

func _ready() -> void:
    player.connect("unvisible", self, "_reset_level")
    if debug_no_player:
        player.queue_free()

func _reset_level() -> void:
    print("reset")
    get_tree().reload_current_scene()

The print statement proves that this setup make a kind of infinite loop of reset because the signal screen_exited is still emited when the player is queue_free.

Is it expected?


I am even more surprised with this modified version which produces the same thing:

func _ready() -> void:
    if debug_no_player:
        player.queue_free()
    player.connect("unvisible", self, "_reset_level")
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

The simple solution is to not emit the signal if the node is queued for deletion:

func __on_screen_exited() -> void:
    if !is_queued_for_deletion():
        emit_signal("unvisible")

Note that this is happening before the node is freed. Using queue_free ensure all the steps needed to remove the Node safely happen between frames.

After the Node is removed from the Scene, Godot will disconnect signals from it, and then disconnect signals to it, then finally release the Node.

At the point you get this notification, the Node has disappeared because it is removed from the scene, but it has not been disconnected yet.


You are, of course, reloading the scene, which causes the loop:

func _reset_level() -> void:
    print("reset")
    get_tree().reload_current_scene()

Presumably, if you are running on a debug mode where there is no player, then the removal of the player should not cause the scene to reload. Thus:

func _ready() -> void:
    if debug_no_player:
        player.queue_free()
    else:
        player.connect("unvisible", self, "_reset_level")

Although, I'd argue for making the player its own scene and instance it (and connect its signals) only if debug_no_player is false, instead of removing the player if debug_no_player is true.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .