Enemies fly off screen when respawning

Godot Version

4.6.2

Question

So i am trying to get an enemy spawner working, but when two enemies spawn at the same point they start vibrating then shoot offscreen. how would i fix this?

extends CharacterBody2D

@export var spawn_position = Vector2(-26, -79)
var respawn_pos: Array = [
	Vector2(-26, -79), 
	Vector2(0, -79), 
	Vector2(41, -79)
	]
#@export var enemy_id: int
@export var respawn_time := 3.0

@onready var sprite: Sprite2D = $Sprite2D
@onready var collision_shape: CollisionShape2D = $CollisionShape2D
@onready var timer: Timer = $Timer


var just_spawned := true
var invulnerable := false
var is_dead = false
var can_attack := true
var healing = false
var heal_cooldown = true
var health
var hit_cooldown = false
var original_layer: int
var original_mask: int
var speed = speedy

func _ready():
	original_layer = collision_layer
	original_mask = collision_mask
	door = get_tree().get_first_node_in_group("door")
	just_spawned = true
	#sets spawn location
	if GameState.on_main_menu:
		GameState.enemy_2_positions = position
	#if not is_dead:
		#if not GameState.is_game_starting2:
			#global_position = GameState.enemy_2_positions
		#if GameState.is_game_starting2:
			#global_position = spawn_position
			#GameState.is_game_starting2 = false
	move_and_slide()

func _deferred_restore():
	await _wait_for_enemies()
	restore_game_state()

		
func restore_game_state():
	if not GameState.has_saved_state:
		return

	for enemy in get_tree().get_nodes_in_group("enemies"):
		var id = enemy.get_instance_id()
		if id in GameState.enemy_2_positions:
			enemy.global_position = GameState.enemy_2_positions[id]


func disable_target():
	# Turn OFF target
	if is_dead or invulnerable:
		return
		
	GameState.money += 1
	GameState.kills += 1
	is_dead = true
	sprite.visible = false
	set_physics_process(false)
	collision_shape.set_deferred("disabled", true)
	await get_tree().physics_frame
	timer.start(respawn_time)
func _on_timer_timeout():
	can_attack = true
	if is_dead:
		
		var respawn_position = respawn_pos.pick_random()
		global_position = respawn_position

		# Restore collision settings
		collision_layer = original_layer
		collision_mask = original_mask

		# Reactivate visuals
		sprite.visible = true
		invulnerable = true
		# Re‑enable collision SAFELY
		await get_tree().physics_frame
		collision_shape.set_deferred("disabled", false)
		set_physics_process(true)
		is_dead = false
		
		await get_tree().create_timer(0.3).timeout
		invulnerable = false

	#heals door when not getting hit
	if not heal_cooldown:
		if door and global_position.distance_to(door.global_position) > 20:
			Global.health += 1
			if Global.health > 100:
				Global.health = 100
	else:
		heal_cooldown = true

@export var speedy: float = 100.0

var door: Node2D


@warning_ignore("unused_parameter")
func _physics_process(_delta):
	if door == null:
		return

	# Direction toward door
	var direction = (door.global_position - global_position).normalized()
	# Move enemy
	velocity = direction * speed
	move_and_slide()
	if sprite.visible == false:
		speed = 0
	if sprite.visible == true:
		speed = speedy
	GameState.enemy_2_positions = position


# enemy detects and attacks the door
	if door and global_position.distance_to(door.global_position) < 20:
		if can_attack:
			attack_door()
			Global.getting_hit = false

# enemy damages the door
func attack_door():
	heal_cooldown = true
	Global.health -= 1
	#print("hit")
	can_attack = false
	timer.start(1.5) # attack cooldown time
	if Global.health <= 0:
		get_tree().change_scene_to_file("res://scenes/game_over.tscn")



func _wait_for_enemies():
	while get_tree().get_nodes_in_group("enemies").is_empty():
		@warning_ignore("redundant_await")
		await get

Below is my script for the enemy spawner

extends Node2D

@export var enemy_scene: PackedScene
@export var enemy_count: int = 3

@export var spawn_positions := [
Vector2(-26, -79),
Vector2(0, -79),
Vector2(41, -79)
]

func _ready():
for i in range(enemy_count):
spawn_enemy()

func spawn_enemy():
var enemy = enemy_scene.instantiate()
enemy.global_position = get_random_spawn_pos()
add_child(enemy)

func get_random_spawn_pos() → Vector2:
var positions = [
Vector2(-26, -79),
Vector2(0, -79),
Vector2(41, -79)
]
return positions.pick_random()

Do the enemi is spawnig with velocity?

Maybe the spawning position is wrong?

I tried that, i set the velocity to zero when they spawn in and it still happens. I figured out it happens when their hitboxes fully overlap

So 2 enemis are spawning the same time and they fly off?

yes. I am trying to figure out how to temporarily turn off collision upon spawning right now.

Maybe you can add a respawn timer to not spawn 2 in one shot.

Like a time to next spawn = 2 seconds.

I do have a respawn timer, but they still spawn in at the same location if killed at the same time.

A random timer may be a god idea.

I will try that now

And switch to jolt phisics.

This makes collision more stable.

In project settings in phisics settings with advanced settings anabled you can switch to jolt.

I will do that

I am working on a 2d game so i can’t swtich to jolt physics.

It sounds like when the physics unembeds them from each other they are launched apart. I’d suggest either not letting them collide with each other, or not spawning a new enemy if it would collide on spawn.

I will get right on that, thank you!

Do you think you could explain how to do that? I have tried googling it and can’t find anything

I had a similar issue until I made the spawn destination a random position (within bounds…). That solved the ‘one landing on top of the previous’ for me. As my spawnees are animated, they crawled away quickly enough to not stay in the ‘drop zone’.

I do have random spawn points, so it shounds like the solution is to just add a lot of spawn points? Or do you mean select a random spawn point between two valid points, because if you do do you think you could explain how to do that?

Do you need these enemies to collide with each other? If not, you can set their collision masks so they won’t collide.

If they do need to collide with each other, you could make it so the spawner does a collision check with all enemies, but just a check, no physics. If the spawner is colliding with any enemies, defer spawning new enemies….

I turned off their collision masks but they still collide with each other.

Someone with better understanding of the physics system than I will probably need to chime in, then.