My crouch animation of two frames is skipping the second one

Godot Version

4.4.1

Question

I’m trying to add a crouch animation that consists of just two frames. I’m using AnimationPlayer, it all works except that the animation takes just the first frame and skip the second. All other animations like run and jump work fine, it’s just the crouch one.

I think the problem might be here maybe ?

	if is_on_floor():
		if is_crouching:
			animation_player.play("crouch")

#Crouch
	if Input.is_action_just_pressed("crouch"):
		crouch()
	elif Input.is_action_just_released("crouch"):
		stand()
	
	move_and_slide()
	
func crouch():
	is_crouching = true
func stand():
		is_crouching = false

There is an issue with the way you play the crouch animation. It looks like it calls play every time is_on_floor is true while the player is crouching, which makes the animation start over and over again because it is being called to play every process frame. Try adding a statement that checks if the crouch animation has already finished before the animation_player.play("crouch"). Hope this helps!

1 Like

Thanks, but i only end up messing up other stuff, not sure how to write that statement.

Try swapping if is_crouching for if is_crouching and not animation_player.is_playing(). Additionally, if you haven’t already, you can set the loop mode of the animation to “linear” so the animation keeps repeating and doesn’t need to be restarted all the time.

2 Likes

Thanks, but swapping it breaks the animation completely, also the animation to linear dont make any change.

Oh my bad, I assume that’s because you’re already playing an animation before crouching? In that case, I would just move the animation_player.play("crouch") into crouch() (without keeping the if-statements) If there is no other animation playing and moving the line doesn’t work I don’t know what could work, sorry

1 Like

It’s just a total basic 2d crouch animation, so it should start when idle or when running. It does work, but it’s a two-frame animation and it only use the first frame. I’m totally new to coding and my brain is melting as i come from 2d/3d art and it’s not this brutal :sweat_smile:

So even with this code (just what I said put into code):

#	if is_on_floor():
#		if is_crouching:
#			animation_player.play("crouch")

#Crouch
	if Input.is_action_just_pressed("crouch"):
		crouch()
	elif Input.is_action_just_released("crouch"):
		stand()
	
	move_and_slide()
	
func crouch():
	is_crouching = true
	animation_player.play("Crouch")
func stand():
		is_crouching = false

only the first frame plays?

It gives me the error “standalone lambdas cannot be accessed”

This is the script attached to the player, can anyone tell me if something might be blocking the crouch animation from displaying the second frame ? (it’s a two-frame animation):

extends CharacterBody2D

const SPEED = 150
const JUMP_VELOCITY = -300.0

@onready var animation_player: AnimationPlayer = $AnimationPlayer  # Adjust path if necessary

var reverse_to_right = false
var reverse_to_left = true
var is_animation_finished = true
var moving_left := false  # Defaults to false
var is_crouching = false
@onready var sprite = $Sprite2D

func _physics_process(delta: float) -> void:

# Add the gravity
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	var direction := Input.get_axis("move_left", "move_right")

	# Flip the sprite
	if direction > 0:
		$Sprite2D.flip_h = false  # Assuming your Sprite node is named "Sprite2D"
	elif direction < 0:
		$Sprite2D.flip_h = true   # Flip horizontally if moving left
	#else:
	$Sprite2D.flip_h = $Sprite2D.flip_h  # No change if direction is 0 (optional, you could remove this line)

	# Play animations
	if is_on_floor():
		if direction == 0:
			animation_player.play("idle")
		else:
			animation_player.play("run")
	else:
		animation_player.play("jump")

	#Crouch Animation
	if is_on_floor():
		if is_crouching:
			animation_player.play("crouch")

	#Collision for crouching
	if Input.is_action_pressed("crouch"):
		$CollisionShapeCrouch.disabled = false
		$CollisionShapeStanding.disabled = true

	else:
		$CollisionShapeCrouch.disabled = true
		$CollisionShapeStanding.disabled = false

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

	#Crouch
	if Input.is_action_just_pressed("crouch"):
		crouch()
	elif Input.is_action_just_released("crouch"):
		stand()

	move_and_slide()

func crouch():
	is_crouching = true
func stand():
		is_crouching = false

Try adding this helper function that checks if the current animation is different from the one being requested:

func play_animation(requested: String) -> void:
	if animation_player.current_animation != requested:
		animation_player.play(requested)

Then change your animation state handling from this:

    # Play animations
	if is_on_floor():
		if direction == 0:
			animation_player.play("idle")
		else:
			animation_player.play("run")
	else:
		animation_player.play("jump")

	#Crouch Animation
	if is_on_floor():
		if is_crouching:
			animation_player.play("crouch")

to this:

    if is_on_floor():
		if Input.is_action_pressed("crouch"):
			play_animation("crouch")
		elif direction == 0:
			play_animation("idle")
		else:
			play_animation("run")
	else:
		play_animation("jump")

Also, I suggest you optimize your code because there are a lot of redundancies and unnecessary functions.