Idle animation is overriding attack animation

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

I have this script that is working fine, except for the attack animation which I believe is being overridden by the idle or running animations. The attack animation is only showing the first frame every time I press Ctrl and then goes to idle or run animations. Any tips on how to fix this?

extends KinematicBody2D

onready var ground_ray = get_node("ground_ray")
onready var ground_ray2 = get_node("ground_ray2")
onready var anim = get_node("AnimationPlayer")

const UP = Vector2(0, -1)
const GRAVITY = 10
const ACCELERATION = 50
const MAX_SPEED = 180
const JUMP_HEIGHT = -380

var motion = Vector2()
var acc = Vector2()

func _physics_process(delta):
	motion.y += GRAVITY
	var friction = false
	
	acc.x = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
	
	if acc.x == 1:
		$Sprite.flip_h = false
		motion.x = min(motion.x+ACCELERATION, MAX_SPEED)
		if anim.current_animation != "run" && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
    		anim.play("run")		
	elif acc.x == -1:
		$Sprite.flip_h = true
		motion.x = max(motion.x-ACCELERATION, -MAX_SPEED)
		if anim.current_animation != "run" && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
			anim.play("run")
	else:
		if anim.current_animation != "idle" && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
			anim.play("idle")
		friction = true
				
	
	if Input.is_action_just_pressed("ui_up") && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
		motion.y = JUMP_HEIGHT
	
	if (ground_ray.is_colliding() || ground_ray2.is_colliding()):
		if friction == true:
			motion.x = lerp(motion.x, 0, 0.2)	
		if Input.is_action_just_pressed("ui_ctrl"):
			if anim.current_animation != "combo 1":
				anim.play("combo 1")
	else:
		if motion.y <= JUMP_HEIGHT*0.45:
			if anim.current_animation != "jump":
				anim.play("jump")
		if motion.y >= 0:
			motion.y += 8
		if motion.y >= JUMP_HEIGHT*0.45 && motion.y <= JUMP_HEIGHT*(-0.45): 
			if anim.current_animation != "maxHeight":
				anim.play("maxHeight")
		if motion.y > JUMP_HEIGHT*(-0.45):
			if anim.current_animation != "fall":
				anim.play("fall")
		if friction == true:
			motion.x = lerp(motion.x, 0, 0.2)
	motion = move_and_slide(motion, UP)

I think the problem is that after you attacked, the next time you enter physics process you will met other cases and another animation will start. For example, you press control, so play attack animation, but in next physics process call process call, idle conditions met, so idle is run.

Another thing, Have you tried changing all this:

if Input.is_action_just_pressed("ui_ctrl"):
        if anim.current_animation != "combo 1":
            anim.play("combo 1")

for this:

 if Input.is_action_just_pressed("ui_ctrl"):
     anim.current_animation = "combo 1"

You can set animations like that so you don’t have to check if animation is already being played.

For your problem, i think you should ad abool variable like is_attacking which you set to true when start the attack animation, and set to false when finish. If is attacking, then don’t try to change animation.

p7f | 2019-01-21 12:00

Hi. I set the flag attack and changed the if statements, the combo animation works now, but can be stopped at any time. Do you know how I can prevent “combo 1” animation from being interrupted? Any Animation Player command? Thank you for your response.

arthurZ | 2019-01-21 15:18

Can you share me the project so i see in depth? I would use tha atack flag to prevent any other animations to be loaded, and connect animation finished signal to a function that, if the animation finished was the attack, then se attack flag to false.

p7f | 2019-01-21 15:23

Here are the last changes. The combo 1 animation is playing almost well, the only problem is that if I press Ctrl again it restart the combo 1 animation.

extends KinematicBody2D

onready var ground_ray = get_node("ground_ray")
onready var ground_ray2 = get_node("ground_ray2")
onready var anim = get_node("AnimationPlayer")

const UP = Vector2(0, -1)
const GRAVITY = 10
const ACCELERATION = 50
const MAX_SPEED = 180
const JUMP_HEIGHT = -380

var motion = Vector2()
var acc = Vector2()

var attack = false

func _physics_process(delta):
	motion.y += GRAVITY
	var friction = false
	
	acc.x = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
	
	if acc.x == 1 && !attack:
		$Sprite.flip_h = false
		motion.x = min(motion.x+ACCELERATION, MAX_SPEED)
		if anim.current_animation != "run" && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
    		anim.play("run")		
	elif acc.x == -1 && !attack:
		$Sprite.flip_h = true
		motion.x = max(motion.x-ACCELERATION, -MAX_SPEED)
		if anim.current_animation != "run" && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
			anim.play("run")
	else:
		if anim.current_animation != "idle" && !attack && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
			anim.play("idle")
		friction = true
				
	
	if Input.is_action_just_pressed("ui_up") && anim.current_animation != "combo 1" && (ground_ray.is_colliding() || ground_ray2.is_colliding()):
		motion.y = JUMP_HEIGHT
	
	if (ground_ray.is_colliding() || ground_ray2.is_colliding()):
		if friction == true:
			motion.x = lerp(motion.x, 0, 0.2)	
		if Input.is_action_just_pressed("ui_ctrl"):
			attack = true
			anim.play("combo 1")
			yield(anim, "animation_finished")
			attack = false
	else:
		if motion.y <= JUMP_HEIGHT*0.45:
			if anim.current_animation != "jump":
				anim.play("jump")
		if motion.y >= 0:
			motion.y += 8
		if motion.y >= JUMP_HEIGHT*0.45 && motion.y <= JUMP_HEIGHT*(-0.45): 
			if anim.current_animation != "maxHeight":
				anim.play("maxHeight")
		if motion.y > JUMP_HEIGHT*(-0.45):
			if anim.current_animation != "fall":
				anim.play("fall")
		if friction == true:
			motion.x = lerp(motion.x, 0, 0.2)
	motion = move_and_slide(motion, UP)

arthurZ | 2019-01-21 17:34

hi:
Change anim.play("combo 1") by `anim.current_animation = “combo 1”. Have you tried that?
If not you can check current animation as you did before.

p7f | 2019-01-21 17:41

This worked very well. Do you think yield works as good as signal to notified about finished animation? Thanks again man.

if Input.is_action_just_pressed("ui_ctrl"):
    attack = true
    anim.current_animation = "combo 1"
    yield(anim, "animation_finished")
    attack = false

arthurZ | 2019-01-21 17:57

I think in this case yield is better! We should post an answer so this does not remain unsolved, and then you should select it. Would you do it, so you can add the finished version of the code? Or you prefer me to do so?

p7f | 2019-01-21 18:01

You can make it. Thanks.

arthurZ | 2019-01-21 18:12

hey, i think that if you use yield, you don’t need attack flag anymore… can you please test it?

p7f | 2019-01-21 18:15

I test it, and without attack flag we comeback to the first issue, the combo 1 animation just plays the first frame and the idle or run animation overrides it. I just don’t understand why it overrides if I am using the anim.current_animation = "combo 1" to avoid it.

arthurZ | 2019-01-21 18:44

:bust_in_silhouette: Reply From: p7f

Hi,
This was solved in comments, but here is a summary of what we got working.

  • anim.pay("combo 1") was changed to anim.current_animation = "combo 1" so the animation didn’t restart when pressed Ctrl repeatedly.

  • yield was used when the attack animation started, so that no other actions can be done while character is attacking.

For further info, se comments above.