How to stop player animation playing mid-air?

Godot Version

Godot v4.3 Windows 11 64-Bit
(2D Project)

Question

I managed to create a death animation for my sprite that plays when an enemy touches it. It works fine, but when I jump onto the enemy, the player sprite will play the animation mid-air instead of falling to the floor then doing the animation. How do I fix that?
Here’s a video of the issue
Here’s the player sprite code

You can add the following code to the _physics_process function and remove it from the die function:

if !is_alive and is_on_floor():
    timer.start()
    
    if not facing_right:
        $AnimatedSprite2D.flip_h = true
        $AnimationPlayer.play("die_right")
    else:
        $AnimationPlayer.play("die_right")

This way, your die animation will play only if the player is dead (triggered by the die function) and is on floor.

Then the animation doesnt even work. The timescale slows and the player loses ability to move, but no animation plays and the scene doesnt reload. Also the player just freezes mid-air.
Updated player code

You need to also change other parts of the code that only executes when the player is alive…

Like here. If u want it to keep falling if dead mid air, you need te remove the move_and_slide() from inside this if statement.

# Apply movement
if is_alive:
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
	move_and_slide()

The same is valid for the jump animation. Please consider re-reading your code and find incoherence regarding the is_alive variable.

So I removed the move_and_slide() but then the player loses the ability to move at all. I’m sorry but I don’t really understand what you’re telling me to do, especially in regards to finding “incoherence regarding the is_alive variable”, as I’m new to coding so don’t know much. Could you please elaborate on what I’m supposed to do with the is_alive variables, and why I should remove move_and_slide() if that results in the loss of movement for the pleyer. Thanks for the help and sorry for my misunderstanding

Oh, sorry if i seemed impatient.

The thing is, you are doing too many validations with the variable is_alive that can be a problem.

Here when I asked you to remove the function, it was from inside the if is_alive: piece.

Something like:

# Apply movement
if is_alive:
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

move_and_slide()

As you can see, now the move_and_slide function (that is the one responsable for making the player move) will trigger regardless of whether the player is alive or not.
So even if it collide with the slime, trigger the die function and updates the variable is_alive to false, the player body will keep moving and not just freeze mid air :slight_smile:

The same idea occours here:

if is_alive:
		if is_on_floor():
			if direction == 0:
				$AnimationPlayer.play("idle_right")
			if direction > 0:
				$AnimationPlayer.play("run_right")
				facing_right = true
			if direction < 0:
				$AnimationPlayer.play("run_right")
				facing_right = false
		else:
			$AnimationPlayer.play("jump")

Where the jump animation (that I believe is the same for falling) won’t trigger unless the player is alive.
You can just move the $AnimationPlayer.play("jump") line to here:

# Add the gravity.
if not is_on_floor():
    velocity += get_gravity() * delta
    $AnimationPlayer.play("jump") # added here

Another thing regarding the multiple if statements using the is_alive var goes in your last paste bin from line 35 to 64. There are too many validations for this variable that could be done only once, something like:

if is_on_floor():
	if is_alive:
		if direction == 0:
				$AnimationPlayer.play("idle_right")
			if direction > 0:
				$AnimationPlayer.play("run_right")
				facing_right = true
			if direction < 0:
				$AnimationPlayer.play("run_right")
				facing_right = false
			
			if direction:
				velocity.x = direction * SPEED
			else:
				velocity.x = move_toward(velocity.x, 0, SPEED)
	else:
		timer.start()
 
		if not facing_right:
			$AnimatedSprite2D.flip_h = true
			$AnimationPlayer.play("die_right")
		else:
			$AnimationPlayer.play("die_right")

move_and_slide()

Hi, I’m pretty sure I’ve now done everything you just said correctly, but the player can’t move now. The jump works, and the player switches direction if moving left or right, but it wont move when I press A or D (or arrow equivalents). Here’s the code.

This piece of code has bad identation. As you can see, you are checking if the direcation is higer or lower than zero only if the direction is equals to 0, which makes no sense.

if direction == 0:
    $AnimationPlayer.play("idle_right")
    if direction > 0:
        $AnimationPlayer.play("run_right")
        facing_right = true
	if direction < 0:
		$AnimationPlayer.play("run_right")
		facing_right = false
 
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

Try something like this:

if direction == 0:
    $AnimationPlayer.play("idle_right")
elif direction > 0:
    $AnimationPlayer.play("run_right")
    facing_right = true
elif direction < 0:
	$AnimationPlayer.play("run_right")
	facing_right = false

if direction:
	velocity.x = direction * SPEED
else:
	velocity.x = move_toward(velocity.x, 0, SPEED)

This is related to that part when I told you to find incoherences in your code. Even though you are new to it, please, don’t just follow tutorials blindly.
You still have the forum to ask any questions u need, reading and re-reading a lot your code, is what will make u grow stronger in coding.
Still, we’ll keep here to help u.

Thanks for helping me out, and I’m really sorry to take up your time, but I’ve found another issue now. I did all your steps so far, and re-read it multiple times to check everything, but now the jump is really wierd. Before, I could change direction mid jump, choose length of jump, jump up straight then move mid-air, which is all fine. But now I can only either jump up perfectly straight or jump full-length, there is no ability to decide length of jump or change direction mid-air, if that makes sense. Heres a video.

Don’t worry! It’s not moving mid air anymore because the code that changes the player x velocity is inside a if statement where it check if it is on the floor.
You can take this part of the code here:

if direction:
	velocity.x = direction * SPEED # this line increse player velocity to move
else:
	velocity.x = move_toward(velocity.x, 0, SPEED)

Remove it from inside the if is_on_floor(): above it and create a new if, something like:

if direction and is_alive: # if there is a input and player is alive, it will move
    velocity.x = direction * SPEED
else: # if not, it's velocity will decrease until 0 and stop
    velocity.x = move_toward(velocity.x, 0, SPEED)

This should work.

Yes, that works! The jumping is fine now, also, thanks to your help but also some of my own tinkering, I managed to get the player to fall to the floor THEN play the death animation, which is great! However now there is the problem where the death animation repeats on loop for the duration of the timer (0.6s, death animation is 0.4s) which I could fix by making the death timer the exact same duration as death animation. However this means as soon as the animation is done once, the level instantly restarts, whereas i want it to play once then remain on the final frame for a bit for dramatic effect. Problem No.2 is that the player can still turn left and right when dead, which kinda defeats the purpose of being dead lol.

I fixed the second problem! I just added an “if is_alive” to the section telling the player sprite to flip depending on direction, meaning if the player is dead it loses the ability to flip. Now the only thing to do is make it so the death animation stops on final frame instead of repeating. How would I do this?

I think you can do it in the animation itself. In the animation tab should have a button that indicates that the animation will loop. Maybe removing it, it will just stop in the last frame once it finishes.

The loop button in the die_right animation in the AnimationPlayer is already disabled. Is there a way to stop the animation on final frame with code then? Or instead of play(“die_right”) is there a way to tell it to play once only?

Oh, got it. Maybe it’s because the die animation is being played in the _physics_process function, so everytime it checks that the player is not alive, it will play it.

You can try to remove it from inside the _physics_process function by adding extra logic or just add a new conditional to the if statement by checking if the current animation is not the die ones.

To get the current animation you can use the $AnimationPlayer.get_current_animation(). It will return the name of the current animation being played.
More info at AnimationPlayer — Godot Engine (stable) documentation in English

I think a big problem in your code is that you are missing a state machine, which leads to alot of boolean variables and if-statements to check which logic and animation to run.

Look up state-machine on youtube and try to implement one. It will save you alot of time and effort

2 Likes

Thanks for the suggestion, I’ve now implemented a simple state machine, its much easier and all the animations are working fine. However I still can’t figure out how to make it so when the player rolls, the character moves faster and further in a certain direction for the duration of the roll animation. Currently it plays the animation but doesn’t move the player or increase speed. I tried using velocity.x = dir * roll_speed but that doesn’t work.
Here’s the new player code.

nevermind I’ve figured it out. Thank you @matheus-depaula for all your help and thank you @herrspaten for the state machine suggestion, it makes things so much easier.

2 Likes

Sure, at any time! Don’t forget to close the topic.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.