Been bugging me for more than a year now....

I have a 2D character in a state machine I have set up with a turn state, supposed to ONLY handle the transition from turning left to right and vice versa.

The turn state checks the AnimatedSprites flip value, and either passes onto the walk state if (from Idle) its pointing in the same direction as moving to, or if pointing in the opposite direction from moving to, it plays the (3 frame) animation for the turn, and do NOTHING else until its complete, THEN passes on to the walk state.

if you hold down the button, the turn is completed successfully and passes to the walk state. But if you TAP the button, the animation doesnt complete, and it snaps back to the position it was previously looking in.

You can see what I mean from the gif here....

How the hell do I get the animation to finish playing before passing onto the next state. This snap back is annoying me so bad!

Yes yes, I get it, dont use yields use signals, but this is the only way I have got it to work. Albeit with the key tapping bug. Without these, with the button held down, it doesnt wait for the animation to finish and passes straigt onto the walk state.

extends State

onready var move: = get_parent()

# interface for the state
#func  unhandled_input(event: InputEvent) -> void:
#	move.unhandled_input(event)


func enter(msg: Dictionary = {}) -> void:
	print("Turn State")
	var flip = owner.get_node("AnimatedSprite").flip_h
	
	# pass the message to the parent move state...
	move.enter(msg)


	if move.get_move_direction().x > 0.0 && flip == false:
		_state_machine.transition_to("Move/Run")
	
	if move.get_move_direction().x < 0.0 && flip == true:
		_state_machine.transition_to("Move/Run")

	if move.get_move_direction().x > 0.0 && flip == true:
		owner.get_node("AnimationPlayer").play("Turn")
		yield(owner.get_node("AnimationPlayer"), "animation_finished")
		flip = false		
		_state_machine.transition_to("Move/Run")

	if move.get_move_direction().x < 0.0 && flip == false:
		owner.get_node("AnimationPlayer").play("Turn")
		yield(owner.get_node("AnimationPlayer"), "animation_finished")
		flip = true
		_state_machine.transition_to("Move/Run")
		
func exit() -> void:
	get_parent().exit()

Tried getting a method to fire from within the AnimationPlayer once the animtaion hits the last frame, but Im not diong that right either apparently.

Tried adding the signal to make it work, (without the above yeld and state.transition_to parts) but same problem, just snaps back as soon as the key is just tapped...

func _go_to_run_state():
	_state_machine.transition_to("Move/Run")

func _on_AnimationPlayer_animation_finished(anim_name):
	if anim_name == "Turn":
		_go_to_run_state()
		print(anim_name, " finished")

I kinda need this to work as its part of the nice smooth animation I have been going for and its going to be needed to position the character accurately sometimes.

Ideas?

Is this function not working at all?

func _on_AnimationPlayer_animation_finished(anim_name):
    if anim_name == "Turn":
        _go_to_run_state()
        print(anim_name, " finished")

First try putting that print statement as the first line of the function and see if that helps you debug at all

Nope, still the identical problem....

there was ONLY the print statement to start with in that part. It still prints put at the beginning and end of that function. It CLAIMS its finished.... but still get the snap.

The only thing I can think of is that something in the parent move state is overriding something, but its only really getting the get_move_direction() and setting the velocity. Nothing to do with the turn state I thinks causing the problems.

extends State

"""
Parent state, abstracts and handles basic movement
Move related child states can delegate movement to it,
or use its utility functions
"""

export var max_speed_default: = Vector2(300.0, 1500.0)
export var acceleration_default: = Vector2(100000, 3000.0)
export var jump_impulse: = 900.0

var acceleration: = acceleration_default
var max_speed: = max_speed_default
var velocity: = Vector2.ZERO

func physics_process(delta: float) -> void:
	velocity = calculate_velocity(velocity, max_speed, acceleration, delta, get_move_direction())
	velocity = owner.move_and_slide(velocity, owner.FLOOR_NORMAL)

static func calculate_velocity(
	# to calculate velocity we need the following arguments.....
	old_velocity: Vector2,
	max_speed: Vector2,
	acceeration: Vector2,
	delta: float,
	move_direction: Vector2	
) -> Vector2:
	
	var new_velocity: = old_velocity
	
	# will steer towards the new direction player is tryingto move to
	new_velocity += move_direction * acceeration * delta
	# clamp the velocity using the max speed value.
	new_velocity.x = clamp(new_velocity.x, -max_speed.x, max_speed.x)
	new_velocity.y = clamp(new_velocity.y, -max_speed.y, max_speed.y)
	
	return new_velocity

static func get_move_direction() -> Vector2:
	return Vector2(
	Input.get_action_strength("move_right") - Input.get_action_strength("move_left"),
	1.0		# 1.0 keeps the y part of the vec2 consistant
)

Maybe it's that owner.get_node("AnimatedSprite").flip_h is not being reassigned to the flip variable? I would maybe try adding owner.get_node("AnimatedSprite").flip_h = flip to the end of the enter function and see if that fixes it, as it could be that the flip_h variable isn't ever being set so it changes back to the other direction even after the animation is finished.

Ok, adding owner.get_node("AnimatedSprite").flip_h = flip to the end of the enter function, does resolve the key tap issue, (I think I had wrongly assumed that flip = true / flip = false WAS doing this part!) but the transition from this turn state to the idle or walk state now has a glitchy frame.... Whereas before it was smooth holding the key down, now on holding the key down AND with the key tap, it seems theres a split second where the flip hasnt initialised properly and it glitches back to the previous flipped_h.....

I slowed the turn animation down bit so its seeable in this crappy gif...

...this needs to be smoother than an Otters pocket, at the moment its as rough as a Badgers /area\ This guy is just an NPC, tho all of my player characters, enemys and other NPCs have all been animated on the same principal, so I need to get it right before ploughing on too far with the project!

I'm not positive, but my guess would be that one of the conditions is triggering and changing flip_h to the incorrect value but is not changing the state. Looking at the code though, I don't see where it would be occurring though.

It might be the yield that is causing the issue, as it wouldn't set the flip_h until the yield finishes if it's at the end of the enter function. I would maybe try adding owner.get_node("AnimatedSprite").flip_h = flip right before the yield statements in lines 26 and 32 and see if that changes anything.

yeah, tried that (and placing it all over the place just to try!), and also with removing the yield lines (I'll stop using those now, I promise!) and relying on the animation finished signal function instead. It doesnt make a blip of difference.

Adding the owner.get_node..... line at the end of the enter() function DOES solve my original problem tho, but exchanges it for a new glitchy issue. There is something a bit odd going on with that line. I have gone through it with a pen and paper, there shouldnt be anything causing what looks like a 1 frame unwanted flip.

I might have to try another way to get this working. I have around 15 characters (so far) all now animated needing to use this transition! It has to work somehow!

a year later