Idle animation not playing, using 'match'

Godot Version

Godot 4.2.2

Question

I have a simple 2D sprite character that moves in 4 directions: up, down, left and right. The character has attack animations for these 4 directions, and idle animations for these directions as well.

The character moves in all directions displaying the correct animations, and attack in all directions displaying all correct animations.

Problem:
The idle animation is not playing properly. It’s defaulting to the last idle animation, and not displaying the correct idle animation based on direction.

I am still new to coding but i tried different new concepts in one go, and I am sure i missed something somewhere. I appreciate your guidance.

extends CharacterBody2D


var speed = 100
var playerState = "idle"
var is_attacking = false
var dir = Vector2.ZERO

func _physics_process(delta):
	# Get player's input for movement, W A S D
	var direction = Input.get_vector("left", "right", "up", "down")
	
	# Update player state based on direction
	update_player_state(direction)
	
	if Input.is_action_just_pressed("attack") and not is_attacking:
		attacking()
	
	if not is_attacking:
		handle_movement(direction)
		play_anim()
	else:
		play_anim()  # Ensure attack animation plays if attacking

func update_player_state(direction: Vector2):
	if direction.x == 0 and direction.y == 0:
		playerState = "idle"
	else:
		playerState = "walking"
	# Update direction for animation purposes / dir is not updated??
	dir = direction
	print(dir) 

func handle_movement(direction: Vector2):
	velocity = direction * speed
	move_and_slide()

func play_anim():
	if is_attacking:
		play_attack_anim()
	elif playerState == "idle":
		play_idle_anim()
	elif playerState == "walking":
		play_walk_anim()

func play_idle_anim():
	match dir:
		Vector2.UP:
			$AnimatedSprite2D.play("up_idle")
		Vector2.RIGHT:
			$AnimatedSprite2D.play("right_idle")
		Vector2.DOWN:
			$AnimatedSprite2D.play("down_idle")
		Vector2.LEFT:
			$AnimatedSprite2D.play("left_idle")
		_:
			$AnimatedSprite2D.play("down_idle")
			#default, if others not call for some reason
			#it's always using this last one..
			
func play_attack_anim():
	match dir:
		Vector2.UP:
			$AnimatedSprite2D.play("up_attack")
		Vector2.RIGHT:
			$AnimatedSprite2D.play("right_attack")
		Vector2.DOWN:
			$AnimatedSprite2D.play("down_attack")
		Vector2.LEFT:
			$AnimatedSprite2D.play("left_attack")

func play_walk_anim():
	match dir:
		Vector2.UP:
			$AnimatedSprite2D.play("up_walk")
		Vector2.RIGHT:
			$AnimatedSprite2D.play("right_walk")
		Vector2.DOWN:
			$AnimatedSprite2D.play("down_walk")
		Vector2.LEFT:
			$AnimatedSprite2D.play("left_walk")

func attacking():
	is_attacking = true
	play_anim()  # Ensure attack animation starts immediately
	var attack_animation_length = 0.5
	await get_tree().create_timer(attack_animation_length).timeout
	is_attacking = false

func player():
	pass

The problem is that in idle state your direction is (0,0), since you are not moving, so it doesnt match any of your given cases. What you can do is to take only the last direction by changing “update_player_state”:

func update_player_state(direction: Vector2):
	if direction.x == 0 and direction.y == 0:
		playerState = "idle"
	else:
		playerState = "walking"
	    # Update direction for animation purposes / dir is not updated??
	    dir = direction

	print(dir) 

i think this should work. now it doesnt change the direction when you go into idle so it should keep the last direction

2 Likes

I thought it was a complicated issue… thank you, this worked, and I learned something new :smiley:

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.