Why my attack animation is stuck at first frame

Godot Version

4.2.1

Question

my attack animation is stuck at first frame:

var attack4 = false
func animation():
	if is_on_floor():
	    if Input.is_action_just_pressed("attack"):
	    	attack4 = true
	    if attack4:
	    	$AnimatedSprite2D.play('attack4')

		if velocity.x < 0:
			$AnimatedSprite2D.flip_h = true
			$AnimatedSprite2D.play('run')
		elif velocity.x > 0:
			$AnimatedSprite2D.flip_h = false
			$AnimatedSprite2D.play('run')
		
	if is_on_floor() and velocity.x==0:
		$AnimatedSprite2D.play('idle')
		

My guess it has to do with your second if statement at the same level that plays idle animation.

Have you trouble shot it with print statements?

Place a printstatement under your attack4 = true, and one by your animation.play(“idle”), see if they are both activating in the same func call.

Also, you set attack4 = true, but where do you set it false?

So, what happens is you call animation() if on floor and if you press attack, attack = true, now if attack play animation.

However, the next time animation() is called if your player is on floor it will automatically play attack animation since attack4 still == true.

With how you are using attack4 in what I can see it is unecessary, unless it is used in other code. You could just place your animation under input_pressed and eliminate the attack4.

However, you need to create a system that prevents your idle animation from overriding your attack animation as well.

var attack4 = false
func animation():
	if is_on_floor():
	    if Input.is_action_just_pressed("attack"):
	    	attack4 = true
	    	$AnimatedSprite2D.play('attack4')
            attack4 = false

        if velocity.x < 0:
		     $AnimatedSprite2D.flip_h = true
		     $AnimatedSprite2D.play('run')
	    elif velocity.x > 0:
		     $AnimatedSprite2D.flip_h = false
		     $AnimatedSprite2D.play('run')
		
	    elif not attack4:
		     $AnimatedSprite2D.play('idle')
1 Like

Thanks, it’s working right now but i have to set attack4 false in _on_animation_finished() signal instead of below the $AnimatedSprite2D.play('attack4') otherwise it will set the attack4 to false before animation is finished.

Yeah that makes sense, otherwise your other animations would override it. Sounds good!

1 Like

one more problem, i’m trying to move player towards left/right until animation is finished when pressing both left/right key and attack key, but it doesn’t seem to work, it sets velocity.x to 0 but not to 900:

var speed = 500
func _physics_process(delta):
    var axis = Input.get_axis('moveLeft', "moveRight")
    velocity.x = speed*axis
    animation()

func animation():
	
	if is_on_floor():
		if Input.is_action_just_pressed("attack"):
			attack4 = true
			$AnimatedSprite2D.play('attack4')

		if velocity.x < 0 and not attack4:
			$AnimatedSprite2D.flip_h = true
			$AnimatedSprite2D.play('run')
		elif velocity.x < 0 and attack4:
			speed = 0
			velocity.x = -900
		
		elif velocity.x > 0 and not attack4:
			$AnimatedSprite2D.flip_h = false
			$AnimatedSprite2D.play('run')
		elif velocity.x > 0 and attack4:
			speed = 0
			velocity.x = 900
		
		elif not attack4:
			$AnimatedSprite2D.play('idle')
			print($AnimatedSprite2D.animation)
	
	if velocity.y < 0:
		$AnimatedSprite2D.play('jump')
	elif velocity.y > 0:
		$AnimatedSprite2D.play('fall')

func _on_animation_finished():
	if attack4:
		attack4 = false
		speed = 500

I think your problem is that the physics_process is resetting your velocity. Physics is called 60 times per second. So, you set speed to 0 and velocity.x = -900, the animation starts playing, however 1/60th of a second later physics runs again and sets velocity.x = speed(0)*axis, effectively setting it to 0.

You can in physics try and add an if statement before setting velocity.x. if not attack4: velocity.x = something.

This also eliminates the million elif statements

var speed = 500
func _physics_process(delta):
    var axis = Input.get_axis('moveLeft', "moveRight")
     If not attack4:
         velocity.x = speed*axis
    animation()

func animation():
	
	if is_on_floor():
		if Input.is_action_just_pressed("attack"):
			attack4 = true
			$AnimatedSprite2D.play('attack4')
        if not attack4:
	       	if velocity.x < 0:
			    $AnimatedSprite2D.flip_h = true
	     		$AnimatedSprite2D.play('run')
	       	elif velocity.x > 0:
	     		$AnimatedSprite2D.flip_h = false
	     		$AnimatedSprite2D.play('run')
            else:
     			$AnimatedSprite2D.play('idle')
        else:
             if velocity.x < 0:
     			velocity.x = -900
	     	 elif velocity.x > 0:
     			velocity.x = 900
		
		
			print($AnimatedSprite2D.animation)
	
	if velocity.y < 0:
		$AnimatedSprite2D.play('jump')
	elif velocity.y > 0:
		$AnimatedSprite2D.play('fall')

func _on_animation_finished():
	if attack4:
		attack4 = false

Generally if you have a lot of if/elif statements checking same for same thing and another thing you can seperate them to make less checks.

1 Like

Thanks again :+1: