• [deleted]

I want to have an attacking state and have it wait for the animation to finish before changing state. The problem is that when i spam the attack bind (left mouse button) it triggers more than once, freezes the animation and it awaits forever for an animation that never finishes. The attack state code is pretty much the same as the dodge state but when i spam the dodge bind (shift) it doesn't do that.
Video demonstration

State Machine Class

Click to reveal Click to hide
extends Node
class_name StateMachine


@export var current_state: State
var states: Dictionary = {}


func _ready() -> void:
	for child in get_children():
		if child is State:
			states[child.name] = child
			child.transitioned.connect(on_child_transitioned)
	current_state.Enter()


func _process(delta: float) -> void:
	current_state.Update(delta)


func _physics_process(delta: float) -> void:
	current_state.Physics_update(delta)


func on_child_transitioned(new_state_name: String) -> void:
	var new_state = states.get(new_state_name)
	if new_state != null:
		if new_state != current_state:
			current_state.Exit()
			new_state.Enter()
			current_state = new_state

State Class

Click to reveal Click to hide
extends Node
class_name State

signal transitioned(new_state_name: String)

func Enter() -> void:
	pass
	
func Exit() -> void:
	pass
	
func Update(_delta: float) -> void:
	pass

func Physics_update(_delta: float) -> void:
	pass

Attack State

Click to reveal Click to hide
extends State
class_name AttackState

@export var actor: CharacterBody3D
@export var forward_movement: float
@onready var animation_player: AnimationPlayer = $"../../AnimationPlayer"
@onready var armature: Node3D = $"../../Armature"
@onready var spring_arm: SpringArm3D = $"../../SpringArm3D"


func Enter() -> void:
	animation_player.play("Attack_1")
	animation_player.speed_scale = 2.5
	actor.velocity = -armature.transform.basis.z * forward_movement


func Physics_update(_delta: float) -> void:
	await animation_player.animation_finished
	#if not animation_player.is_playing():
	var move_direciton = InputManager.get_move_direction_rotated(spring_arm)
	
	if move_direciton:
		transitioned.emit("Run")
	else:
		transitioned.emit("Idle")

Dodge State

Click to reveal Click to hide
extends State
class_name DodgeState


@export var actor: CharacterBody3D
@export var forward_movement: float


@onready var animation_player: AnimationPlayer = $"../../AnimationPlayer"
@onready var spring_arm: SpringArm3D = $"../../SpringArm3D"
@onready var armature: Node3D = $"../../Armature"


func Enter() -> void:
	animation_player.play("Dodge")
	animation_player.speed_scale = 2.5
	actor.velocity = -armature.transform.basis.z * forward_movement
	
	
func Physics_update(_delta: float) -> void:
	await animation_player.animation_finished
	var move_direciton = InputManager.get_move_direction_rotated(spring_arm)
	
	if move_direciton:
		transitioned.emit("Run")
	else:
		transitioned.emit("Idle")

Idle State

Click to reveal Click to hide
extends State
class_name IdleState


@export var actor: CharacterBody3D
@export var speed: float


@onready var animation_player: AnimationPlayer = $"../../AnimationPlayer"
@onready var spring_arm: SpringArm3D = $"../../SpringArm3D"


func Enter() -> void:
	animation_player.play("Idle")
	animation_player.speed_scale = 0.8


func Physics_update(_delta: float) -> void:
	var move_direction = InputManager.get_move_direction_rotated(spring_arm)
	
	if move_direction:
		transitioned.emit("Run")
	else:
		InputManager.stop_actor(actor, speed)
	
	if Input.is_action_just_pressed("attack1"):
		transitioned.emit("Attack")
	elif Input.is_action_just_pressed("jump"):
		transitioned.emit("Jump")
	elif Input.is_action_just_pressed("dodge"):
		transitioned.emit("Dodge")

Unsure why Idle would continue to transition to 'Attack' when spammed unless it is transitioning back to another input state first. Where's the run state?

Worth verifying animation names and that it signals 'animation_finished' (looping?). Likely the animation player/animation is misbehaving.

(imho I'd have another state - like Attacking - that just waits but maybe that's just me)