Animation only plays the first frame when button pressed

Godot Version

4.2.2

Question

Hey! I am trying to make an attack function, where if the player presses “E” (“attack”), the character plays an animation. However, whenever I press “E”, it only plays the first frame.

Here is the code (if something is missing, then please let me know):

@onready var animated_sprite = $AnimatedSprite2D

func _physics_process(delta):
	attacking = Input.is_action_pressed("attack")
	
	if attacking:
			animated_sprite.play("attack")

Do you have other animations that the animated_sprite plays at some point? Also you should call Input.is_action_just_pressed and not is_action_pressed as this will only get called once, while is_action_pressed gets called the whole time you press the button down

I just changed it to attacking = Input.is_action_just_pressed("attack") - nothing changed.

Yes, there are other animations. Just like the “walking” and “idle” animations. All of these are in the func _physics_process(delta):

Then its very likely that your animation gets “overriden” by the other animations. Can you show the whole code so i can help debugging

Yes, here it is please ignore the random comments, last night was a long night :,D:

extends CharacterBody2D


const SPEED = 100.0
const JUMP_VELOCITY = -250.0
var playerhealth = 100 # Player's health
var playerdamage = 10 # Player's damage
#var enemyattacking = false # Enemy status
var attacking = false # Player status
#@export var enemydamage =


# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

@onready var animated_sprite = $AnimatedSprite2D
#@onready var enemy = $"../enemy1"
@onready var enemy = get_node("../enemy1")  # The path in the tree, not the file system.


func _physics_process(delta):
	#print(health)
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta

	# Handle jump.
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY
		
	var direction = Input.get_axis("left", "right")
	if direction > 0:
		animated_sprite.flip_h = false
	elif direction < 0:
		animated_sprite.flip_h = true
		
	if direction == 0:
		animated_sprite.play("idle")
	else:
		animated_sprite.play("e-walk")
	
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()
	
	attacking = Input.is_action_just_pressed("attack")
	

	 #Start the attack animation when the attack button is pressed
	if attacking:
		animated_sprite.play("attack")
		
#func _input(event):
	#if event.is_action_pressed("attack"):
		#animated_sprite.play("attack")

func _on_area_2d_body_entered(body):
	if body.name == "enemy1": # If the entered body is an enemy's then:
		print("Body entered: ", body.name) 
		if attacking:
			body.enemyhealth -= playerdamage
			print("Enemy health: ", body.enemyhealth)
		# this allows the player to take damage from the enemy
		body.enemyattacking = true
		playerhealth -= body.enemydamage
		
		print("Player health: ", playerhealth)
		if playerhealth <= 0:
			get_tree().reload_current_scene()

func _on_area_2d_body_exited(body):
	if body == enemy:
		enemy.enemyattacking = false

Well: Do you want the ability to move during your attack? If no:
you would have to do something similar to this:

var is_attacking: bool = false

func _physics_process(delta) -> void:
    if not_is_on_floor():
        velocity.y += gravity * delta
    
    if is_attacking:
        return
    ...
    ...
    if attacking:
        animated_sprite.play("attack")
        is_attacking = true

func _on_animation_finished(animation) -> void:
    if animation == "attack":
        is_attacking = false

And make sure to connect the animation_finished signal of the animated_sprite to the _on_animation_finished() method. Another way to determine when your attack is done is, to use a timer

I might be doing something wrong here. It still doesn’t work… I still kinda new to this, so I am sorry if, I don’t notice/understand something right away.

Code

extends CharacterBody2D


const SPEED = 100.0
const JUMP_VELOCITY = -250.0
var playerhealth = 100 # Player's health
var playerdamage = 10 # Player's damage
#var enemyattacking = false # Enemy status
var attacking = false # Player status
#@export var enemydamage =
var is_attacking: bool = false


# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

@onready var animated_sprite = $AnimatedSprite2D
#@onready var enemy = $"../enemy1"
@onready var enemy = get_node("../enemy1")  # The path in the tree, not the file system.


func _physics_process(delta) -> void:
	#print(health)
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta

	# Handle jump.
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY
		
	var direction = Input.get_axis("left", "right")
	if direction > 0:
		animated_sprite.flip_h = false
	elif direction < 0:
		animated_sprite.flip_h = true
		
	if direction == 0:
		animated_sprite.play("idle")
	else:
		animated_sprite.play("e-walk")
	
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()
	
	attacking = Input.is_action_just_pressed("attack")
	

	 #Start the attack animation when the attack button is pressed
	if attacking:
		animated_sprite.play("attack")
		is_attacking = true
		
func _on_animation_finished(animation) -> void:
	if animation == "attack":
		is_attacking = false
		animated_sprite.animation_finished
#func _input(event):
	#if event.is_action_pressed("attack"):
		#animated_sprite.play("attack")

func _on_area_2d_body_entered(body):
	if body.name == "enemy1": # If the entered body is an enemy's then:
		print("Body entered: ", body.name) 
		if attacking:
			body.enemyhealth -= playerdamage
			print("Enemy health: ", body.enemyhealth)
		# this allows the player to take damage from the enemy
		body.enemyattacking = true
		playerhealth -= body.enemydamage
		
		print("Player health: ", playerhealth)
		if playerhealth <= 0:
			get_tree().reload_current_scene()

func _on_area_2d_body_exited(body):
	if body == enemy:
		enemy.enemyattacking = false

after you check if the player is_on_floor() you have to check if he is currently attacking:

if is_attacking:
        return

And btw i like the use of the _input method more (which you commented out):

func _input(event):
    if event.is_action_just_pressed("attack"):
        is_attacking = true
        animated_sprite.play("attack")

In this case you dont need to check if attacking is pressed in the physics_process method

EDIT: the return keyword stops the method

Now the animation plays, but it doesn’t stop. I tried turning off the “animation looping”, but now it only stops at the last frame.

Code

extends CharacterBody2D


const SPEED = 100.0
const JUMP_VELOCITY = -250.0
var playerhealth = 100 # Player's health
var playerdamage = 10 # Player's damage
#var enemyattacking = false # Enemy status
var attacking = false # Player status
#@export var enemydamage =
var is_attacking: bool = false


# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

@onready var animated_sprite = $AnimatedSprite2D
#@onready var enemy = $"../enemy1"
@onready var enemy = get_node("../enemy1")  # The path in the tree, not the file system.


func _physics_process(delta) -> void:
	#print(health)
	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta
	
	if is_attacking:
		return

	# Handle jump.
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY
		
	var direction = Input.get_axis("left", "right")
	if direction > 0:
		animated_sprite.flip_h = false
	elif direction < 0:
		animated_sprite.flip_h = true
		
	if direction == 0:
		animated_sprite.play("idle")
	else:
		animated_sprite.play("e-walk")
	
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()
	
	attacking = Input.is_action_just_pressed("attack")
	

	 #Start the attack animation when the attack button is pressed
	#if attacking:
		#animated_sprite.play("attack")
		#is_attacking = true
		
#func _on_animation_finished(animation) -> void:
	#if animation == "attack":
		#is_attacking = false
		#animated_sprite.animation_finished
func _input(event):
	if event.is_action_pressed("attack"):
		is_attacking = true
		animated_sprite.play("attack")

func _on_area_2d_body_entered(body):
	if body.name == "enemy1": # If the entered body is an enemy's then:
		print("Body entered: ", body.name) 
		if attacking:
			body.enemyhealth -= playerdamage
			print("Enemy health: ", body.enemyhealth)
		# this allows the player to take damage from the enemy
		body.enemyattacking = true
		playerhealth -= body.enemydamage
		
		print("Player health: ", playerhealth)
		if playerhealth <= 0:
			get_tree().reload_current_scene()

func _on_area_2d_body_exited(body):
	if body == enemy:
		enemy.enemyattacking = false

you still need the _on_animation_finished() method, since this will allow you to move again. Click on your animated_sprite and go to signals. connect the animation_finished signal to the method.
EDIT: The method has to look like this:

func _on_animation_finished() -> void:
    if animated_sprite.animation == "attack":
        is_attacking = false

Now it works!! Thank you a lot!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.