Why does the AnimatedSprite2D randomly not finish?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By skulnd

Thanks in advance for any help on this.

I am a beginner with Godot and my engine just updated to 4.0. I am trying to build a 2D platformer game using state machines. I have an “Attack” state that is triggered when the attack input is “just_pressed”. I then have a signal for when the animation finishes, and I specify that if it is the attack animation, it will either revert to a “floor” or “air” state depending on the “is_on_floor” value.

Unfortunately, the attack animation loops randomly anywhere from once to several times before transitioning to another state. This occurs most if I attack as the first input when the scene plays, or when other buttons are pressed as I am attacking. But I am also seeing it randomly during all actions.

I did not have the issue when using v3.5. I have also confirmed the attack animation is not set to loop on the animatedsprite2D.

What can I change to ensure the attack animation finishes after playing once and the state changes as needed?

Here is the code I am using for the state machine and the animation finished signal:

match state:
	States.FLOOR:
		if direction:
			velocity.x = direction * SPEED
			avatar.play("Run")
		else:
			velocity.x = lerpf(velocity.x,0,0.3)
			avatar.play("Idle")
		
		if Input.is_action_just_pressed("jump"):
			velocity.y = JUMPFORCE
			state = States.AIR
		
		move_and_slide()
		
		if not is_on_floor():
			state = States.AIR
			
		if Input.is_action_just_pressed("attack"):
			state = States.ATTACK

		
		
	States.AIR:
		if direction:
			velocity.x = direction * SPEED
		
		if velocity.y < 0:
			avatar.play("Jump")
		elif velocity.y > 0:
			avatar.play("Fall")
		
		move_and_slide()

		if is_on_floor():
			state = States.FLOOR
			
		if Input.is_action_just_pressed("attack"):
			state = States.ATTACK
	
	States.ATTACK:
		hitBox.disabled = false
		avatar.play("Attack")
		
		
		
		
				
func _on_animated_sprite_2d_animation_finished():
	if avatar.animation == "Attack":
		hitBox.disabled = true
	
	if is_on_floor() == true:
		state = States.FLOOR
	elif is_on_floor() == false:
		state = States.AIR
	
if avatar.animation == "Hurt":
	state = States.FLOOR
:bust_in_silhouette: Reply From: aidave

You likely need to have a state change detection.

For example have a section for input detection where you are setting “newstate” instead of state, then have an animation section where you check if “newstate != state” before playing animations, then set “state = newstate” at the end.

Excellent, thank you!

This is what was needed. I did create a newstate check and instead of the input triggering the state change directly, it changes the newstate check. I then put the state change under the newstate check:

if atkState:
	state = States.ATTACK

The state then only calls the animation function, with the newstate revert following the animation:

...
    		States.ATTACK:
			    attack_state()
		
		
		
func attack_state():
	if atkState:
		hitBox.disabled = false
		avatar.play("Attack")
		atkState = false

skulnd | 2023-03-10 15:38

Welcome! I suspected it was hitting play() over and over on the animation, but wasn’t sure. I think everyone runs into this the first time they try to make a character controller :slight_smile:

Another option is to check everywhere if the animation is playing the same animation or not, but that will be more spaghetti code-ish (or it could be wrapped in a function)

aidave | 2023-03-10 21:02