I have problems with respawning enemies after checkpoints

Hello, I’m new to Godot and I’m trying to learn how to use engine. Right now I’m trying to make a simple 2D platformer, and got to the point where I managed to make a checkpoint system and respawning the player. But each time the player respawns the enemies don’t respawn with it. To my understanding is because i have a killzone script separated from the script of the enemies and since the enemies queue_free after being defeated they won’t return to their original positions. My question is, how can I make it so that when the player respawns after hitting a checkpoint the enemies respawn with the player?

This is my player code:

class_name Player
extends CharacterBody2D


const SPEED = 250.0
const JUMP_VELOCITY = -400.0

@onready var animated_sprite = $AnimatedSprite2D
@onready var jump_height_timer = $JumpHeightTimer
@onready var coyote_timer = $CoyoteTimer

var gravity = 1250.0
var can_jump = true
var can_coyote_jump = false
var dying: bool = false


func _ready():
	add_to_group("Player")
	GameManager.player = self

func die():
	dying = true
	if dying == true:
		animated_sprite.play("death")


func _process(delta):
	# Add gravity
	if not is_on_floor() and (can_coyote_jump == false):
		velocity.y += gravity * delta
		
	# Handle jump
	if Input.is_action_just_pressed("jump"):
		if is_on_floor() or can_coyote_jump:
			jump_height_timer.start()
			jump()
			if can_coyote_jump:
				can_coyote_jump = false
		
		
	# Get inputs for movement
	var direction = Input.get_axis("move_left", "move_right_")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
	
	
	# Play and Flip the Animation
	if is_on_floor():
		if direction > 0:
			animated_sprite.flip_h = false
		elif direction < 0:
			animated_sprite.flip_h = true
	
		if direction == 0:
			animated_sprite.play("idle")
		else:
			animated_sprite.play("run")
	else:
		animated_sprite.play("jump")
		
	# Handle Coyote Time
	var was_on_floor = is_on_floor()
	if was_on_floor and !is_on_floor() and velocity.y >= 0:
		print("FALL")
		can_coyote_jump = true
		coyote_timer.start()
	
	
	move_and_slide()


func _on_coyote_timer_timeout():
	can_coyote_jump = false
	
	
func jump():
	velocity.y = JUMP_VELOCITY
	can_jump = false
	

func spring_jump(jump_force):
	velocity.y = JUMP_VELOCITY * jump_force


func _on_jump_height_timer_timeout():
	if !Input.is_action_pressed("jump"):
		if velocity.y < 0:
			velocity.y = 0
			print("Low Jump")
	else:
		print("High Jump")

this is the code of the killzone


class_name Killzone
extends Area2D

@onready var timer = $Timer


func _on_body_entered(body):
	if body.is_in_group("Player"):
		print("You died")
		Engine.time_scale = 0.5
		timer.start()
	
	if body.has_method("die"):
		body.die()


func _on_timer_timeout():
	Engine.time_scale = 1.0
	GameManager.respawn_player()

this is the code of one type of enemy

extends Node2D


@onready var respawn_timer = $Respawn_Timer
@onready var ray_cast_der = $RayCastDER
@onready var ray_cast_izq = $RayCastIZQ
@onready var animated_sprite = $AnimatedSprite2D
@export var jump_force = 1
@export var respawn_position: Vector2

const SPEED = 40

var direction = 1
var initial_position : Vector2


func _ready():
	add_to_group("Enemy")

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	if ray_cast_der.is_colliding():
		direction = -1
		animated_sprite.flip_h =  false
	if ray_cast_izq.is_colliding():
		direction = 1
		animated_sprite.flip_h = true
	
	position.x += direction * SPEED * delta

func _on_hitbox_body_entered(body):
	if body.is_in_group("Player"):
		respawn_timer.start()
		body.spring_jump(jump_force)
		direction = 0
		animated_sprite.play("death")
		await animated_sprite.animation_finished
		get_parent().remove_child(self)


func _on_respawn_timer_timeout():
	set_process(true)
	reset_position()


func reset_position():
	position = initial_position
	direction = 1
	animated_sprite.play("move")

and this is the code of the game manager which handles respawn

extends Node

var starting_point = Vector2(40, 556)
var current_checkpoint : Checkpoint
var player : Player


func respawn_player():
	if current_checkpoint != null:
		player.position = current_checkpoint.global_position
	else :
		player.position = starting_point
	
	respawn_enemies()
	

func respawn_enemies():
	pass

Thanks if you decide to look into this

You can solve this reinstanciating the entire stage and only set the player start position

  • Create an autoload with the name you prefer (as example lets call player_info, if GameManager is already an autoload you can use it instead.)
  • Create a variable called checkpoint_position: Vector2
  • When you reach the checkpoint, set the your_autoload_script.checkpoint_position = the_checkpoint_global_position
  • When your player dies, call get_tree().reload_current_scene()
  • In the respawn_player function:
func respawn_player():
	if checkpoint_position != Vector2.ZERO:
		player.global_position = your_autoload_script.checkpoint_position
	else :
		player.global_position = starting_point

Thank you for taking the time to reply to my problem, I really appreciate it. But now, it gives me an error when I touch the checkpoint. I did everything you mentioned and yes, the Game Manager is an Auto Load, but I realized i made a mistake and forgot to give the code of the checkpoint.

Here is the code of the Checkpoint

extends Node2D
class_name Checkpoint

@export var spawnpoint = false
@onready var animated_sprite = $AnimatedSprite2D

var activated = false


func activate():
	GameManager.current_checkpoint = self
	activated = true
	animated_sprite.play("Touched")


func _on_area_2d_area_entered(area):
	if area.get_parent() is Player and !activated:
		activate()

I think the line GameManager.checkpoint_position = Checkpoint_global_position is the one giving me the error.

Thank you if you decide to look into this again.

But what’s the error?


In the checkpoint script, try this:

extends Node2D
class_name Checkpoint

@export var spawnpoint = false
@onready var animated_sprite = $AnimatedSprite2D

var activated = false

func _on_area_2d_area_entered(area):
	if area.get_parent() is Player and !activated:
		GameManager.checkpoint_position = global_position
		activated = true
		animated_sprite.play("Touched")