idle animation overrides attack animation

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

I have a problem with animation overriding… my question is how to fix idle animation overriding attack animation. other animations like running and jumping works well, but the attack animation only plays first frame and goes back to idle animation. I know it’s because idle animation doesn’t stop while attack is pressed but I can’t find how to fix it. please help me

extends KinematicBody2D

const UP = Vector2(0, -1)
const gravity = 15
const speed = 100
const jump = -300
const accel = 20

var motion = Vector2()
var facing_right = true
var can_idle = true

func _physics_process(_delta):

motion.y += gravity
var friction = false

motion.x = clamp(motion.x, -speed, speed)

if facing_right == true:
	$Sprite.scale.x = 1
else:
	$Sprite.scale.x = -1

if Input.is_action_pressed("right") && can_idle == true:
	motion.x += accel
	facing_right = true
	$AnimationPlayer.play("run")
	
elif Input.is_action_pressed("left") && can_idle == true:
	motion.x -= accel
	facing_right = false
	$AnimationPlayer.play("run")
else:
	if can_idle == true:
		$AnimationPlayer.play("idle")
		motion.x = lerp(motion.x, 0, 0.2)
if is_on_floor():
	if Input.is_action_pressed("jump"):
		motion.y = jump
	if friction == true:
		motion.x = lerp(motion.x, 0, 0.2)
else:
	if friction == true:
		motion.x = lerp(motion.x, 0, 0.05)
	if motion.y < 0:
		$AnimationPlayer.play("jump")
	elif motion.y > 0:
		$AnimationPlayer.play("fall")

if Input.is_action_just_pressed("attack"):
	can_idle == false
	$AnimationPlayer.play("attack")
	yield($AnimationPlayer, "animation_finished")
	can_idle == true



motion = move_and_slide(motion, UP)
:bust_in_silhouette: Reply From: bloodsign

You can go to your AnimationPlayer node. On the right panel of Godot Editor, right next to the [Inspector] Tab, there’s a [Node] Tab.

You should see a list of Signals under there. If you select AnimationPlayer node on your scene tree, you will see a signal on that list named:

animation_finished(anim_name:String)

Double click on it to open a new window and connect it to your script on your scene tree.

It should create a function inside your script.

   func _on_AnimationPlayer_animation_finished(anim_name):
       pass

This function gets called everytime an animation is finished (if it’s not on loop- I think)

from there you can do something like:

 func _on_AnimationPlayer_animation_finished(anim_name):
       match anim_name:
              "attack":
                       can_idle = true

#EDIT: On second note,

if Input.is_action_just_pressed("attack"):
    can_idle == false
    $AnimationPlayer.play("attack")
    yield($AnimationPlayer, "animation_finished")
    can_idle == true

Why is your can_idle line of code using double equals? That’s for comparing and not assigning.
Shouldn’t it be:

can_idle = false
can_idle = true

Thanks for helping me! I solved it by doing what you said. But I don’t understand why that match thing on func _on_AnimationPlayer_animation_finished.can solve overriding problems.

Edit: it is not about what I want, but what if I want to attack while I am not on the floor? (in other words, while in the air) When I attack in the air, jumping motion overrides attacking motion( now I blocked attacking while in the air.).

Here is my code that I changed some:

extends KinematicBody2D

const UP = Vector2(0, -1)
const gravity = 15
const speed = 100
const jump = -300
const accel = 20

var motion = Vector2()
var facing_right = true
var can_idle = true

func _on_AnimationPlayer_animation_finished(anim_name):
match anim_name:
“attack”:
can_idle = true

func _physics_process(_delta):

motion.y += gravity
var friction = false

motion.x = clamp(motion.x, -speed, speed)

if facing_right == true:
	$Sprite.scale.x = 1
else:
	$Sprite.scale.x = -1

if Input.is_action_pressed("right") && can_idle == true:
	motion.x += accel
	facing_right = true
	$AnimationPlayer.play("run")
	
elif Input.is_action_pressed("left") && can_idle == true:
	motion.x -= accel
	facing_right = false
	$AnimationPlayer.play("run")
else:
	if can_idle == true:
		$AnimationPlayer.play("idle")
		motion.x = lerp(motion.x, 0, 0.2)
if is_on_floor():
	
	if Input.is_action_pressed("jump"):
		motion.y = jump
	if Input.is_action_just_pressed("attack"):
		can_idle = false
		$AnimationPlayer.play("attack")
		motion.x = lerp(motion.x, 0, 0.95)
		yield($AnimationPlayer, "animation_finished")
		can_idle = true

else:
	if motion.y < 0:
		$AnimationPlayer.play("jump")
	elif motion.y > 0:
		$AnimationPlayer.play("fall")
	

motion = move_and_slide(motion, UP)

acola1234 | 2021-02-12 01:43

hmm, I would suggest you use an enum of states to keep track of what your player’s current state is. Or at least after you have finished working on your movement logic(refactor it)

At any rate, regarding the jumping animation overriding your attack animation:

if is_on_floor():
    if Input.is_action_pressed("jump"):
        motion.y = jump
    if Input.is_action_just_pressed("attack"):
        can_idle = false
        $AnimationPlayer.play("attack")
        motion.x = lerp(motion.x, 0, 0.95)
        yield($AnimationPlayer, "animation_finished")
        can_idle = true

else:
    if motion.y < 0:
        $AnimationPlayer.play("jump")
    elif motion.y > 0:
        $AnimationPlayer.play("fall")

I think part of the reason is simply because on this line of code:

if is_on_floor():
    if Input.is_action_pressed("jump"):
        motion.y = jump

Once your motion.y changed or rather your player’s contact on the floor is broken (on the third line of this code snippet), the logic jumps towards the else:

else:
    if motion.y < 0:
        $AnimationPlayer.play("jump")
    elif motion.y > 0:
        $AnimationPlayer.play("fall")

Thus, your action input animation is overridden since the logic is no longer on the is_on_floor() block(since your player is now off the ground).

There are several ways you could go about this, but a quick suggestion would be to separate the logic of attacking from your jumping. In this way, you should be able to attack in any state(jumping, falling, on_floor)
rather than putting your attack logic inside is_on_floor() block.

By any chance were you trying to achieve something similar to this?

Imgur: Godot Jumping & Attacking GIF

Here’s a quick approach I just tried right now:
Disclaimer: it is by no means an elegant approach (embarrassingly sits in a corner)

var jump_force = 300
var jump_delay = 0.1
var jump_time_lapsed = 0
var jumping = false
onready var anim = $AnimationPlayer

func _physics_process(delta):
	motion.y += gravity
	if Input.is_action_just_pressed("ui_up"):
		if is_on_floor():
			jumping = true
	if jumping:
		jump_time_lapsed += delta 
#don't jump yet, since in my animation there's a  wind up/squat/squash 
#before jumping.
		anim.play("jump")                 
		if (jump_time_lapsed>jump_delay):
			motion.y -= jump_force
			jumping = false

	if Input.is_action_just_pressed("ui_accept"):
		anim.play("attack")
	motion = move_and_slide(motion,Vector2.UP)

func _on_AnimationPlayer_animation_finished(anim_name):
	match anim_name:
		"jump":
			jump_time_lapsed = 0
			anim.play("idle")

Although, I would highly suggest to start using states since it would come handy in the long run.

bloodsign | 2021-02-12 10:06

I saw it too late… I really appreciate your helps and suggestions!! I’ll try what you suggested right away! Thanks for your helps again

acola1234 | 2021-02-14 10:14