Godot Version
4.4.1.stable
Question
I’m going in circles here banging my head…this should be a basic function of animationplayer in script. I should be able to go “animationplayer.stop()” and it just ends and clears the animation from playing/existing. But .stop() just resets the animation (why isn’t that .reset???). What I want simply is nowhere to be found in the documentation and somehow has never been asked before!
Here’s what I have:
- 2 animations: “charge 1” and “charge2”
- charge1 starts playing when the player starts holding down a key.
- charge1 is not looping and is set in the Animation timeline to play charge2 once it completes.
- charge2 is looping and is intended to loop over and over until the player releases the held-down key.
- However, no matter what I do, charge2 plays infinitely even after the key is released since there is no apparent way to make it stop. again, WTF. It’s permanately there at this point.
- Input.is_action_pressed doesn’t work here as it makes infinite bullets appear
func _process(_delta):
if Input.is_action_just_pressed("fire"):
animation_player.play("charge1")
if Input.is_action_just_released("fire"):
animation_player.stop()
Is animationplayer
and animation_player
intentionally two different variables?
typo just in here, sorry. Good catch though! I re-wrote it just a bit to make things simpler here.
So I just looked at the documentation, because I’ve never had this problem before. After reading it I didn’t think the problem was the AnimationPlayer. So I setup a little test and I can indeed press a button and stop()
or pause()
my looping idle animation. When I press the attack button, that animation starts and my idle animation continues as before whether stopped or paused.
Potential Solution #1
I think inadvertently you may not have given us all the information that is relevant. I suspect that you are using a controller and pressing either the right or left trigger - which is not a button. At least not as far as Godot is concerned. Triggers send a value from 0 to 1.0 telling you how far in they are pressed. And what you might be experiencing is that after action is released, it is also just pressed because it isn’t a boolean value.
If you’re not using a trigger on a controller, something else is going on. However I think altering your code might help. If it’s just a frame of input, then this may solve it.
func _process(_delta):
if Input.is_action_just_released("fire"):
animation_player.stop()
elif Input.is_action_just_pressed("fire"):
animation_player.play("charge1")
Potential Solution #2
It also might not be that. It might be that you have this in _process()
instead of _physics_process()
. I’m not sure why you chose to use _process()
, and there may be a very good reason; but my gut tells me this is better off in _physics_process()
because playing an animation is technically something you want framerate dependent.
Potential Solution #3
A third potential solution is to play the RESET animation when you want to stop. It’s one frame long, sets everything back to default and isn’t looped.
3 Likes
I’ve always struggled with .stop(). I actually dread it bc it never works for me. It never has.
PS1: Just using a keyboard, but that’s a good thought. Having the released loop first makes sense.
PS2: I’m not sure I necessarily have a good reason, but regardless the .stop() command should work in both.
PS3: I did try this before asking; it also did not work and my looping animation continued.
What I got to work was just not doing .stop() at all and rather creating once instance that plays charge1 and charge2, then when I want it to go away (fire bullet on action released) I queue free that instance and create another (fire_bullet) with the next thing in its place. I did leave a lot out of my code in the original post as I figured it wasn’t necessary, but here you go. It is working now, just with the oddity that I’m removing an instance and creating another. It’s really not such a big deal though, as the charge
## Constants
const Bullet = preload("res://player_bullet.tscn")
## Variables (export, normal, onready)
@onready var blaster_sprite = $BlasterSprite
@onready var muzzle = $BlasterSprite/Muzzle
@onready var charge_timer = $ChargeTimer
@onready var remote_transform_2d = $BlasterSprite/Muzzle/RemoteTransform2D
var charge
## Functions
func _process(_delta):
blaster_sprite.rotation = get_local_mouse_position().angle()
if Input.is_action_just_pressed("fire"):
charge_timer.start()
charge_bullet()
if Input.is_action_just_released("fire"):
fire_bullet()
if Input.is_action_pressed("fire") == false and charge != null:
charge.queue_free()
# function for charging up shots.
func charge_bullet():
charge = Utilities.instantiate_scene(Bullet, muzzle.global_position)
var charge_path = charge.get_path()
remote_transform_2d.remote_path = ""
remote_transform_2d.remote_path = charge_path
remote_transform_2d.force_update_cache()
var charge_animation = charge.get_node("Projectile/AnimationPlayer")
charge_animation.play("charge1")
# function for actually firing the shots.
func fire_bullet():
var bullet = Utilities.instantiate_scene(Bullet, muzzle.global_position)
var bullet_animation = bullet.get_node("Projectile/AnimationPlayer")
bullet.rotation = blaster_sprite.rotation
if charge_timer.is_stopped():
bullet_animation.play("charge_shot")
else:
bullet_animation.play("fire")
Glad you got it working. It’s still really weird. Something else is going on, but might be time to move on.
TBH, I don’t think I’ve ever used stop()
on an AnimationPlayer. But then I typically use them to keep running forever on something that always has an animation running (like a player or enemy with a default idle animation) or as one-shots like with splash screens.