Problem with duplicate enemies

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Matteo

Hello,
first thing i have to say is that I’m new in Godot, so maybe my problem is very simple and i don’t know that.

I’m trying to do a platformer with some enemies, I did an attack() function for the player that destroy the enemy only when it collide with the player using the “body entered” signal and the $Enemy.queue_free ()
My problem is the following:
when i try to put more than one enemy in my level, when I attack one enemy all the enemies in the level all are destroyed
I think the problem is the queue_free function because it destroy the entire node or maybe a signal that i created for the death function of the enemy.

So my question is: Is possible to make a duplicate of an enemy that act indipendently from the first?
Sorry for my bad english

Thanks you all fo the help

For more precise help, post the code that 1) instantiates the enemies and 2) removes the enemies on collision.

jgodfrey | 2020-03-29 21:05

:bust_in_silhouette: Reply From: njamster

destroy the enemy only when it collide with the player using the body_entered signal and the $Enemy.queue_free()

A correctly connected callback should resemble something like this:

func _on_KinematicBody2D_body_entered(body):
	body.queue_free()

if yours (as you state it does) looks like this…

func _on_KinematicBody2D_body_entered(body):
	$Enemy.queue_free()

… then instead of freeing the entering body, you’re freeing a child-node of your scene called “Enemy”. Which - if it is a node containing all enemy scenes instanced to the level - could very well free all enemies in the level at once.

If this is not the problem, then you need to provide a more complete example.

Thanks for your help.
I’ll try to explain me well. first thing that I’ve done was create a scene for the enemy and a scene for the player; after that i created a scene called “level” and i inserted the player and the enemy
this is the code in my player(KinematicBody2D), i used a signal for detect if something enter in the body of enemies(RigidBody2D); the signal “die” is connected with the enemy script
this is my player script

  func _on_enemies_body_entered():
	var animation= $AnimatedSprite.get_animation() 
	var frame= $AnimatedSprite.get_frame()
	if  animation=="attack" and frame==2:	
		enemy_death()
	

func enemy_death():
	emit_signal("die")

this is the enemy script`

func _on_KinematicBody2D_die() -> void:
	queue_free()

when I start the scene where there are two copy of the same enemy when i attack one of them also the one i haven’t attacked die and I don’t know why.

Matteo | 2020-03-30 10:39

If you spawn multiple instances of your enemy scene, they will all connect the “die”-signal to _on_KinematicBody2D_die(). So if the player emits that signal once, all enemies will catch it and consequentially call queue_free().

Your player-scene should consist of at least these nodes:

- KinematicBody2D
  - Area2D
   - CollisionShape2D

Connect the Area2D’s body_entered-signal to a callback:

func _on_Area2D_body_entered(body):
    var animation= $AnimatedSprite.get_animation() 
    var frame= $AnimatedSprite.get_frame()
    if  animation=="attack" and frame==2:   
        body.die()

Now your enemy script should look like this:

func die():
    queue_free()

To make your code more stable, consider adding your enemy scene to a group and check at the beginning of _on_Area2D_body_entered if the body is in that group:

func _on_Area2D_body_entered(body):
    if body.is_in_group("enemies"):
        var animation= $AnimatedSprite.get_animation() 
        var frame= $AnimatedSprite.get_frame()
        if  animation=="attack" and frame==2:   
            body.die()

njamster | 2020-03-30 11:11

Now when I attack an enemy the game crash and it reports the following error:
Invalid call. Nonexistent function “die” in base “String”
I don’t know why

Matteo | 2020-03-30 12:55

This error tells you that you’re trying to call a function called “die” on something (a string in this case) that doesn’t have this function. Make sure you’re calling:

body.die()

and not

"body".die()

njamster | 2020-03-30 18:17

I wrote correct, but i think the problem is the way i defined what body is…
How can I define body as the enemy?

Matteo | 2020-03-30 18:51

You don’t define body. All you do is connect the body_entered-signal of your Area2D to the callback. Godot will take care of the rest and pass the entering body as an argument. The code I provided should work as is, no additions required.

njamster | 2020-03-30 19:21

This is my entire player script

In the Animate function when I call the _on_Area2D_body_entered(body) function there is an error that say: Identifier “body” is not declared in the current scope. so i thought that maybe i had to define body with something

extends KinematicBody2D
var movimento= Vector2(0,0)
const UP= Vector2(0,-1)
const SPEED=600
const JUMP_SPEED=2000
const GRAVITY = 220


func _physics_process(delta):
	move()
	animate()
	gravity()
	jump()
	
	
func move():
	if Input.is_action_pressed("right") and not Input.is_action_pressed("left"):
		movimento.x= SPEED
	elif Input.is_action_pressed("left") and not Input.is_action_pressed("right"):
		movimento.x = -SPEED
	else:
		movimento.x=0						
	move_and_slide(movimento,UP, false, 4, PI/4, false )

func jump():
	
	if Input.is_action_pressed("jump") and is_on_floor():
		movimento.y-=JUMP_SPEED
	
	

func animate():
	if movimento.x<0:
		$AnimatedSprite.set_flip_h(true)
		$AnimatedSprite.play("run")
	elif movimento.x>0:
		$AnimatedSprite.set_flip_h(false)
		$AnimatedSprite.play("run")
	elif movimento.y<0:
		$AnimatedSprite.play("jump")
	elif movimento.y>0 and not is_on_floor():
		$AnimatedSprite.play("fall")	
	#	attack
	elif Input.is_action_pressed("atttack") and is_on_floor():
		_on_Area2D_body_entered(body)
		yield(get_tree(), "idle_frame")
		$AnimatedSprite.play("attack")
#		
		
	else:
		$AnimatedSprite.play("idle")

func gravity():
	
	if is_on_floor():
		movimento.y=0
	else:
		movimento.y+=GRAVITY



func _on_Area2D_body_entered(body):
    var animation= $AnimatedSprite.get_animation() 
    var frame= $AnimatedSprite.get_frame()
    if  animation=="attack" and frame==2:   
        body.die()

Matteo | 2020-03-30 19:37

Yes, when you’re calling the callback manually, you have to provide a body. However, the callback will also be called automatically once a body enters your Area2D!

Now in your specific case you’re probably better off using get_overlapping_bodies (see here). So once the player presses the “attack”-input, you’re getting an array containing every body inside the Area2D, play the “attack”-animation and when it reaches frame 2, you call the die()-function on all bodies inside that array:

elif Input.is_action_pressed("atttack") and is_on_floor():
    var enemies_in_reach = $Area2D.get_overlapping_bodies()
    yield(get_tree(), "idle_frame")
    $AnimatedSprite.play("attack")
    while $AnimatedSprite.get_frame() != 2:
        continue
    for enemy in enemies_in_reach:
        enemy.die()

In this case the callback _on_Area2D_body_entered is not needed.

njamster | 2020-03-31 09:42

Thank you soooooo much, with that I finally resloved my problems, sorry for all the questions that I did

Matteo | 2020-03-31 12:09