0 votes

Hi, I'm working on a game where if an enemy goes offscreen or dies, if the player revisits the position they originally started from then they respawn at that position. However, how I have the code currently set up doesn't work how I expect:

# for setting things when this comes onscreen
func _on_VisibilityNotifier2D_screen_entered():
    if (can_spawn):
        visible = true;
        set_physics_process(true)
        health = MAX_HEALTH
        motion = Vector2()
        can_spawn = false

#disable things when they leave the screen + reset them to
#their starting position
func _on_VisibilityNotifier2D_screen_exited():
    set_physics_process(false)
    set_position(starting_position)

    print($VisibilityNotifier2D.is_on_screen())
    #only allow this to spawn if the spawn position is not onscreen
    if (!$VisibilityNotifier2D.is_on_screen()):
        can_spawn = true;
        visible = false;

What is supposed to happen is that when an object goes offscreen, it teleports back to the position back to where it started off. If the starting position is still onscreen however, then the object will need to be scrolled offscreen again before it can be spawned -- thus, the if(!is_on_screen()): can_spawn = true check. Here's a finite state machine diagram for the intended behaviour in case it's unclear:
enter image description here

What actually happens though is that the object instantly spawns regardless of the aforementioned check. Looking at some print() debugging, isonscreen() always returns false from this method, even if the object was just moved onscreen.

Can anyone recommend any ways to fix this? I was thinking of using a 1-frame timer to make sure the object cannot respawn in the same frame that it despawns, but I was wondering if there were any better solutions.

Godot version 3.2.3
in Engine by (31 points)

1 Answer

0 votes

"Note: For performance reasons, VisibilityNotifier2D uses an approximate heuristic with precision determined by ProjectSettings.world/2d/cell_size. If you need precise visibility checking, use another method such as adding an Area2D node as a child of a Camera2D node."

VisibilityNotifier2Ds aren't the most precise things. is_on_screen() will always return false when checked from the screen_exited signal callback. The signal is literally fired when is_on_screen() == false. Are you checking a different VisibilityNotifier2D in that callback?

As for the Enemy respawning right away I'm thinking it's because you're setting its position in the screen_exited callback. Try:

func _on_VisibilityNotifier2D_screen_exited():
    set_physics_process(false)
    can_spawn = true;
    visible = false;
    call_deferred("set_position", starting_position)
by (2,834 points)

I was aware that VisibilityNotifier2D was imprecise from the documentation description, but seeing in what regards it was from some independent testing (on_screen_entered() would fire before the VisibilityNotifier came onscreen, at certain cell sizes) I assumed it wasn't the same thing.

Also, no, I'm checking the same VisibilityNotifier2D that calls the signal. I set the position before checking is_on_screen(), so I assumed that the value would change based on the new position. It seems that is_on_screen() only updates at the start of each frame?

Oh, and, the code you describe there doesn't appear to work. Seems to work the same as it did prior, unless I was supposed to change something else as well.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.