How to make state changes after when animation finish

Godot Version

4.1.1

Question

I want to make player attack state to change after when attack animation finish but attack state only stays when I press key and switches to other state right away even though animation does not finish yet how do I fix this?

extends CharacterBody2D
const max_spd: int = 110
const accl: int = 10
const friction: int = 16

const attackdistance = 30

@onready var anime = $AnimatedSprite2D

var can_attack = false
var attackdash = true

enum States {IDLE, RUN, DASH, PATTACK}

var player_state = States.IDLE

func _physics_process(delta: float) -> void:
	
	if player_state != States.DASH and player_state != States.PATTACK:
		get_input(delta)
		can_attack = false
		
		print(velocity)
		
	if velocity.length() < 9:
		player_state = States.IDLE
	elif velocity.length() != 0:
		player_state = States.RUN
	
	if Input.is_action_just_pressed("attack"):
		player_state = States.PATTACK
		
	move_and_slide()
	update_animation()

	

func change_state(newStates):
	player_state = newStates

func get_input(delta):
	var input = Vector2(
		Input.get_action_strength("right") - Input.get_action_strength("left"),
		Input.get_action_strength("down") - Input.get_action_strength("up")
	).normalized()
	
	var lerp_weight = delta * (accl if input else friction)
	velocity = lerp(velocity, input * max_spd, lerp_weight)
	
func attack():
	var mouse_direction: Vector2 = (get_global_mouse_position() - global_position).normalized()
	
	can_attack = true
	anime.play("attack")
	velocity = mouse_direction.normalized() * attackdistance
	attackdash = true
	await get_tree().create_timer(0.2)
	attackdash = false
	await anime.animation_finished
	can_attack = false

func update_animation():
	if velocity.x > 0:
		anime.flip_h = true
	if velocity.x < 0:
		anime.flip_h = false
	match(player_state):
		States.IDLE:
			anime.play("idle")
			print("i stand")
		States.RUN:
			anime.play("running")
			print("i run")
		States.DASH:
			anime.play("dash")
		States.PATTACK:
			attack()
			print("i swing")

func _physics_process runs every frame.

In the above, if the “attack” key is ever pressed, then player_state becomes States.PATTACK, for that frame. But in the next frame, is_action_just_pressed("attack") is false, so we necessarily end up in state IDLE or in state RUN.

This is always a bit of a mess, and the proper way to take care of this is to use a state machine. But if you want something quick and dirty that will get the job done, you could condition the state changes in _physics_process on the state not being PATTACK, and then in func attack() after the await animation_finished, you can set the state back to IDLE.

If you don’t know what a state machine is, then go ahead and do things the “quick and dirty” way. You’ll find eventually that every tiny little addition requires you to modify code in like 12 different locations and you always miss one, and end up with weird bugs everywhere. When that happens, look into state machines (which maybe sounds like a scary term, but is actually not bad at all and simplifies this sort of process a ton).