This site is currently in read-only mode during migration to a new platform.
You cannot post questions, answers or comments, as they would be lost during the migration otherwise.
0 votes

Hi, im trying to create an AI for my enemis and I can't figure out why the animations are overriding. This is the only problem (i think ).

This is the code:

extends KinematicBody2D

export var acceleration = 300
export var max_speed = 80
export var friction = 200
export var wander_range = 4 
export(String) var objectID = "IceBoss"

enum {
    IDLE,
    WANDER,
    CHASE,
    ATTACK
}

var state = CHASE
var velocity = Vector2.ZERO
var knockback = Vector2.ZERO
var body_aux

onready var stats = $Stats
onready var playerDectectionZone = $PlayerDetectionZone
onready var hurtBox = $HurtBox
onready var softCollision = $SoftColliosion
onready var wanderController = $WanderController
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")

var follow = true
signal death(value)

func _ready():
    stats.damage = 10
    state = pick_random_state([IDLE, WANDER])

func _physics_process(delta):
    knockback = knockback.move_toward(Vector2.ZERO, friction*delta)
    knockback = move_and_slide(knockback)

    if velocity != Vector2.ZERO:
    #animationTree.set("parameters/Idle/blend_position", velocity)
    #animationTree.set("parameters/1HandAttack/blend_position", velocity)
    #animationTree.set("parameters/2HandAttack/blend_position", velocity)
    #animationTree.set("parameters/Walk/blend_position", velocity)
    animationTree.set("parameters/Death/blend_position", velocity)
    match state:
        IDLE:
            animationTree.set("parameters/Idle/blend_position", velocity)
            animationState.travel("Idle")
            velocity = velocity.move_toward(Vector2.ZERO, friction*delta)
            seek_player()
            if wanderController.get_time_left() == 0:
                update_wander()

        WANDER: 
            seek_player()
            if wanderController.get_time_left() == 0:
                update_wander()
            accelerate_towards_point(wanderController.target_position, delta)
            animationState.travel("Walk")
            if global_position.distance_to(wanderController.target_position) <= wander_range:
                update_wander()
        CHASE:
            #print(velocity)
            var player = playerDectectionZone.player
            if player != null:
                accelerate_towards_point(player.global_position, delta)
                animationState.travel("Walk")
            else:
                state = IDLE
        ATTACK:
            attack_state()

    if softCollision.is_colliding():
        velocity += softCollision.get_push_vector() * delta * 400 
    if stats.health > 0:
        velocity = move_and_slide(velocity)

func attack_state():
    #var array = ["1HandAttack", "2HandAttack"]
    #var rand_attack = array[randi() % array.size()]
    animationState.travel("2HandAttack")
    velocity = Vector2.ZERO

var position_after_attack

func accelerate_towards_point(point, delta):
    var direction = global_position.direction_to(point)

    if playerDectectionZone.player != null:
        var distance =  global_position.distance_to(playerDectectionZone.player.global_position)
        if distance > 42:
            velocity = velocity.move_toward(max_speed * direction, acceleration * delta)
            animationTree.set("parameters/Walk/blend_position", velocity)
            animationState.travel("Walk")
    #elif distance < 38:
    #velocity = velocity.move_toward(max_speed * direction * (-1), acceleration * delta)
        else:
            setParameters()
            state = ATTACK

func attack_animation_ended():
    state = IDLE

func seek_player():
    if playerDectectionZone.can_see_player():
        state = CHASE
    else:
        state = IDLE

func update_wander():
    state = pick_random_state([IDLE, WANDER])
    wanderController.set_wander_timer(rand_range(1, 3))

func pick_random_state(state_list):
    state_list.shuffle()
    return state_list.pop_front()

func _on_HurtBox_invincibility_started():
    pass # Replace with function body.


func _on_HurtBox_invincibility_ended():
    pass # Replace with function body.


func _on_Stats_no_health():
    animationState.travel("Death")
    state = 10
    call_deferred("disabled")
    emit_signal("death", objectID)
    $Timer.start(2)

func disabled():
    $Position2D/HitBox/CollisionShape2D.disabled = true
    $HurtBox/CollisionShape2D.disabled = true
    #$Area2D/CollisionShape2D.disabled = true
    #disconnect("_on_Area2D_body_entered", self, "disabled")

func _on_HurtBox_area_entered(area):
    stats.health -= area.damage
    knockback = area.knockback_vector * 120
    hurtBox.create_hit_effect()
    hurtBox.start_invincibility(0.4)


func _on_Timer_timeout():
    queue_free()


func _on_BackAttack_body_entered(_body):
    setParameters()
    state = ATTACK


func _on_BackAttack_body_exited(_body):
    seek_player()


func _on_FrontAttack_body_entered(_body):
    setParameters()
    state = ATTACK


func _on_FrontAttack_body_exited(_body):
    seek_player()


func setParameters():
    var poz = Vector2.ZERO
    if global_position != null && playerDectectionZone.player != null:
        poz = playerDectectionZone.player.global_position - global_position
    animationTree.set("parameters/1HandAttack/blend_position", poz)
    animationTree.set("parameters/2HandAttack/blend_position", poz)


func _on_LeftAttack_body_entered(_body):
    setParameters()
    state = ATTACK


func _on_LeftAttack_body_exited(_body):
    seek_player()


func _on_RightAttack_body_entered(_body):
    setParameters()
    state = ATTACK

func _on_RightAttack_body_exited(_body):
    seek_player()

This is how the boss scene looks: https://imgur.com/a/d0iiM67
This is how the animations attack looks like: https://imgur.com/a/pmM3hh5
And this is the overriding: https://youtu.be/27mwAqtgCdg

Godot version 3.0
in Engine by (24 points)

1 Answer

+1 vote
Best answer

There is an animation finished signal which you can use. If you dont allow the state to change until the animation finished signal is received this should stop the animations from overriding each other.

by (3,328 points)
selected by

How can I do that?
Is ok to make a yield("animationPlayer", "animation_finished") everytime a change my state or?

That is one way but you would probably need to make a few changes to your code then to implement that.

I would probably add a boolean variable and set it to true initially then where you have code to change the state add a line

    if new_boolean:
        new_boolean = false
        #do your state change here

then you can have a single function connected to the signal where you have

func _animation_finished():
    new_boolean = true

that way when you set a state change you will set the boolean to false so no other state change can happen and that gets reset to true when the animation is finished.

Depends on your game though your function would work better if the animation requests are not going to get called a second time but judging from the error you are getting it looks pretty clear that they are being called repeatedly.

Thanks for ideea, i did it. My code is a mess but i fix it.
I really appreciate it

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.