Animation stuck on first frame after animation changed

Godot Version

4.2.2

Question

Hey there, I know it’s a common issue beginners are having but when reading the others posts I didn’t find my answer.

I have a chicken that moves randomly on my screen, and switches “walking” animation to an “idle” animation. It works perfectly:

#Chicken Script

func _physics_process(delta):

		var isLeft = velocity.x < 0
		animated_sprite_2d.flip_h = isLeft
		
		if (velocity.x > 1 || velocity.x < -1):
			animated_sprite_2d.animation = "walking"
		else:
			animated_sprite_2d.animation = "idle"
			
		if velocity.x == 0:
			animated_sprite_2d.flip_h = randomIdle
		move_and_slide()

#Direction Change, makes the chicken randomly stops for more natural movement
func _on_timer_timeout():

		var randomIdle = true

		if randf() < 0.5:
			randomIdle = false
		else:
			randomIdle = true
		
		if randf() < 0.6:
			direction.x = 0
			direction.y = 0
		else:
			direction.x = randf_range(-1.0,1.0)
			direction.y = randf_range(-1.0,1.0)
		pass
		
		velocity = direction * randf_range(50,100)
			

The issue I’m having is that I have a monster entity that basically moves towards the closest chicken on the map, and then attacks the chicken. When the monster is attacking, I run the “attack” animation. When the “attack” animation is over, the monster gets stuck on the first “walking” frame, whereas it works perfectly before attacking:

#Monster Script

func _physics_process(delta): 
	
	if isAttacking == false: #this is how i managed to stop the monster from walking
		
		newClosestChicken = find_closest_chicken()
		direction = global_position.direction_to(newClosestChicken.global_position)
		velocity = direction * 50
	
		var isLeft = velocity.x < 0
		animated_sprite_2d.flip_h = isLeft

		if velocity.x != 0:
			animated_sprite_2d.animation = "walking"
		else:
			animated_sprite_2d.animation = "idle"
		move_and_slide()

func _on_pouler_attacked():
	isAttacking = true
	animated_sprite_2d.animation = "attack"

func _on_animated_sprite_2d_animation_finished():
	isAttacking = false

Thanks

Try to use animation_finished() signal on AnimatedSprite2D instead func _on_animated_sprite_2d_animation_finished(), because I think the problem is your var it’s not changing correctly. You can check what is happening if you put an else on if isAttacking == false: and print isAttacking variable.

I’m also recommended you to check state machines on Godot, it’s a system created for what you’re aiming for.

See a video here

Hey, I’m not sure what you mean by using the animation_finished() signal instead of what I’m using:

I’ve also checked the isAttacking variable and it does change properly from true to false.

I’ll take a look at the State Machine doc and try to implement it. If you have any other idea on how I can fix it happy to take it!

Thanks

1 Like

Let me know if state machines works better to edit my answer, good luck!

The variable is not the issue, otherwise the monster wouldn’t keep walking after killing the chicken. There seems to be something wrong with the animations.

try this

		if velocity.x != 0:
			print("walking animation " + str(Time.get_ticks_msec()))
			animated_sprite_2d.animation = "walking"
		else:
			print("idle animation " + str(Time.get_ticks_msec()))
			animated_sprite_2d.animation = "idle"

And see when they are not called anymore. If you could post the same clip with this console message I’d appreciate it.

On the other hand, Are you sure find_closest_chicken() is working properly? are you doing queue_free to the previous chicken so it’s not found anymore?

Hey,

Yes I confirm the find closest chicken works properly. I remove the chickens from the group as they dissapear.

Is the animation set to loop? Because maybe by the time the chicken is killed, the walking animation stops and when you try to get back to it, it’s over, so it’s just showing you the last frame.
If that doesn’t work, also press the “autoplay” button (to the right of the loop button)

image

The animation is indeed looped:

Do you stop the animation at any point of your code? Like do you do animated_sprite_2d.stop ?
What’s the value of animated_sprite_2d.is_playing() ?

try this:

	if velocity.x != 0:
		print("walking animation " + str(Time.get_ticks_msec()))
		animated_sprite_2d.animation = "walking"
	else:
		print("idle animation " + str(Time.get_ticks_msec()))
		animated_sprite_2d.animation = "idle"

	if animated_sprite_2d.is_playing():
		print ("Animation is playing")
	else:
		print ("Animation IS NOT PLAYING!!!")

Indeed it seems that the animation is not playing…

I’m not using animated_sprite_2d.stop anywhere though… Shouldn’t animated_sprite_2d.animation start the animation again since the velocity is > 0 ?

Problem solved, it was as simple as replacing animated_sprite_2d.animation = “walking” by animated_sprite_2d.play(“walking”) !

By the way @lionx I’m using now the State Machine method. Even though it wasn’t the source of my issue, it’s still way cleaner proceeding this way.

1 Like

I figured it out. Here’s what happens.

If you play a loopable animation (like your “walking”) and you’ve started the animated_sprite somehow (calling “play” or by telling it to autoplay) the animation keeps playing. If you switch to another loopable animation, the animations keep playing and everything works fine.

BUT…

if you switch to a non-loopable animation (in your case, the “attack”) and that animation reaches the end, the animated_sprite STOPS COMPLETELY.
If you assign again the walking animation, it doesn’t matter, it won’t start. You need to call “play” to start the animation again.

I just tested it, switching between animations, and everything works fine (even changing animated_sprite.animation) but as soon as I let a non-loopable animation reach its end, everything stops.

Keep that in mind for the next time, even if you’re changing your code to a state machine, if you don’t handle this correctly you’ll keep having the same problem.

A quick fix is to make a method called “set_animation” and check if the animation is not started, then start it, with that you don’t have to worry about anything anymore.

func set_animation(animation_name):
	animated_sprite_2d.animation = animation_name
	if not animated_sprite_2d.is_playing():
		animated_sprite_2d.play()
1 Like

I’ll keep that in mind, thanks again !

Good to know that it worked for you in one way or another :slight_smile:

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