VisibilityNotifier2D _on_screen_exited called when it shouldn't

Godot Version



I am setting up a basic shoot-em-up. For the enemies I have a VisibilityNotifier2D properly connected to a _on_VisibilityNotifier2D_screen_exited method so that when they exit the bottom of the screen I can queue_free. Enemies are their own scene. I have one “main” scene where I instantiate and add_child(enemy) the enemies by code and that works well. I want to create a second scene where enemies are already present in the scene. In this second scene I added a couple enemies using the “Instance child scene” method and placed them in the scene within the viewport. When I play the scene the enemies are deleted.

Some details, not sure if relevant:

  • Both the enemies and player use Area2D and there are no bodies except for the tilemap.
  • This is a retro game with a window/viewport size of 160x144. I have the project setting/world/2d/cell size to the default 100 which, for the first main scene, triggers the _on_VisibilityNotifier2D_screen_exited after a while when the enemies exit the viewport.
  • The enemy has an animated sprite and collision shape with size is 16x16 and the visibility notifier is a little bigger at 20x20.
  • This scene has the player instantiated and it is displayed and behaves properly.
  • There is the player, a Tilemap and ParallaxBackground in the scene and nothing else.
  • The enemy is definitely within the viewport. I have the following code:
func _on_VisibilityNotifier2D_screen_exited():
	print("destroyed enemy because it exited screen")

I get this printout in the console:

destroyed enemy because it exited screen
(80, 80)
(80, 80)

I tried commenting the last line queue_free() and still there is no enemy to be seen.

If you did that and you can’t see it then it’s outside the viewport. While running the game, check the Remote tab in the Scene dock and check where the enemy is.

1 Like

Thanks. I did not know about the “Remote” scene tab. That helped me figure out what was happening. There was some code on _ready that was calling queue_free on a group which included the enemy I had placed. When that code ran, the visibility notifier signal was also triggered.

I have another question now, is it possible to differentiate whether the screen_exited signal is called because it actually went out of the screen versus the parent node was deleted using queue_free? Doesn’t look like it provides any arguments. I guess I need to check myself using is_queued_for_deletion from Object — Godot Engine (3.5) documentation in English

Looking around I found someone who had a similar issue but they decided to check screen coordinates manually instead of relying on the VisibilityNotifier.

You can check if the Node is queued for deletion with Object.is_queued_for_deletion()

Something like:

func _on_VisibilityNotifier2D_screen_exited():
	if is_queued_for_deletion():
		# The signal has been emitted because the node is being freed
		# The signal has been emitted because the node is outside the screen

EDIT: Yep, you found the answer while I was writing this :sweat_smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.