0 votes

Hello Godot community. I am making 2D platformer.
I have made an enemy which has four different states (Idle, Chase, Attack, Death)
Enemy has PlayerDetector Area2D and Hitbox Area2D. Once Player entered PlayerDetector Area2D, Enemy will be in Chase State. As soon as Player exited PlayerDetector Area2D, Enemy will return to Idle State. If player entered Hitbox Area2D, Enemy will be in Attack State. What I would like to do is, when one enemy is in Attack State, change all other duplicated enemies to Idle State. How could I achieve this? I would appreciate if you could kindly teach me.

What I have tried so far:
① Disable Player's PlayerDetector Area2D, so enemy will no longer be in Chase State.
But this caused a problem that all of the enemies will be in Idle State, as soon as Player's playerDetector Area2D is disabled, since it will trigger Enemy's PlayerDetector Area2D exited function.

② send a signal has_player == true and enable enemy's Chase State only if has_player == false. This funnily worked only if duplicated enemies are in Idle State. If enemies are already in Chase State, they would not return to Idle State.

Enemy Script:

extends KinematicBody2D

var max_hp = 100
var current_hp= max_hp
var defense = 0

var speed = 100
var chase_speed = 170
var gravity = 800
var direction = 1
var velocity = Vector2.ZERO

var has_player = false
var has_trigger = false

onready var _animation_player: AnimationPlayer = $Position2D/CatSkin/AnimationPlayer
onready var sprite = get_node("Position2D/CatSkin")
onready var flashTimer = $flashTimer
onready var player = get_node("../Player")
onready var EnemyCollision = $EnemyCollision
onready var label = $Label
onready var bullet = get_node("../Bullet02")

signal button_mashing_start()


enum state {IDLE, CHASE, GRAB, FINISH, DEATH}
var enemy_state = state.IDLE


func _ready():
    enemy_state = state.IDLE
    $Position2D/CatSkin/AnimationPlayer.connect("animation_finisehd", self, "on_animation_finished")
    Autoload.connect("button_mashing_cleared", self, "button_mashing_cleared_signal_recieved")
    Autoload.connect("button_mashing_failed", self, "button_mashing_failed_signal_recieved")
    Autoload.connect("has_triggered", self, "has_triggered_signal_recieved")
    current_hp = max_hp
    pass

func _physics_process(delta):

    match enemy_state:
        state.IDLE:
            label.text="IDLE"
            _animation_player.play("Cat Idle")
            if velocity.x>0:
                $Position2D.scale.x=1
            elif velocity.x<0:
                $Position2D.scale.x=-1

            velocity.y += gravity * delta
            velocity.x = speed * direction

            if EnemyCollision.is_colliding():
                velocity += EnemyCollision.get_push_vector() * delta * 100
            velocity = move_and_slide(velocity, Vector2.UP)

            if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
                direction = direction * -1
                flip()

                velocity.y += gravity * delta
                velocity.x = speed * direction

        state.CHASE:
            label.text="CHASE"
            _animation_player.play("Cat Chase")

            if velocity.x>0:
                $Position2D.scale.x=1
            elif velocity.x<0:
                $Position2D.scale.x=-1

            velocity = Vector2(sign(player.global_position.x - global_position.x), 0).normalized()


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

            if EnemyCollision.is_colliding():
                velocity += EnemyCollision.get_push_vector() * delta * 100
            velocity = move_and_slide(velocity * chase_speed)

        state.GRAB:
            label.text="GRAB01"
            _animation_player.play("Cat Grab Attack")

            velocity.y = 0
            velocity.x = 0 * direction 
            velocity = move_and_slide(velocity, Vector2.UP)
            emit_signal("button_mashing_start")

        state.FINISH:
            label.text="GRAB02"
            _animation_player.play("Cat Grab Attack 02")

            velocity.y = 0
            velocity.x = 0 * direction
            velocity = move_and_slide(velocity, Vector2.UP)

        state.DEATH:
            label.text="DEATH"
            _animation_player.play("Cat Death")

            has_player = true
            velocity.x = 0 * direction 
            velocity = move_and_slide(velocity, Vector2.UP)
            Autoload.emit_signal("screen_shake")
            $HitboxCat/CollisionShape2D.set_deferred("disabled", true)
            $HitboxCat/CollisionShape2D2.set_deferred("disabled", true)
            $HurtboxCat/CollisionShape2D.set_deferred("disabled", true)
            $HurtboxCat/CollisionShape2D2.set_deferred("disabled", true)





func _on_DeathSound_finished():
    queue_free()


func _on_AnimationPlayer_animation_finished(anim_name):
    print("Grab02 End!")
    Autoload.emit_signal("grab_finished", self)
    enemy_state = state.CHASE
    has_player = false

func flip(): #Flip Sprite
    if direction>0:
        $Position2D.scale.x=1
    elif direction<0:
        $Position2D.scale.x=-1


func _on_HurtboxCat_area_entered(area):
    if area.name == "Bullet02":
        print("I got hit!")
        $HitSound.play()

        var base_damage = area.damage
        self.current_hp -= base_damage
        if self.current_hp <= 0:
            print("IM DEAD!")
            enemy_state = state.DEATH
            $DeathSound.play()

func _on_HitboxCat_area_entered(area):
        if area.name == "HurtboxPlayer":
                enemy_state = state.GRAB
                print("Grab01!")
                pass


func _on_PlayerDetector_area_entered(area: Area2D): #Player Search Area. Chase when Player entered
    if area.name == "PlayerDetector":
        if has_player == false:
            print("found you !")
            enemy_state = state.CHASE
        else:
            enemy_state = state.IDLE

func _on_PlayerDetector_area_exited(area: Area2D): #Player Search Area. Stop Chase when Player exited
    if area.name == "PlayerDetector":
        if has_player == false:
            print("lost you !")
            enemy_state = state.IDLE

func has_triggered_signal_recieved():
    has_player = true
    print("has_player=false")
Godot version ver3.3.3
in Engine by (65 points)

1 Answer

+1 vote
Best answer

if all the duplicate enemies are under one parent node then once one enemy enters the chase state you can call a function that does something like this.

 for i in $Enemyholder.get_children: ## getting all the enemies
      if i.state != chase: ## excluding the enemy that is chasing player
            i.state = idle #### setting the state to idle and you can maybe disable collision of the enemy that are supposed to be idle here too
by (709 points)
selected by

Thank you Asthmar.
I tried as you suggested, but I receive an error "Invalid get index 'get_children' (on base: 'null instance')"

Never mind. I think I solved this.
I used autoload. I made enemy to emit signal("hasplayer") when collided with player. Enemy will then receive signal("hasplayer") and do if enemy_state != state.Attack: disable PlayerDetector Area2D. And enabled PlayerDetector Area2D when state.Attack finished.

Thank you so much Asthmar for the help. much appreciated

You're welcome, Glad you found a solution! Also, I think I see why my code didn't work I forgot parentheses after getchildren it should have been getchildren().

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.