Animation override

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

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: Imgur: The magic of the Internet
This is how the animations attack looks like: Imgur: The magic of the Internet
And this is the overriding: https://youtu.be/27mwAqtgCdg

:bust_in_silhouette: Reply From: Gluon

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.

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

ispilantebrusli | 2022-02-11 12:14

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.

Gluon | 2022-02-11 13:26

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

ispilantebrusli | 2022-02-12 01:42