Spawning object after it has been destroyed

Godot Version

4.3

Question

Hi everyone. I’m trying to create a game where an object spawns and you need to grab the correct object towards it to destroy it, and once it’s destroyed it will spawn again after a certain amount of time. While I’ve been able to accomplish spawning the object I want and destroying it, I haven’t been able to get it to spawn again. I’ve tried using if statements but that keeps it from spawning altogether.
This part gets it to spawn:

extends Area2D

var customer = preload("res://right_customer.tscn")
var spawned = false
var posit = self.global_position

func _ready() -> void:
	spawn()


func spawn():
			%SpawnTimer.start()
		
func _on_spawn_timer_timeout() -> void:
		inst(posit)
		spawned = true
		global.customers += 1
		%SpawnTimer.stop() # Keeps it from spawning over and over again.
		
func inst(pos):
	var instance = customer.instantiate()
	instance.position = pos
	add_child(instance)

And this part is to destroy the object that has been spawned.

extends Node2D

var draggable = false
var is_inside_dropable = false
var body_ref
var offset: Vector2
var initialPos: Vector2

func _ready():
	initialPos = global_position
	$Label.text = self.name

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
	if draggable:
		if Input.is_action_just_pressed("click"):
			offset = get_global_mouse_position() - global_position
			global.is_dragging = true
		if Input.is_action_pressed('click'):
			global_position = get_global_mouse_position() - offset
		elif Input. is_action_just_released("click"):
			global.is_dragging = false
			if is_inside_dropable:
				var test = body_ref.desired_food[0]
				if self.name == test:
					body_ref.queue_free()
					global.score += 1
				else:
					body_ref.patience -= 20
			var tween = get_tree().create_tween()
			tween.tween_property(self,"global_position",initialPos,0)
				

func _on_area_2d_mouse_entered():
	if not global.is_dragging:
		draggable = true
		scale = Vector2(1.2, 1.2)


func _on_area_2d_mouse_exited():
	if not global.is_dragging:
		draggable = false
		scale = Vector2(1,1)


func _on_area_2d_body_entered(body):
	if body.is_in_group("dropable"):
		is_inside_dropable = true
		body_ref = body
		body.scale = Vector2(1.1,1.1)
		

func _on_area_2d_body_exited(body):
	if body.is_in_group("dropable"):
		is_inside_dropable = false
		body.scale = Vector2(1,1)
		

If anyone can help, I’d appreciate it.

It looks like you are stopping the timer in the spawner, but nothing starts it again. Something will have to call spawn() again. I’m not sure what “global” is but I assume it’s an autoload you have. Maybe have a function in global that tells the timer to start again, and call it from your _on_area_2d_body_entered? Would be a good idea to tell us what global is. It’s kind of important and I’m only able to assume it’s an autoload.

Yes, global is the autoload.

Cool. So you need something to call spawn again and probably doing it from global would work. You seem to be making a lot of unnecessary choices or maybe ones that are just confusing to me. var posit = self.global_position… it will never change, it simply captures where the global position is just before _ready fires. You have a lot of seemingly unnecessary logic jumps too and probably far more conditionals or nesting than necessary, but maybe I’m missing something? You may need to give more info.

Yeah, that’s to help put the instantiated object in the position where I drop it in the scene; last I checked, if I don’t do that, the object just appears in the top left of the screen.
Also, I am trying to call spawn from global, but it give me a “null_instance” error. I’m trying to call the node, but it doesn’t seem to work.
This is the function I used in the autoload to try to spawn again, and the SpawnPoint is it’s own scene that I’ve placed in the main scene.

func start_again():
	var custom = get_node("/root/global/SpawnPoint")
	custom.spawn()

Apologies if this isn’t very clear.

	if self.name == test:
		body_ref.queue_free()
		global.score += 1

to it add a code/line to run func spawn()

if self.name == test:
					body_ref.queue_free()
					global.score += 1
					global.start_again()

I used this code and it still gives me the “null_instance” error.

Attempt to call function 'spawn' in base 'null instance' on a null instance.

And this is after I created the function in global.

can you show the start_again() in global

I think all you have to do is call %SpawnTimer.start() after the doodad gets queue_freed. % should make the node accessible anywhere as long as it’s in the scene. Sorry if I’m butting in on your process, but just some tips, you can probably queue free in the area entered signal. I think _input is better for checking if the mouse is moving rather than do it constantly in process. You can probably move a lot of that in _process into signals. Calling all of these each process frame is pretty un-optimal though you aren’t doing enough per frame to notice it being slow. Just some thoughts.

2 Likes
func start_again():
	var custom = get_node("/root/global/SpawnPoint")
	custom.spawn()

Okay, that worked, thanks. But I had to create it as a child in the main scene rather than a child of the SpawnPoint Scene itself, if that makes any sense.

I suppose, although the idea is to have it happen when the player lets go of the mouse button, but maybe that does clutter things up, or at least it does the way I’m doing it.

Thanks again.

1 Like

Well, at least you are paying attention to your process, just checking ;).

1 Like

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