• Godot HelpProgramming
  • AnimatedSprite animation not waiting to complete before moving on with yield command

Ok, I have been trying to get this to work for about 3 or 4 weeks of spare time and CANNOT get it to do what I want....

I have a short (2 frame) animation for a character to turn, I managed to get it working PERFECTLY the way I wanted with this code using a standard Input process.....

func applyMovement(delta):

	if Input.is_action_pressed("ui_left"):
		if $AnimatedSprite.flip_h == false:
			print("F")
			$AnimatedSprite.play("Turn")
			yield($AnimatedSprite, "animation_finished")
			velocity.x = 0
		$AnimatedSprite.flip_h = true
		direction = -1
		speed.x = MAX_SPEED * direction
		$AnimatedSprite.play("Walk")

	elif Input.is_action_pressed("ui_right"):

		if $AnimatedSprite.flip_h == true:
			print("Q")
			$AnimatedSprite.play("Turn")
			yield($AnimatedSprite, "animation_finished")
			velocity.x = 0
		$AnimatedSprite.flip_h = false
		direction = 1
		speed.x = MAX_SPEED * direction
		$AnimatedSprite.play("Walk")

However, I am trying to impliment this using a finite state machine, (using the GDQuest pro 2d game course version) and this doesnt work anymore! I dont know why....

I have things broken down into idle, turn and move states, and I want to have the 2 frame animation complete before passing on to the move state, but yield doesnt seem to work anymore the way it did in the above code....

It doesnt matter where I put any of the instructions, it just deosnt work. I have changed it so many times, put it in so many places, I dont know what do to now.

Heres the code for the 'turn state'



extends 'state.gd'

func enter(host):
	pass
	
	 
func handle_input(host, event):
	if event.is_action_pressed('jump'):
		return 'jump'		


func update(host, delta):

	var input_direction = get_input_direction()	

	if input_direction.x == -1:
		if host.get_node('AnimatedSprite').flip_h == true:
			return 'move'
		elif host.get_node('AnimatedSprite').flip_h == false:
			turn(host)
	
	if input_direction.x == 1:
		if host.get_node('AnimatedSprite').flip_h == false:
			return 'move'
		elif host.get_node('AnimatedSprite').flip_h == true:
			turn(host)

				

func get_input_direction():
	var input_direction = Vector2()
	input_direction.x = int(Input.is_action_pressed("move_right")) - int(Input.is_action_pressed("move_left"))
	return input_direction 



func turn(host):
	var input_direction = get_input_direction() # get the input direction

	if input_direction.x == -1:	# left

		if host.get_node('AnimatedSprite').flip_h == false:
			print("Turn LEFT")
			print(host.look_direction)
			host.get_node('AnimatedSprite').play("turn")
			host.get_node('AnimatedSprite').flip_h = true
			print("Turned LEFT")			
			yield(host.get_node('AnimatedSprite'), "animation_finished")
			print("Anim finish L")	
			return 'move'

		
	elif input_direction.x == 1:	# right

		if host.get_node('AnimatedSprite').flip_h == true:
			print("Turn RIGHT")
			print(host.look_direction)
			host.get_node('AnimatedSprite').play("turn")		
			host.get_node('AnimatedSprite').flip_h = false
			print("Turned RIGHT")
			yield(host.get_node('AnimatedSprite'), "animation_finished")	
			print("Anim finish R")	
			return 'move'

Now, although it prints through all the Print statements in the output, so it IS trawling through the code as intended, its not stopping (yielding) at the turn animation anymore. The 1st frame of the turn animation is played super brifely, before moving on to the 'move' state. HELP! ( and I dont know how to get all the code to format properly, sorry for hard reading)

Welcome to the forums @BingBong! I fixed the code formatting for you by indenting everything by a single tab. You can also surround the code block in three ~ and it will be rendered as code.

As for the problem, I am not sure right off to be honest. I rarely use yield, and when I have used finite state machines I generally connect the signal I am waiting on to a function and then store variables so I know what state to transition to. It is perhaps not the cleanest, but its worked okay for me.

Something like this:

extends 'state.gd'

var animation_finished_state = null
var state_update_locked = false

func enter(host):
	host.get_node("AnimatedSprite").connect("animation_finished", self, "on_animation_finished")

func handle_input(host, event):
	if event.is_action_pressed('jump'):
		return 'jump'		


func update(host, delta):
	# if the state is locked, so the animation cannot be
	# interrupted, then just return.
	if (state_update_locked == true):
		return
	
	var input_direction = get_input_direction()	

	if input_direction.x == -1:
		if host.get_node('AnimatedSprite').flip_h == true:
			return 'move'
		elif host.get_node('AnimatedSprite').flip_h == false:
			turn(host)
	
	if input_direction.x == 1:
		if host.get_node('AnimatedSprite').flip_h == false:
			return 'move'
		elif host.get_node('AnimatedSprite').flip_h == true:
			turn(host)

				

func get_input_direction():
	var input_direction = Vector2()
	input_direction.x = int(Input.is_action_pressed("move_right")) - int(Input.is_action_pressed("move_left"))
	return input_direction 



func turn(host):
	var input_direction = get_input_direction() # get the input direction

	if input_direction.x == -1:	# left

		if host.get_node('AnimatedSprite').flip_h == false:
			print("Turn LEFT")
			print(host.look_direction)
			host.get_node('AnimatedSprite').play("turn")
			host.get_node('AnimatedSprite').flip_h = true
			print("Turned LEFT")			
			animation_finished_state = "move"
			state_update_locked  = true

		
	elif input_direction.x == 1:	# right

		if host.get_node('AnimatedSprite').flip_h == true:
			print("Turn RIGHT")
			print(host.look_direction)
			host.get_node('AnimatedSprite').play("turn")		
			host.get_node('AnimatedSprite').flip_h = false
			print("Turned RIGHT")
			animation_finished_state = "move"
			state_update_locked  = true

func on_animation_finished(animation_name):
	if animation_finished_state != null:
		# Change states here! I'm not sure how the state is stored
		# in the original code, so I"m just assuming its in a class
		# variable called "state"
		state = animation_finished_state 
		animation_finished_state = null
		state_update_locked  = false
		print ("Changed state!")

I'm not sure if it will help, but that is how I would adjust the code so it doesn't rely on yield. I wrote the adjustments to the code from memory and without a compiler, so it may not be 100% correct but hopefully it gives the gist of it.

Also, while looking at the code, I realized that on lines 20 and 26 in "turn state", you are calling turn(host), but the turn function returns a string but you are not returning this string in the update function. You may want to change those lines to return turn(host), which may potentially fix the problem as well.

Thanks for taking the time do do that!

The state itself is stored in THAT script connected to a node (Turn) - as in the GDQuest tutorial -

So not figured out how to connect this to change the state from the on_animation_finished function, but I get the idea of whats going on.

As for the turn(host) on lines 20 and 26, I added the separate turn function to clean things up a bit, I had the code for that IN the update function (amongst other places to see what works), but as it gives a - return 'move' - string as its result, surely having the turn(host) IN the update function it will give the return 'move' string anyway?? Or did I miss something bout the way things are handled!?

Also, adding return turn(host) crashes things at the moment.

I shall spend some time applying your method and post back a final working state. Just baffled as to why yield refuses to work in this case!

Any alternate solutions welcomed!

Ok, should anyone need to refer to this one is as it stands working. Just.

Tinkered with it and its kind of ok, but not robust enough for use atm, button mashing left or right, seems to make things hang running the scene sooner or later, sometimes with NO red error in the error debugger window, sometimes with

connect: Signal 'animation_finished' is already connected to given method '_on_AnimatedSprite_animation_finished' in that object.

...which is the error returned with every key press the CONNECT_ONESHOT argument solves, as in this case, 'animation_finished' is called within the physics process, as the state machine _physics_process is handling the actual state change.

Needs more tinkering or a different approach to be less crashy in my case, but thanks for the help!

extends 'state.gd'

var animation_finished_state = null
var state_update_locked = false

func enter(host):
	host.get_node("AnimatedSprite").connect("animation_finished", self, "_on_AnimatedSprite_animation_finished", [], CONNECT_ONESHOT)

# Does need the ONESHOT set here else you get error signal already connected each time you try and move left/right.
	
func handle_input(host, event):
	if event.is_action_pressed('jump'):				
		return 'jump'		


func update(host, delta):			

	if (state_update_locked == true):
		return
		
	var input_direction = get_input_direction()		

	if input_direction.x == -1:
		if host.get_node('AnimatedSprite').flip_h == true:
			return 'move'
		elif host.get_node('AnimatedSprite').flip_h == false:
			turn(host)

	if input_direction.x == 1:
		if host.get_node('AnimatedSprite').flip_h == false:
			return 'move'
		elif host.get_node('AnimatedSprite').flip_h == true:
			turn(host)

	if input_direction.x == 0:
		return 'idle'

func get_input_direction():								
	var input_direction = Vector2()
	input_direction.x = int(Input.is_action_pressed("move_right")) - int(Input.is_action_pressed("move_left"))
	return input_direction 



func turn(host):
	var input_direction = get_input_direction()	

	if input_direction.x == -1:	# left
		if host.get_node('AnimatedSprite').flip_h == false:
			print("Turn LEFT")
			host.get_node('AnimatedSprite').play("turn")
			host.get_node('AnimatedSprite').flip_h = true
			print("Turned LEFT")			
			animation_finished_state = "move"
			state_update_locked  = true
		
	elif input_direction.x == 1:			# right
		if host.get_node('AnimatedSprite').flip_h == true:
			print("Turn RIGHT")
			host.get_node('AnimatedSprite').play("turn")		
			host.get_node('AnimatedSprite').flip_h = false
			print("Turned RIGHT")
			animation_finished_state = "move"
			state_update_locked  = true
		


func _on_AnimatedSprite_animation_finished():
	if animation_finished_state != null:
#		state = animation_finished_state  # Doesnt need this bit to call the state as this IS the state!
		animation_finished_state = null
		state_update_locked  = false
		print ("Changed state!")
		return 'move'
2 years later