Need help with diagnal movement animations

Godot Version
v4.3.stable.official[77dcf97d8]

Question
While implementing movement to my player character, i only made animations for the cardinal directions. Those work great but I’m trying to make it so when holding W and A or W and D, the animation for walking north plays, and also the same thing when going south. However, when the player walks diagonally, the animation freezes on the first frame.

extends CharacterBody2D

@export var speed = 400



@onready var _animated_sprite = $PlayerCharacter

func _process(_delta):
	if Input.is_action_pressed("right"): #idle veified
		_animated_sprite.play("WalkE")
		
	if Input.is_action_pressed("left"): #idle verified
		_animated_sprite.play("WalkW")
		
	if Input.is_action_pressed("up"): #idle verified
		_animated_sprite.play("WalkN")
		
	if Input.is_action_pressed("up") and Input.is_action_pressed("left"):#idle verified
		_animated_sprite.play("WalkN")
	
	if Input.is_action_pressed("up") and Input.is_action_pressed("right"):#idle verified
		_animated_sprite.play("WalkN")
	
	if Input.is_action_pressed("down"): #idle verified
		_animated_sprite.play("WalkS")
	
	if Input.is_action_pressed("down") and Input.is_action_pressed("left"):#idle verified
		_animated_sprite.play("WalkS")
	
	if Input.is_action_pressed("down") and Input.is_action_pressed("right"):#idle verified
		_animated_sprite.play("WalkS")
	
	
	if not (Input.is_action_pressed("right") or Input.is_action_pressed("left") or Input.is_action_pressed("up") or Input.is_action_pressed("down")):
		_animated_sprite.play("Idle")





func get_input():
	var input_direction = Input.get_vector("left", "right", "up", "down")
	velocity = input_direction * speed
	





func _physics_process(delta):
	get_input()
	move_and_slide()

_process is called every frame. Let’s simplify the code a bit to the case where only the ‘up’ and ‘left’ button can be pressed:

Now picture a player pressing both keys at the same time. The first if statement is true, so WalkW plays. But the next statement is also true, so the first animation doesn’t even finish, and is immediately set to WalkN. The same is true for the third if statement; the animation starts playing WalkN again.

The other issue is that is_action_pressed returns true as long as a key is being pressed. So even if we simplified the code to just this:

func _process(_delta):	
	if Input.is_action_pressed("left"): #idle verified
		_animated_sprite.play("WalkW")

The animation would never play when you push the ‘left’ button, because ever time _process is called the animation starts over again from the first frame. Instead, check if the animation was pressed just once with is_action_just_pressed.

My solution would be to determine at the start of _process what the last input_direction was that wasn’t also a Vector2.ZERO. Then a match statement can be used to determine what direction the player should move in.

var animation_direction : String = "N"	# default value
var animation_to_play : String = "Idle_"

match last_input_direction:
	case Vector2(-1, 0):
		animation_direction = "E"
	# rest goes here
	
if input_direction == Vector2.ZERO:				# Just like we can walk in four directions, we can also idle in four directions
	animation_to_play = "Idle_" +animation_direction
else:
	animation_to_play = "Walk_" + animation_direction 

Note that this code also accounts for idling in four directions, so you will have to add animations for those extra directions.

Also note that this code works OK-ish for just walking alone, but will get very complicated and unwieldy as soon as you want to add more states to your character controller. You’ll have lots of if-statements to keep in mind, most of them nested several indents deep, and that doesn’t help readability. If you want a good solution for that, look into the state machine pattern. (If you look for ‘state machine Godot’, you’ll probably find something useful.) Of course, don’t bother if walking is all you need.

Thanks a lot! I’m new to Godot so to be honest I didn’t even know state machines existed lol but I’ll make sure to check it out!