Search code examples
animationgodotgodot4

Animation of enemy keeps looping even with unchecked looping mark


Death animation of enemy is keep looping even though I unchecked the looping mark. Does my code has problem or is it just a error in godot?

I keep checked looping mark and change the code in different way but the result was same. But if I erase DEATH state and put animation in kill fuction animation does not work.

This is my code:

extends CharacterBody3D

@export var attack_range = 2.0
@onready var move_speed = 6
@onready var player_entered = false
@onready var player: CharacterBody3D = get_tree().get_first_node_in_group("player")
@onready var sprite = $AnimatedSprite3D
var dead = false

enum States {IDLE, FOLLOW, DEATH}

var state = States.IDLE

func _physics_process(delta):
    match state:
        States.IDLE:
            idle()
        States.FOLLOW:
            follow()
        States.DEATH:
            death()

func change_state(newState):
    state = newState
func idle():
    print("idle")
    sprite.play()
    if player_entered:
        change_state(States.FOLLOW)
func follow():
    print("follow")
    sprite.play("run")
    var dir = player.global_position - global_position
    dir.y = 0.0
    dir = dir.normalized()
    
    velocity = dir * move_speed
    move_and_slide()
    attempt_to_kill_player()
    if dead:
        change_state(States.DEATH)
func death():
    print("died")
    sprite.play("die")
    

func attempt_to_kill_player():
    var dist_to_player = global_position.distance_to(player.global_position)
    if dist_to_player > attack_range:
        return
        
    var eye_line = Vector3.UP * 1.5
    var query = PhysicsRayQueryParameters3D.create(global_position+eye_line, player.global_position+eye_line, 1)
    var result = get_world_3d().direct_space_state.intersect_ray(query)
    if result.is_empty():
        player.kill()

func kill():
    dead = true
    $CollisionShape3D.disabled = true

func _on_playerdetection_body_entered(body):
    player_entered = true

Solution

  • _physics_process is still called every physics frame. So if your in the death state. The death function is called every frame. The sprite.play("die") call will play the animation again, when a animation finished, so you have to change the code there.

    One possible change could be, to create a flag, which is set to true after the animation played once. This can be done in the animation finished signal handler.

    var death_animation_played = false
    
    func _on_animated_sprite_2d_animation_finished():
        if sprite.animation == "die":
            death_animation_played = true
    
    func death():
        print("died")
        if death_animation_played == false:
            sprite.play("die")
    

    Connect the function _on_animated_sprite_2d_animation_finished to the animation_finished signal of you AnimatedSprite2D.

    Edit: Just got an idea, which is even easier. Check which animation is/was playing last and only then play the die animation, if it wasnt the last one:

    func death():
        print("died")
        if not sprite.animation == "die":
            sprite.play("die")