0 votes

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 facingright = true
var can
idle = true

func physicsprocess(_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)
Godot version 3.2.3
in Engine by (15 points)

1 Answer

+1 vote
Best answer

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
by (334 points)
selected by

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 facingright = true
var can
idle = true

func onAnimationPlayeranimationfinished(animname):
match anim
name:
"attack":
can_idle = true

func physicsprocess(_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)

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.

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

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 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 webmaster@godotengine.org with your username.