I can't figure out why my enemy is not cloning itself

Godot 4.3

Hi! Deadlines for my project are coming in close and I can’t seem to figure this out. How can I make my enemies spawn in after one dies and the timer ends?

CODE:

extends CharacterBody2D

const SPEED = 400
const gravity = 100
var direction = 1
var object_scene = preload(“res://scenes/enemies/chicken.tscn”)
@onready var timer = $“…/Timer”

func _ready():
add_to_group(“enemy”)

func _physics_process(delta: float) → void:
pass
move_and_slide()
move_enemy()
reverse_direction()
add_gravity(delta)

func move_enemy():
velocity.x = SPEED * direction

func _on_hitbox_body_entered(body):
if body.is_in_group(“bullet”):
queue_free()
timer.start()
func reverse_direction():
if is_on_wall():
direction = -1

func add_gravity(delta):
if not is_on_floor():
velocity.y += gravity * delta

func _on_hitbox_area_entered(area):
if area.is_in_group(“swap”):
direction = 1

func _on_timer_timeout():
var new_object = object_scene.instantiate()
add_child(new_object)
new_object.position = Vector2(-944, 432)
new_object.position = Vector2(776, 432)

An object cannot execute it’s function after it’s free. The connection to timer.timeout has been severed.

You need to find a new object to spawn the chicken. Maybe the timer should have it’s own script to spawn chickens while appropriate.

How would I do so? I tried this but that didn’t work either.

CODE:

extends Timer

@onready var chicken = $"../chicken"
@onready var chicken_instantiate = preload("res://scenes/enemies/chicken.tscn")

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	if chicken.queue_free():
		start()

func _on_timeout():
	stop()
	add_child(chicken)
	chicken_instantiate.instantiate() 
	var enemy_position = Vector2(-248, 352)
	chicken_instantiate.enemy_position

This looks pretty nice as an idea, but there are a lot of syntax and logistical issues.

queue_free() deletes a node, it does not return anything so it does not work as a condition for if statements. This line only deletes the chicken.

If you want to start the timer only when a chicken is removed you will have to add a connection when the chicken exits the tree

func _ready() -> void:
	chicken.tree_exiting.connect(start)

chicken was just deleted, so this variable is now invalid, presumably it was already a child.


This creates a new instance, but without being assigned to a variable the new instance is lost forever. You probably want to use

var new_chicken = chicken_instantiate.instantiate()

And now you could modify the new chicken.


The second statement has no effect, you need to assign the chicken’s enemy_position (or probably just position?). The first line create a unrelated variable with the same name, the two do not interact in any way.


In total I would recommend these changes to your timer/spawner script.

extends Timer

@onready var chicken: Node2D = $"../chicken"
const chicken_instantiate: PackedScene = preload("res://scenes/enemies/chicken.tscn")

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	chicken.tree_exiting.connect(start)

func _on_timeout():
	stop()

	# assign `chicken` to a new chicken isntance
	chicken = chicken_instantiate.instantiate()
	
	# set new chicken's position
	chicken.position = Vector2(-248, 352)

	# start timer again when the new chicken is freed
	chicken.tree_exiting.connect(start)

	# add new chicken as a child
	add_child(chicken)

it’s giving me an error saying that tree_exiting is a signal but is being used as a function on line 20. How do i fix that?

Just a small error. Looking at the code provided by @gertkeno I believe line 20 should be the same as the one seen in _ready().

	chicken.tree_exiting.connect(start)

Don’t forget to understand the code you’re using!

1 Like

Can you paste your code? It sounds like you are using chicken.tree_exiting(start) when it needs .connect