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")
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"
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")
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")