When should I play animations and how to make sure an animation is finished before a different one is played. I am currently having a problem with my "punch" animation that only plays for a split second before it reverts back to the idle animation. This code is mostly testing things out so it is quite messy and I apologise.
extends CharacterBody2D
const SPEED = 130.0
const JUMP_VELOCITY = -600.0
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
@onready var animated_sprite = $AnimatedSprite2D
@onready var collision_top = $CollisionShape2DTop
@onready var raycast_crouch_1 = $RayCast2DCrouch1
@onready var raycast_crouch_2 = $RayCast2DCrouch2
@onready var hitbox = $AreaHitbox
var health = 100
var was_on_floor = false
var was_jumping_up = false
var land_timer = 0.0
var is_crouching = false
var crouching_under = false
var enemy_attack = false
var attack_cooldown = true
var alive = true
var is_punching = false
func _physics_process(delta):
update_health()
enemy_is_attacking()
if health <= 0:
alive = false
get_tree().reload_current_scene()
# Add the gravity.
if not is_on_floor():
velocity.y += gravity * delta
# Handle jump.
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
if Input.is_action_just_pressed("crouch") and is_on_floor():
crouch()
elif Input.is_action_just_released("crouch"):
if can_stand():
stand()
else:
if not crouching_under:
crouching_under = true
if crouching_under and can_stand() and not Input.is_action_pressed("crouch"):
stand()
crouching_under = false
# Get the input direction: -1, 0, 1
var direction = Input.get_axis("move_left", "move_right")
# Flip the sprite
if direction > 0:
animated_sprite.flip_h = false
elif direction < 0:
animated_sprite.flip_h = true
# Handle punch
if Input.is_action_just_pressed("punch"):
is_punching = true
# Determine animation state
if is_punching:
animated_sprite.play("punch")
is_punching = false # Punch animation is over
elif is_on_floor() and not is_punching:
if not was_on_floor:
animated_sprite.play("land")
land_timer = 0.1 # Play land animation for 0.1 seconds
elif land_timer > 0:
land_timer -= delta
elif direction == 0:
if is_crouching:
animated_sprite.play("crouch")
else:
animated_sprite.play("idle")
else:
if is_crouching:
animated_sprite.play("crouch")
else:
animated_sprite.play("run")
was_on_floor = true
was_jumping_up = false
else:
if velocity.y < 0:
if not was_jumping_up:
animated_sprite.play("jump_up")
was_jumping_up = true
elif velocity.y > -100 and velocity.y < 100:
animated_sprite.play("jump_max")
else:
animated_sprite.play("jump_down")
was_on_floor = false
# Handle movement
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
func can_stand() -> bool:
var result = not raycast_crouch_1.is_colliding() and not raycast_crouch_2.is_colliding()
return result
func crouch():
if is_crouching:
return
collision_top.disabled = true
is_crouching = true
func stand():
if is_crouching == false:
return
collision_top.disabled = false
is_crouching = false
func update_health():
var health_bar = $HealthBar
health_bar.value = health
if health >= 100:
health_bar.visible = false
else:
health_bar.visible = true
func player():
pass
func _on_regen_timer_timeout():
if health < 100:
health = health + 10
if health > 100:
health = 100
elif health <= 0:
health = 0
func enemy_is_attacking():
if enemy_attack and attack_cooldown:
health -= 20
attack_cooldown = false
$DamageCooldown.start()
print(health)
func _on_damage_cooldown_timeout():
attack_cooldown = true
func _on_area_hitbox_body_entered(body):
print("Entered")
enemy_attack = true
func _on_area_hitbox_body_exited(body):
if body.has_method("enemy"):
enemy_attack = false
I tried adding timers, conditions, etc. but I cant seem to figure it out and I am not sure what is the right way. I have seen multiple examples using different methods on how to do it so I am not sure which is the right way.
Your animation will immediately finish, because you reset your is_punching variable to early. The play function of the AnimationSprite will not hold your code till the animation is finished. So by setting is_punching false there your code will branch into your normal behaviour on the next call.
Instead you can use the signals of the AnimatedSprite. It has the signal animation_finished which is called every time a animation ends. You can connect a function to it through the editor in the right tree in the tab "Node".
Or connect it through code like this (I did it in the ready function):
func _ready():
animated_sprite.animation_finished.connect(self._on_animated_sprite_2d_animation_finished)
In the function you can check, if the animation was your "punch" animation. And if so reset your boolean:
func _on_animated_sprite_2d_animation_finished():
if animated_sprite.animation == "punch":
is_punching = false
The rest of your code can stay the same to have a working punch animation. Just remove the is_punching = false
part from the loop:
# Determine animation state
if is_punching:
animated_sprite.play("punch")
elif is_on_floor() and not is_punching: