ive been learning gamedev for pixel games for almost a week now and im stuck, i dont know how i can fix the animation overlap of the 2 movestate(MOVE,ATTACK), ive tried many solutions but nothing seems to work, pls help ;(

every thing is working and functioning but i just cant seem to fix the overlap in animations whenever the attack function is working.

im using
characterbody2d
-animatedsprite2d
-animatedsprite2d named ATTACK_UP
-animatedsprite2d named ATTACK_DOWN
-animatedsprite2d named ATTACK_RIGHT
-animatedsprite2d named ATTACK_LEFT
-COLLISIONSHAPE2D

this is my player code

extends CharacterBody2D

var normal_speed = 110
var sprint_speed = 150
var last_direction := Vector2.DOWN
var attacking := false  
var moving:=false

enum {
	MOVE,
	ROLL,
	ATTACK
}
var state = MOVE

@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D
@onready var attack_down: AnimatedSprite2D = $ATTACK_DOWN
@onready var attack_up: AnimatedSprite2D = $ATTACK_UP
@onready var attack_left: AnimatedSprite2D = $ATTACK_LEFT
@onready var attack_right: AnimatedSprite2D = $ATTACK_RIGHT

func _ready():
	hide_all_attack_sprites()
	

func _process(delta):
	# Check for input and update states here
	match state:
		MOVE:
			move_state(delta)
		ATTACK:
			 start_attack()

func move_state(delta):

	var speed = normal_speed
	if Input.is_action_pressed("sprint"):
		speed = sprint_speed

	var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")

	# Restrict diagonal movement
	if Input.is_action_pressed("ui_right") || Input.is_action_pressed("ui_left"):
		direction.y = 0
	elif Input.is_action_pressed("ui_up") || Input.is_action_pressed("ui_down"):
		direction.x = 0

	direction = direction.normalized()

	if direction != Vector2.ZERO:
		last_direction = direction

	velocity = direction * speed
	move_and_slide()

	# Movement animations only if not attacking
	if not attacking:
		moving=true
		if direction != Vector2.ZERO:
			if direction.x < 0:
				animated_sprite_2d.play("walk left")
			elif direction.x > 0:
				animated_sprite_2d.play("walk right")
			elif direction.y < 0:
				animated_sprite_2d.play("walk up")
			elif direction.y > 0:
				animated_sprite_2d.play("walk down")
		else:
			if last_direction.x < 0:
				animated_sprite_2d.play("idle left")
			elif last_direction.x > 0:
				animated_sprite_2d.play("idle right")
			elif last_direction.y < 0:
				animated_sprite_2d.play("idle up")
			elif last_direction.y > 0:
				animated_sprite_2d.play("idle down")

	# Trigger attack when the button is pressed
	if Input.is_action_just_pressed("attack"):
		state = ATTACK

func start_attack():
	attacking = true
	moving = false
	velocity = Vector2.ZERO

	hide_all_attack_sprites()
	animated_sprite_2d.visible = false

	var current_attack_sprite: AnimatedSprite2D = null

	if last_direction.x < 0:
		current_attack_sprite = attack_left
	elif last_direction.x > 0:
		current_attack_sprite = attack_right
	elif last_direction.y < 0:
		current_attack_sprite = attack_up
	elif last_direction.y > 0:
		current_attack_sprite = attack_down

	if current_attack_sprite:
		current_attack_sprite.visible = true
		current_attack_sprite.frame = 0
		current_attack_sprite.play("default")
		await current_attack_sprite.animation_finished
		current_attack_sprite.visible = false

	attacking = false
	animated_sprite_2d.visible = true
	state = MOVE

A gif or short video would have been useful here, as I did not understand what you were asking and had to figure out the problem solely by reading your code. I’m pretty sure I know what is wrong.

await current_attack_sprite.animation_finished

This line of code dutifully waits for the animation to finish. Presumably, because this is an animation, it takes at least two frames to complete the attack animation.

func _process(delta):

This is a Godot method that everything that inherits from Node has, and it is not nearly as gentle a soul as the await keyword. It is called once every frame, so approximately once every 1/60th of a second. On t=1, it calls start_attack. On the next frame (t=2), it will call start_attack a second time. On the frame after that, it will call start_attack again. It doesn’t particularly care that start_attack is waiting for a signal; it can call it again and again, until eventually the first animation finishes and state = MOVE is set.

If you wish to keep using your code as is, you need to block start_attack from being called while the animation is still playing. This will be messy and make your code worse, especially if you intend to add another type of animation. The cleanest solution is to refactor the code so that you have a state machine. If you search for ‘godot character animation state machine’ or something similar, you’ll probably find a ready-made solution you can use.

1 Like