00:00
00:00
Newgrounds Background Image Theme

LOCKdev just joined the crew!

We need you on the team, too.

Support Newgrounds and get tons of perks for just $2.99!

Create a Free Account and then..

Become a Supporter!

Godot - Attack Animation Not Working

655 Views | 2 Replies
New Topic Respond to this Topic

I am very new to game development and have no experience with coding. I've been following several Godot tutorials and google results to create my script so it might look very cobbled together. It's supposed to be a platformer.


Making my character walk, idle and jump all seems to be working fine, but when I try to Attack it only plays a millisecond of the first frame. Where did I go wrong and how can I fix this? (I'm still learning so if you can provide explanations with your answers to help me better understand things, I'd really appreciate it!)


extends KinematicBody2D

const UP = Vector2(0, -1)
const GRAVITY = 20
const ACCELERATION = 50
const MAX_SPEED = 500
const JUMP_HEIGHT = -700
var motion = Vector2()
var input_vector = Vector2()
var attack_playing = false

func _physics_process(delta):
    motion.y += GRAVITY
    var friction = false
    input_vector = Vector2.ZERO
    
    if Input.is_action_pressed("ui_right"):
        motion.x = min(motion.x+ACCELERATION, MAX_SPEED)
        $Sprite.flip_h = false
        input_vector.x = 1
    elif Input.is_action_pressed("ui_left"):
        motion.x = max(motion.x-ACCELERATION, -MAX_SPEED)
        $Sprite.flip_h = true
        input_vector.x = -1
    else:
        friction = true
    
    if is_on_floor():
        if Input.is_action_just_pressed("ui_up"):
            motion.y = JUMP_HEIGHT
            input_vector.y = 1
        if friction == true:
            motion.x = lerp(motion.x, 0, 0.2)
    else:
        if friction == true:
             motion.x = lerp(motion.x, 0, 0.05)
    
    _attack()
    
func _attack():
    if Input.is_action_just_pressed("ui_accept"):
        $AttackArea/CollisionShape2D.disabled = false
    
    motion = move_and_slide(motion, UP)
    
    _process_animation()
    
    
func _process_animation():
    if is_on_floor():
    #Play the jump animation if jump was just pressed
        if input_vector.y > 0.75:
            $Sprite.play("Jump")
    #Run when on the floor and the input is over 0.75 in either direction:
        elif input_vector.x < -0/75 or input_vector.x > 0.75:
            $Sprite.play("Run")
        else:
            $Sprite.play("Idle")
    else:
            #Play the jump animation if the character is moving up
        if motion.y < 0:
            $Sprite.play("Jump")
        elif motion.y > 0:
            $Sprite.play("Fall")
    if Input.is_action_just_pressed("ui_accept"):
            $Sprite.play("Attack")


func _on_Sprite_animation_finished():
    attack_playing = false


Response to Godot - Attack Animation Not Working 2020-12-31 11:08:40


At 12/30/20 02:17 AM, SomeTinyCritter wrote: I am very new to game development and have no experience with coding. I've been following several Godot tutorials and google results to create my script so it might look very cobbled together. It's supposed to be a platformer.

Making my character walk, idle and jump all seems to be working fine, but when I try to Attack it only plays a millisecond of the first frame. Where did I go wrong and how can I fix this? (I'm still learning so if you can provide explanations with your answers to help me better understand things, I'd really appreciate it!)


My best guess looking at the code and your description is that it's successfully executing the command to play the Attack animation on the frame when you press the attack button, but it's calling _process_animation() every frame and the very next frame will have it start playing a different animation. If you're on the floor idle when you attack, then the very next frame it will go through the _process_animation() logic and say it needs to play the idle animation now, even though the attack animation isn't done yet.


One way to handle that might be the following. You're going to need some way of re-disabling the attack area when the attack finishes with a timer (either as a node or as pure code) or with a check for the attack animation finishing that disables the AttackArea when the attack is done. So after you've done that, you could do something like this at the beginning of the process_animation() function.

func process_animation():
  if not $AttackArea/CollisionShape2D.disabled:
    return
  # The rest of the code

However, I admit that I'm not using sprite animations in my Godot game so I don't know if there are better ways to handle animation control that would be more flexible when you start wanting to add even more game logic. When it comes time to make an animation for the player dying, if you take this approach you'll probably want to add a block between the func process_animation(): line and the if not $AttachArea/CollisionShape2D.disabled: line so the player can still die even if he's in the middle of attacking. And you might or might not want the player to be "frozen" while attacking so he's not able to jump or run while the attack is playing; if you want him to be able to move then you might want to take a different approach than just using what I showed and completely overriding any other animation while the attack is still going on. But having other controls and animations locked during the attack is a pretty standard approach.


My newsfeed has random GameDev tips & tricks

Response to Godot - Attack Animation Not Working 2021-01-03 22:11:39


At 12/31/20 11:08 AM, 3p0ch wrote: My best guess looking at the code and your description is that it's successfully executing the command to play the Attack animation on the frame when you press the attack button, but it's calling _process_animation() every frame and the very next frame will have it start playing a different animation. If you're on the floor idle when you attack, then the very next frame it will go through the _process_animation() logic and say it needs to play the idle animation now, even though the attack animation isn't done yet.

One way to handle that might be the following. You're going to need some way of re-disabling the attack area when the attack finishes with a timer (either as a node or as pure code) or with a check for the attack animation finishing that disables the AttackArea when the attack is done. So after you've done that, you could do something like this at the beginning of the process_animation() function.
However, I admit that I'm not using sprite animations in my Godot game so I don't know if there are better ways to handle animation control that would be more flexible when you start wanting to add even more game logic. When it comes time to make an animation for the player dying, if you take this approach you'll probably want to add a block between the func process_animation(): line and the if not $AttachArea/CollisionShape2D.disabled: line so the player can still die even if he's in the middle of attacking. And you might or might not want the player to be "frozen" while attacking so he's not able to jump or run while the attack is playing; if you want him to be able to move then you might want to take a different approach than just using what I showed and completely overriding any other animation while the attack is still going on. But having other controls and animations locked during the attack is a pretty standard approach.


Thank you very much for your response!! And thanks for expanding on your answer! I tried out the code you provided but it didn't work for me. I think the _process_animation approach might have complicated things a bit much. I found that there's apparently Nodes called an AnimationTree and AnimationPlayer in Godot that can help keep all the animations together. I tried it out by adding everything to do with state_machine into my script and it worked. Also thanks for letting me know about stopping the attack animation when the player is hurt/dies ^-^ I'll keep figuring things out!


I'll post the tutorials I followed here for anyone else having trouble making a 2D platformer in Godot:

Do this first: http://kidscancode.org/godot_recipes/animation/spritesheet_animation/

Then this: https://www.youtube.com/watch?v=0bq2OIjHxk4


And my script which ended up working:

extends KinematicBody2D

const UP = Vector2(0, -1)
const GRAVITY = 20
const ACCELERATION = 50
const MAX_SPEED = 500
const JUMP_HEIGHT = -700
var motion = Vector2()
var state_machine
var run_speed = 80
var velocity = Vector2.ZERO

func _ready():
    state_machine = $AnimationTree.get("parameters/playback")

func _physics_process(delta):
    motion.y += GRAVITY
    
    get_input()
    
func get_input():
    var friction = false
    var current = state_machine.get_current_node()
    velocity = Vector2.ZERO
    if Input.is_action_just_pressed("ui_accept"):
        state_machine.travel("Attack")
        return
    if Input.is_action_pressed("ui_right"):
        motion.x = min(motion.x+ACCELERATION, MAX_SPEED)
        velocity.x += 1
        $Sprite.flip_h = false
    elif Input.is_action_pressed("ui_left"):
        motion.x = max(motion.x-ACCELERATION, -MAX_SPEED)
        velocity.x -= 1
        $Sprite.flip_h = true
    else:
        friction = true
        
    if velocity.length() == 0:
        state_machine.travel("Idle")
    if velocity.length() > 0:
        state_machine.travel("Run")
    
    if is_on_floor():
        if Input.is_action_just_pressed("ui_up"):
            state_machine.travel("Jump")
            motion.y = JUMP_HEIGHT
        if friction == true:
            motion.x = lerp(motion.x, 0, 0.2)
    else:
        if friction == true:
             motion.x = lerp(motion.x, 0, 0.05)
            
    motion = move_and_slide(motion, UP)


My next goal is to get the collision working for the attack, but this should solve the attack animation issue!