The "Shoot" animation plays way too fast

Godot Version

4.1.2

Question

Hello all,

I’m new to Godot and tried my hand at developing a small 2D platformer on my own after following Udemy courses and YT.
And I’ve run into a few obstacles where I could use the advice and help of the good people here.

My player character currently has 4 basic states that work fine with all the animations. (Move, Jump, Fall, Idle).
And I’ve attempted to introduce 2 more states. (Shoot, Air_shoot)
I’m using a node-based state architecture to manage the states.
The Player character is animated with Sprite2D and AnimationPlayer nodes. Haven’t used an AnimationTree node.

The problem I’m facing now is that whenever I play the “Shoot” button (In this example. let’s say pressing the Shoot button while in the “Idle” state), the animation plays very quickly. It is barely noticeable. I need the animation to play for a bit longer. At least for it to be visible.
Increasing the “Shoot” animation length from 0.4s to 1s didn’t help.

Your help and advice on how to get this rectified would be much appreciated.

Player script:

extends CharacterBody2D

const SPEED = 300
const jump_velocity = -400
@onready var sprite_2d = $Sprite2D
@onready var animation_player = $AnimationPlayer
@onready var animation_tree = $AnimationTree



var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")

var movement_input = Vector2.ZERO
var jump_input = false
var jump_input_actuation = false
var shoot_input = false

var current_state = null
var prev_state = null

@onready var STATES = $STATES

# Called when the node enters the scene tree for the first time.
func _ready():
	for State in STATES.get_children():
		State.STATES = STATES
		State.Player = self
	
	prev_state = STATES.IDLE
	current_state = STATES.IDLE


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta):
	player_input()
	change_state(current_state.update(delta))
	$Label.text = str(current_state.get_name())
	move_and_slide()

func apply_gravity(delta):
	if not is_on_floor():
		velocity.y += gravity * delta

func change_state(input_state):
	if input_state != null:
		prev_state = current_state
		current_state = input_state
		
		prev_state.exit_state()
		current_state.enter_state()
	
func player_input():
	movement_input = Vector2.ZERO
	if Input.is_action_pressed("Left"):
		movement_input.x -= 1
		sprite_2d.flip_h = true
	if Input.is_action_pressed("Right"):
		movement_input.x += 1
		sprite_2d.flip_h = false
	if Input.is_action_pressed("Jump"):
		jump_input = true
	else:
		jump_input = false
		
	if Input.is_action_just_pressed("Jump"):
		jump_input_actuation = true
	else:
		jump_input_actuation = false
		
	if Input.is_action_just_pressed("Shoot"):
		shoot_input = true
	else:
		shoot_input = false

“Shoot” state script. The animation play and changing to other states from the “Shoot” state is handled from here. :

extends "State.gd"

func update(delta):
	Player.apply_gravity(delta)	
	Player.animation_player.play("shoot")
	if Player.is_on_floor():
		return STATES.IDLE
	if Player.velocity.x != 0:
		return STATES.MOVE
	return null

Is it possible that the “shoot” animation is overridden by another state playing an animation? Could you maybe add debug print statements to all the places where you call animation_player.play and see if it matches up with what you expect?

Alternatively, you could also connect to the current_animation_changed signal of the AnimationPlayer and print out the name.

For example, if the output looks like this:

00:00:00 In Idle state: Playing "idle" animation
00:00:05 In Shoot state: Playing "shoot" animation
00:00:05 In Idle state: Playing "idle" animation

…then the animation is short because the “idle” animation is played immediately afterwards.

Found the solution.

Added the below

  • A bool variable to determine whether the animation is currently played. False by default. (var shooting_anim_playing)
  • A timer with the minimum amount of time required for the animation to be played. (var shoot_animation_timer = $ShootAnimationTimer)

In the “enter_state()” function of the “Shoot” state, start the timer, play the animation, and set the bool variable to true.
Once the timer is timed out, reset the bool variable to false.

extends "State.gd"

@onready var shoot_animation_timer = $ShootAnimationTimer

@export var shoot_animation_duration = 0.4
var shooting_anim_playing = false

func update(delta):
	Player.velocity.x = 0
	Player.apply_gravity(delta)
	if !shooting_anim_playing:
			return STATES.IDLE
	return null
	
		
func enter_state():
	shoot_animation_timer.start(shoot_animation_duration)
	Player.animation_player.play("shoot")
	shooting_anim_playing = true

func exit_state():
	shooting_anim_playing = false

func _on_shoot_animation_timer_timeout():
	shooting_anim_playing = false

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