Sprinting animation not playing

Godot Version

Godot_v4.2.2

Question

Hi,
I’m very new to programming and game development in general, and I’m trying to make a 2D platformer in Godot as my first project.
I was trying to make the player sprint but for some reason I can’t make the sprinting animation play.

Here’s my script so far:

extends CharacterBody2D

@export var GRAVITY : int = 900
@export var SPEED : int = 250
@export var JUMP_FORCE : int = 300

@export var SPRINT_VELOCITY : int = 2
@export var DASH_SPEED : int = 0

func _physics_process(delta):

Walk

var direction = Input.get_axis("move_left","move_right")

if direction:
	velocity.x = direction * SPEED
	
	if is_on_floor():
		$AnimationPlayer.play("walk")

Sprint

	if Input.is_action_pressed("sprint"):
		
		velocity.x *= SPRINT_VELOCITY
		
		$AnimationPlayer.play("run2")

Idle

else:
	velocity.x = 0
	
	if is_on_floor():
		$AnimationPlayer.play("idle")

Rotate

if direction == 1:
	$Sprite2D.flip_h = false
elif direction == -1:
	$Sprite2D.flip_h = true

Gravity

if not is_on_floor():
	
	velocity.y += GRAVITY * delta
	
	if velocity.y > 0:
		$AnimationPlayer.play("fall")

Jump

if Input.is_action_just_pressed("jump") and is_on_floor():
	
	velocity.y -= JUMP_FORCE
	
	$AnimationPlayer.play("jump")



move_and_slide()

Instead of playing the sprint animation as intended, it just plays the first frame of the animation while the player is sprinting.

No, sorry (edited my last answer), I think I know what’s going on. You separated your code in titles so it’s a bit confusing (next time please just comment the code instead of splitting it)

if direction:
	velocity.x = direction * SPEED
	if is_on_floor():
		$AnimationPlayer.play("walk")
		if Input.is_action_pressed("sprint"):
			velocity.x *= SPRINT_VELOCITY
			$AnimationPlayer.play("run2")
else:
	velocity.x = 0

The thing is, you’re calling the Play animation for every _process frame, resetting it every frame. This is why you always see the first frame.
When you call “play” the animation resets.
You need to do something like this:

		if Input.is_action_pressed("sprint"):
			velocity.x *= SPRINT_VELOCITY
			if animation_player.current_animation != "sprint" or not animation_player.is_playing():
				animation_player.play("sprint")

Aren’t you having the same problem with the walk animation?
If so, you could just make a new method making sure you don’t reset the animation if it’s already playing:

func set_current_animation(animation_name : String):
	if animation_player.current_animation != animation_name or not animation_player.is_playing():
		animation_player.play(animation_name)

And then:

	if direction:
		velocity.x = direction * SPEED
		if Input.is_action_pressed("sprint"):
			velocity.x *= SPRINT_VELOCITY
			set_current_animation("sprint")
		else:
			set_current_animation("walk")
	else:
		velocity.x = 0
		set_current_animation("idle")
1 Like

Alternatively, if you animation_player is always playing, you could just set the current_animation variable and that will change the animation.
I prefer to have the control of then the animation plays and stops, but if it works for you the alternative exists.


func _ready():
	animation_player.current_animation = "idle"


func _physics_process(delta):

	var direction = Input.get_axis("ui_left","ui_right")

	if direction:
		velocity.x = direction * SPEED
		
		if Input.is_action_pressed("sprint"):
			velocity.x *= SPRINT_VELOCITY
			animation_player.current_animation = "sprint"
		else:
			animation_player.current_animation = "walk"
	else:
		velocity.x = 0
		animation_player.current_animation = "idle"
1 Like

Thanks for the quick reply!
I went with your first method and it got the sprint animation to play properly.
The problem is that now the jump and fall animations are not playing as they were before.

Here’s the script changes:

func set_current_animation(animation_name : String):

if $AnimationPlayer.current_animation != animation_name or not $AnimationPlayer.is_playing():
	$AnimationPlayer.play(animation_name)

func _physics_process(delta):

var direction = Input.get_axis("move_left","move_right")

if direction:
	velocity.x = direction * SPEED
	
	# Sprint
	if Input.is_action_pressed("sprint"):
		velocity.x *= SPRINT_VELOCITY
		set_current_animation("run2")
	else:
		set_current_animation("walk")
	
else:
	velocity.x = 0
	set_current_animation("idle")

I also changed the jump and fall animations to the set_current_animation func and the result is the same.

# Gravity

if not is_on_floor():
	velocity.y += GRAVITY * delta
	
	if velocity.y > 0:
		set_current_animation("fall")
	
# Jump

if Input.is_action_just_pressed("jump") and is_on_floor():
	
	velocity.y -= JUMP_FORCE
	
	set_current_animation("jump")

The best you can do so the animation flow is clear to you is to separate the animations from the physics math.
That way, all the animation code is unified and they don’t conflict with each other or with the physics logic.
Just remove all calls for the animations in your code and do this before the move_and_slide()

#Animations
if is_on_floor():  #it means the character is either idle, running or sprinting
	if velocity.x == 0:
		set_current_animation("idle")
	else:
		if Input.is_action_pressed("sprint"):
			set_current_animation("sprint")
		else:
			set_current_animation("walk")
else: #it means the character is either jumping or falling
	if velocity.y <= 0:
		set_current_animation("jump")
	else:
		set_current_animation("fall")
1 Like

Thank you!
Now everything is working as intended.
I just have one more question, if it’s ok to ask.
Is there a way to delay the idle animation, like by half a second during the change of direction input when sprinting, or to add a transitory animation in between the change of direction instead of immediately playing the idle animation?
Because the immediate transition flashes from one animation to another and it looks weird.

Sure, there are several ways. You could do something like this:
define a variable

var last_time_walked = 0

and then

	#Animations
	if is_on_floor():  #it means the character is either idle, running or sprinting
		var current_time = Time.get_ticks_msec()
		var delay = 300 #in milliseconds
		if velocity.x != 0:
			last_time_walked = Time.get_ticks_msec()
			
		if velocity.x == 0 and current_time - last_time_walked > delay:
			set_current_animation("idle")
		else:
			if Input.is_action_pressed("sprint"):
				set_current_animation("sprint")
			else:
				set_current_animation("walk")
	else: #it means the character is either jumping or falling
		if velocity.y <= 0:
			set_current_animation("jump")
		else:
			set_current_animation("fall")

1 Like

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