hey, I am adding features to a survivors game i made thanks to a youtube tutorial and i added an enemy spawner. my problem is that when i spawn multiple enemies only one of them follows the entire script for some reason. they all go towards the players position but thats it. one of them always follows full script by playing attack animation and emitting the signal, the rest dont however.
i am mostly concerned to why the signal only emits once as that should solve the attack animation problem too. i connected them to my main scene perfectly fine and it works and prints the condition once the signal was connected, my only problem is that this signal is only emitted when one of the enemies die, probably the first to spawn. the print statement should be printing the updated variable every time an enemy dies heres the code.
@onready var player = get_node(“/root/Game/player”)
func _physics_process(delta: float) → void:
var direction = global_position.direction_to(player.global_position) # set position to the direction of players position
velocity = direction * speed
move_and_slide()
func take_damage():
%ProgressBar.visible = true # show health bar
health -= 1
%ProgressBar.value = health
$demon_spirte.play("hurt")
if health == 0:
%ProgressBar.visible = false
%ani_death.start() # start timer to allow death to finish
$demon_spirte.play("die")
dead_enemy.emit()
this is another node within it that i used a collision shape to register if the player was in range:
extends Area2D
signal deal_damage
func _physics_process(delta: float) → void:
var player_in_range = get_overlapping_bodies()
if player_in_range.size() > 1:
deal_damage.emit()
the code in the main script to spawn the enemies:
func mob_spawn():
var new_mob = preload(“res://Scenes/demon.tscn”).instantiate()
%PathFollow2D.progress_ratio = randf() # set position of path to random position along path
new_mob.global_position = %PathFollow2D.global_position # set mobs spawn to this random location
add_child(new_mob) # spawn the mob
sorry if the code is messy/doesnt make sense im new to godot.
sorry about that i hope its formatted now. does the `` make the code look / act like it sound in the engine? also no rush replying thanks for replying in the first place.
extends CharacterBody2D
signal dead_enemy
var health = 3.0
var speed = 250.0
@onready var player = get_node(“/root/Game/player”)
func _physics_process(delta: float) → void:
var direction = global_position.direction_to(player.global_position) # set position to the direction of players position
velocity = direction * speed
move_and_slide()
func take_damage():
%ProgressBar.visible = true # show health bar
health -= 1
%ProgressBar.value = health
$demon_spirte.play("hurt")
if health == 0:
%ProgressBar.visible = false
%ani_death.start() # start timer to allow death to finish
$demon_spirte.play("die")
dead_enemy.emit()
func _on_ani_death_timeout() → void:
queue_free()
func _on_argo_range_deal_damage() → void:
$demon_spirte.play(“attack”)
func _on_demon_spirte_animation_finished() → void:
$demon_spirte.play(“move”)
this is another node within it that i used a collision shape to register if the player was in range:
extends Area2D
signal deal_damage
func _physics_process(delta: float) → void:
var player_in_range = get_overlapping_bodies()
if player_in_range.size() > 1:
deal_damage.emit()
the code in the main script to spawn the enemies:
func mob_spawn():
var new_mob = preload(“res://Scenes/demon.tscn”).instantiate()
%PathFollow2D.progress_ratio = randf() # set position of path to random position along path
new_mob.global_position = %PathFollow2D.global_position # set mobs spawn to this
random location
add_child(new_mob) # spawn the mob
Try putting three ticks on a line above and below the code so like:
tickticktick
the code
tickticktick
In this case each tick is = `
so just add three of those above and below the code, not on the same line.
It would look like
the code
Also, you can just edit one of your replies, you don’t need to reply again
This seems problematic, does you player have more than one hitbox? If not, and you are using collision masks, then you can use has_overlapping_bodies() to check if anything is overlapping the attack range.
they do, im guessing this is bad practice? they have a hitbox for collisions and one inside an Area2D which is also connected. ive attached screenshots of the collision masks and layers if that has anything to do with my issue. thanks for your reply!
the 2nd collision shape in the player node is one for the guns range that works perfectly
Nothing wrong with multiple physics bodies, and your scenes do not have multiple physics bodies; I understand the confusion with multiple collision shapes, but one is for a CharacterBody2D and the other an Area2D, which will not trigger the body_entered signal.
The thing wrong with this snippet is that it’s looking for more than one collision, not one or more. That’s where has_overlapping_bodies() will simplify the expression.
that does make the code more simple and thanks for the help, however the signal is still only emitted once all the enemy nodes are killed. ill attach pictures since i dont think i can upload videos to the forums. i presume it has something to do with my logic on emitting the signal.
yeah thats right sorry if i was unclear before, the print tester that prints “i died” works everytime they die but the signal isnt emitted until they all die.
enemy script that emits the signal (signal highlighed)
extends CharacterBody2D
signal dead_enemy
var health = 3.0
var speed = 250.0
var die = false
@onready var player = get_node("/root/Game/player")
func _physics_process(delta: float) -> void:
var direction = global_position.direction_to(player.global_position) # set position to the direction of players position
velocity = direction * speed
move_and_slide()
func take_damage():
%ProgressBar.visible = true # show health bar
health -= 1
%ProgressBar.value = health
$demon_spirte.play("hurt")
***if health == 0:***
*** print("i died")***
*** %ProgressBar.visible = false***
*** %ani_death.start() # start timer to allow death to finish***
*** $demon_spirte.play("die")***
*** dead_enemy.emit()*** # <----- THIS IS THE SIGNAL THAT SHOULD REGSITER dead_enemy_count()
func _on_ani_death_timeout() -> void:
queue_free()
func _on_argo_range_deal_damage() -> void:
$demon_spirte.play("attack")
func _on_demon_spirte_animation_finished() -> void:
$demon_spirte.play("move")
main game loop script that sould listen for the signal, signals connect on ready and the listener is at the very bottom:
extends Node2D
var wave = 0
var num_killed = 0
func _ready() -> void:
wave_apply()
var enemy_scene = get_node("/root/Game/demon")
enemy_scene.connect("dead_enemy", dead_enemy_count)
func mob_spawn():
var new_mob = preload("res://Scenes/demon.tscn").instantiate()
%PathFollow2D.progress_ratio = randf() # set position of path to random position along path
new_mob.global_position = %PathFollow2D.global_position # set mobs spawn to this random location
add_child(new_mob) # spawn the mob
func wave_apply():
%WaveNum.text = str(wave)
match wave:
0:
print("wave 1")
for i in range(3):
mob_spawn()
#wave += 1
time_between_waves()
1:
print("wave 2")
for i in range(8):
mob_spawn()
func time_between_waves():
#%WaveCooldown.start()
pass
func _on_wave_cooldown_timeout() -> void:
wave_apply()
func dead_enemy_count():
print("killed")
num_killed += 1
print(num_killed)
wow! that was a very simple fix in hindsight… i should probably flowchart important steps like these in the future. thanks a million for your time and a solution, i really appreciate it. enjoy the rest of your week