I'm running a script every physics frame to check if the player is looking at an object. Then I want to display a HUD item using the AnimationPlayer. Here is what my code looks like:

func _physics_process(_delta):
	if get_collider():
		is_looking = true
	else:
		is_looking = false
	update_hud()

func update_hud():
	if is_looking:
		$AnimationPlayer.play("Use")
	else:
		$AnimationPlayer.play_backwards("Use")

The problem with this method is that every frame, it plays the animation from its 1st keyframe. I thought a bit about it, but couldn't find a good method of achieving this. How can I achieve such thing? Are there better methods of doing this?

For this I always just keep a bool that tells whether or not the animation is busy. Animations can shoot out signals for when they’re done so you can flip the bool back to not busy.

if !animationBusy:
	if is_looking:
 		$AnimationPlayer.play("Use")
 	else:
		$AnimationPlayer.play_backwards("Use")

But the problem is that when the animation is over, it's just going to repeat itself.

I think you can set whether an animation repeats or not. You can also check whether a particular animation is playing rather than introducing a new variable.

$Animation.loop = false
if not $AnimationPlayer.is_playing():
    # play a new animation

@cybereality said: I think you can set whether an animation repeats or not. You can also check whether a particular animation is playing rather than introducing a new variable.

In this case $AnimationPlayer.play("Use") is being fired every frame. So it will loop regardless of the animation setting. What I have in mind is to show the UI element when looking = true and hide it when looking = false. I've seen someone else do this with tweens but with that method, the tween animation was still being called every frame and I really like the control the animationplayer gives me. That's why I want to use it.

Yes, that is why you check if the animation is playing first, before you play it, with the code I posted above.

@cybereality said: Yes, that is why you check if the animation is playing first, before you play it, with the code I posted above.

It still loops. However, instead of the animation starting every frame, it starts the first frame after its finished. Right now, I have a fading in animation, so it keeps flashing as it fades in, and starts to fade in again and again.

You have to set the signal then when the animation is done.

func _ready():
    $AnimationPlayer.connect("animation_finished", self, "on_animation_finished")

func on_animation_finished(name):
    if name == "Use":
        $AnimationPlayer.stop()

But that still doesn't work. It will still run $AnimationPlayer.play("Use") right after this. The animation is not looping on it's own. The code keep starting the animation.

I actually figured it out. It was way simpler than I'd thought :# . Here is the final code, hope it would help others:

var should_show = false

    func _physics_process(_delta):
        if get_collider():
            is_looking = true
        else:
            is_looking = false
        update_hud()
    func update_hud():
        if should_show:
			if looking:
				$AnimationPlayer.play("Use")
				should_show = false
        else:
			if looking == false:
				$AnimationPlayer.play_backwards("Use")
				should_show = true

Yes, that is the easiest way to deal with it. My solution would be more flexible if you had a lot of different animations, though, because then you might need booleans for each state, while the animation/signal method could be generic. But whatever works for you.

a year later
2 years later

I know this is old but one way is to disable The Loop on the Animation node in the Godot IDE, their should be a toggle for Loop, this is how I do it, but I would prefer to do it through the code my
self.