0 votes

Hi!

I'm a beginner Godot engine user and I have a question regarding AnimatedSprite and handling user input with _input().

I'm following through this nice tutorial: https://www.davidepesce.com/2019/10/23/godot-tutorial-8-2d-sprite-animation/

When I tested the game I started to initiate an attack by pressing the space key repeatedly. If I press the space fast enough, sometimes the attack animation is stuck on the last frame. I still can move around, but neither the walk animation nor the idle animation will play anymore.

However, when I press the fireball key (ctrl key), it can transition to the fireball animation and when that's over, the walk and idle animations work again.

Also, this behaviour happens with the fireball animation. If I press ctrl fast enough it will stuck on the last frame and it can only transition forward if I press attack.

In the tutorial, this problem is solved in the 11th part, when we introduce a global cooldown for attack animation and we can't attack (theoretically) infinitely fast.

However, I have a question: I tried to solve the above issue by writing the following if statement before attacking (I check if an attack animation is already going on):

if not attack_playing:

Still, the bug hasn't disappeared from that and I don't understand why. I also tried to debug it with print statements, but I couldn't find anything interesting with that. Animation is stuck, it will not call the animationfinished signal (so attackplaying will not be reset) and walking and idle animations will not play, only with fireball can we transition again (of course in the case of my solution, we can't move forward with the other type of attack because of the if condition...).

I can only think that the attack/fireball animation somehow conflicts with user input. Can anyone help me with this?

I paste my code below (the global cooldown solution from tutorial 11 is commented out in _input()):

extends KinematicBody2D

# Initial value (0,1) to face down
var last_direction = Vector2(0,1)

# Player stats
var health = 50
var health_max = 100
var health_regeneration = 1
var mana = 100
var mana_max = 100
var mana_regeneration = 2

# Player movement speed
export var speed = 75

signal player_stats_changed

# Attack variables
var attack_cooldown_time = 1000
var next_attack_time = 0
var attack_damage = 30

var attack_playing = false


func _ready():
    emit_signal("player_stats_changed", self)


func _process(delta):
    # Regenerates mana
    var new_mana = min(mana + mana_regeneration * delta, mana_max)
    if new_mana != mana:
        mana = new_mana
        emit_signal("player_stats_changed", self)

    # Regenerates health
    var new_health = min(health + health_regeneration * delta, health_max)
    if new_health != health:
        health = new_health
        emit_signal("player_stats_changed", self)


func _physics_process(delta):
    # Get player input
    var direction: Vector2
    direction.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    direction.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")

    # Normalize for directional speed
    if abs(direction.x) == 1 and abs(direction.y) == 1:
        direction = direction.normalized()

    # Apply movement
    var movement = speed * direction * delta
    if attack_playing:
        movement = 0.3 * movement  # when attacking, movement speed is slower
    movement = move_and_collide(movement)

    # Animates player based on direction
    if not attack_playing:
        animates_player(direction)


func animates_player(direction: Vector2):
    if direction != Vector2.ZERO:
        # Update last direction
        last_direction = direction

        var animation = get_animation_direction(last_direction) + "_walk"

        # Play walk animation
        $Sprite.play(animation)
    else:
        # Choose idle animation based on last movement direction and play it
        var animation = get_animation_direction(last_direction) + "_idle"
        $Sprite.play(animation)


func _input(event):
    if not attack_playing:
        if event.is_action_pressed("attack"):
            # Check if player can attack
            #var now = OS.get_ticks_msec()
            #if now >= next_attack_time:
            attack_playing = true
            var animation = get_animation_direction(last_direction) + "_attack"
            $Sprite.play(animation)
            # Add cooldown time to current time
            #next_attack_time = now + attack_cooldown_time
        elif event.is_action_pressed("fireball"):
            if mana >= 25:
                mana = mana - 25
                emit_signal("player_stats_changed", self)
                attack_playing = true
                var animation = get_animation_direction(last_direction) + "_fireball"
                $Sprite.play(animation)


func get_animation_direction(direction: Vector2):
    var norm_direction = direction.normalized()
    if norm_direction.y >= 0.707:
        return "down"
    elif norm_direction.y <= -0.707:
        return "up"
    elif norm_direction.x <= -0.707:
        return "left"
    elif norm_direction.x >= 0.707:
        return "right"

    return "down"


func _on_Sprite_animation_finished():
    attack_playing = false
in Engine by (17 points)

Please log in or register to answer this question.

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.