Ready fires twice - no double functions, while/for or yields.

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By SnapCracklins

I searched the forums and my _ready function in a class is firing repeatedly every time this instance deletes itself. It’s a door and it spawns enemies when they are chasing a player and then deleting itself (moves to exit) if the player hides. The door objects inherit from a class but ready is only declared in the parent and there I have a placeholder function which then fires in the inheriting objects to prevent the old double-ready thing.

But now, whenever my instance of enemy spawns and then removes itself, it immediately instances again. I searched and couldn’t find answers that fit my problem.

What is commonly overlooked when this happens and what could I be doing wrong?

Original class:

    extends Area2D


class_name DoorEvent

### all doors have an area 2D. duh. attach sprites in editor.
onready var _sprite = $AnimatedSprite
onready var _box = $CollisionShape2D
onready var _timer = $Timer

var _enterBuffer = true ## this bool only flips on exits
var _monsterBuffer = true ## same but for monster
var _myDest : PackedScene ## holds scene destination, export in inherits\
var _player = preload("res://actors/Lydia.tscn")
var _enemy = preload("res://actors/Patches.tscn")
var _doorProcessed = false


## determine door place and where to go
var _amIDoorLeft = false ## update global with data
var _pos = Vector2(0,0) ## zero velocity shorthand


## connect door functions/signals, play base animation
func _ready():
	var _d_enter = self.connect("body_entered",self,"_door_enter")
	var _p_enter = self.connect("body_entered",self,"_enemy_enter")
	var _d_exit = self.connect("body_exited",self,"_door_exit")
	var _p_Jar = _timer.connect("timeout", self, "_pursuit_pull")
	
	## open animation if from correct side
	if StageHand._lastDoorLeft == _amIDoorLeft:
		_sprite.play('shut')
	else:
		_sprite.play('idle')
	
	## door processing
	if _monsterBuffer == true or _enterBuffer == true:
		_door_process()
	if _monsterBuffer == false or _enterBuffer == false:
		pass

func _door_process():
	pass ### placeholder overriden at instances


### ON ENTER, move player, remove enemy if buffer is off
func _door_enter(body):
		if body.name == "Lydia":
			if _enterBuffer == false:
				_z_edit()
				StageHand._lastDoorLeft = _amIDoorLeft
				_sprite.play("open")
				yield(_sprite,"animation_finished")
				var _nextRoom = get_tree().change_scene_to(_myDest)
		
		### parse distance to time in pursuit mode
		if body.name == "Lydia" and Director._inPursuit == true:
			_pursuit_parse_x()
				
				
### enemy door functions 
func _enemy_enter(body):
		if body.name == "Patches":
			if _monsterBuffer == false:
				_z_edit()
				_sprite.play("open")
				body._velocity = Vector2.ZERO
				Director._enemyHere = false
				Director._inPursuit = false
				_monsterBuffer = true
				_sprite.play('shut')
				body.queue_free()


## process check, at end of shut, return to idle animation
func _process(_delta):
	if _sprite.frame == 3 and _sprite.animation == 'shut':
		_door_idle()


## disable buffer when out of door frame
func _door_exit(body):
	if body.name == "Lydia":
		_enterBuffer = false
	if body.name == "Patches":
		_monsterBuffer = false		


## idle animation frames
func _door_idle():
	_sprite.play('idle')


## assign door and destination based on place
func _door_assign(_dest):
	_myDest = _dest ## this will update in inheritors


## spawn bodies
func _create_body(body):
	_sprite.play('shut')
	var _body_inst = body.instance()
	self.add_child(_body_inst)
	_body_inst.global_position.x = self.position.x + _pos.x
	_enterBuffer = true



### enemy call when chasing between rooms, timeout from buffer
func _pursuit_pull():
	if Director._enemyHere == false:
		_create_body(_enemy)
	else:
		return


### translate distance to time for room change
func _pursuit_parse_x():
	Director._pursuitJar = (abs(Director._playerVector2.x) - abs(Director._enemyVector2.x))/20
	

### respawn player/enemy at catch location if they escape
func _escape_process():
	_pos = Director._playerVector2
	_create_body(_player)
	_pos = Director._enemyVector2
	_create_body(_enemy)
	Director._stunFlag = true
	Director._escapeFlag = false
	_buffers_off()
	

### turn enter buffers off
func _buffers_off():
	_enterBuffer = false
	_monsterBuffer = false
	
	
### z correct for left side doors
func _z_edit():
	if _amIDoorLeft:
		self.z_index = self.z_index+1
	else:
		pass

And then the inheriting door object:

extends "res://doors/DoorEventClass.gd"

export var _myDestination : PackedScene

func _door_process():
	### give instance position, leftdoor flag and get resource
	_pos = Vector2(100,0)
	_amIDoorLeft = true
	_door_assign(_myDestination)
	
	### instance player if coming from right
	if StageHand._lastDoorLeft == false: 
		if Director._escapeFlag == false:
			_create_body(_player)
		else:
			pass

		if Director._inPursuit == true:
			if Director._enemyHere == false:
				_timer.start(Director._pursuitJar)
			if _monsterBuffer == false:
				pass
				
		## when escaping, turn buffers off
		if Director._escapeFlag == true:
			_escape_process()
		else:
			pass
	
	## also buffers off if not spawning
	if StageHand._lastDoorLeft == true:
		_buffers_off()

To summarize:

  1. Enemy spawns from door when _inPursuit flag is on and _enemyHere is off. The enemy has no spawn functions. Global variables (Director) _inPursuit and _enemyHere control instancing and flip whenever the enemy is present.
  2. After moving to door and freeing self, it continues to replicate at the door. (and constantly disappearing, because the buffer i use to allow him to exit is off once he exits.)
  3. Variables are flipping fine, I am checking them with an input command in my player object.
  4. The enemy object, after the initial disappear and constant respawn/disappear loop, does not even trigger physics because it frees so fast. This does not happen on the first instance.

Please provide project code (including everything to edit and run your game and see problem) to make it possible to troubleshoot your problem. There are plenty of reasons why it is not working in the way you expect. Because right now best answer for your question is “because of your mistake or engine’s bug”.
Basically, you asking to image multiple ways to reproduce your problem and then write solutions for it. It does not worth it, especially when sometimes problem is straight at surface and is as simple as wrong order or nodes in tree or misuse of function. Or in worst scenario this is really an engine’s bug and there is like 0.00000001% chance of guessing it. This is why source code of the project is required to help you.

AlexTheRegent | 2022-06-25 08:17

Updated above. Added code. Enjoy my spider’s web. :smiley:

SnapCracklins | 2022-06-25 17:04

:bust_in_silhouette: Reply From: Gluon

I have never heard of this happening in Godot so I would say there must be something in your code causing this. A signal perhaps which fires when you are despawning the sprite which another function then uses as a trigger to spawn another enemy? Without your code its impossible to say, if you upload your code to a github page and link it here someone may be able to help but to be honest I would suggest you should be looking for a logical error in your order of operations somewhere.

Yeah I know, hence why I’m confused and wondering if it’s some error I can’t decode from the clean debugger screen. I added the code above.

SnapCracklins | 2022-06-25 17:05

When does Director._escapeFlag become true?

I dont think we have all of the interacting code here but my first suspicion would be here as you are spawning an enemy and I would wonder if perhaps it spawns the enemy on the door and immediately despawns it and then spawns another enemy?

Gluon | 2022-06-25 22:27

I think you hit the nail on the head. Certainly the problem. The _escapeFlag is a variable for an “Escape sequence” where if the player is caught, an rng roll is made and if they win, they survive - hence the _escapeFlag which is on whenever the room is returned to so the player and enemy spawn in place versus spawning again at the doors. Turns out it was not that particular variable that was the source but you were correct with what was going wrong in my code.

SnapCracklins | 2022-06-25 22:39

Excellent glad you solved it!

Gluon | 2022-06-25 22:41

:bust_in_silhouette: Reply From: SnapCracklins

I figured it out inevitably - I always find the answer here one way or another.
Thanks to @Gluon for helping nail it down.

I am not sure why but updating the enemy instance (_pursuit_pull) function with another global variable did the trick. My guess is that the scene kept instancing every time the enemy instance freed, all the nodes were ready, therefore _ready kept getting called. (As mentioned, the enemy kept spawning and removing itself). I forgot to use the _inPursuit flag which tells my game a chase is in progress, so don’t spawn.

That’s my theory but if I am wrong, please correct me.

This simple line fixed it:

func _pursuit_pull():
if Director._enemyHere == false and Director._inPursuit == true:
	_create_body(_enemy)
	_enterBuffer = false
else:
	pass