Top Down 2D Sprint ❤️

Godot Version

4.4.1

Question

How would I go about adding a sprint to my top-down 2D game?

I am very very new to Godot as I started only 2 days ago and have made amazing progress! But things like this have me stumped.

extends CharacterBody2D

const sprint = 2
@export var move_speed : float = 60
@export var starting_direction : Vector2 = Vector2(0, 1)

@onready var animation_tree = $AnimationTree
@onready var state_machine = animation_tree.get("parameters/playback")

func _ready():
	update_animation_parameters(starting_direction)

func _physics_process(_delta):
	#Get input direction.
	var input_direction = Vector2(
		Input.get_action_strength("right") - Input.get_action_strength("left"),
		Input.get_action_strength("down") - Input.get_action_strength("up")
		#Check if sprinting
		)
	
	update_animation_parameters(input_direction)
	# Update velocity.
	velocity = input_direction * move_speed

# Move and Slide function uses velocity of character body to move character on map
	move_and_slide()
	
	pick_new_state()
	
func update_animation_parameters( move_input : Vector2):
	# Dont change animation parameters if there is no move input.
	if(move_input != Vector2.ZERO):
		animation_tree.set("parameters/Walk/blend_position", move_input)
		animation_tree.set("parameters/Idle/blend_position", move_input)

# Choose state based on what is happening with the player
func pick_new_state():
	if(velocity != Vector2.ZERO):
		state_machine.travel("Walk")
	else:
		state_machine.travel("Idle")
	else: state_machine.travel("Run")

Hi,

The most simple way would be to declare another speed variable for the sprint speed (exactly as you did for move_speed), and then, instead of multiplying the input_direction by move_speed, multiply it by your sprint speed if the sprint input is held down.
You’ve already used input system and if statements so that should not be that much of a deal to implement, but let me know if you need a more in depth explanation.

Also, it will probably be useful to store a boolean is_sprinting to let you know if the character is sprinting or not; that is very convenient when it comes to play animations, change the footstep sounds or VFX, that kind of stuff. But you should start by changing the speed and then work on the visual.

3 Likes

Good evening!
Thank you so much for your reply! I tried your suggestion and the sprint does work. Though after I sprint, my player seems to get stuck in it’s it’s idle animation, no longer showing the walking animations, and only facing to the right while I’m sprinting.

This is what I have, forgive me posting the whole thing, again:

extends CharacterBody2D

const sprint = 2
@export var move_speed : float = 60
@export var sprint_speed : float = 120
@export var is_sprinting = false
@export var starting_direction : Vector2 = Vector2(0, 1)

@onready var animation_tree = $AnimationTree
@onready var state_machine = animation_tree.get("parameters/playback")

func _ready():
	update_animation_parameters(starting_direction)

func _physics_process(_delta):
	#Get input direction.
	var input_direction = Vector2(
		Input.get_action_strength("right") - Input.get_action_strength("left"),
		Input.get_action_strength("down") - Input.get_action_strength("up")
		)
	
	update_animation_parameters(input_direction)
	# Update velocity.
	velocity = input_direction * move_speed
	if Input.get_action_strength("sprint"):
		velocity = input_direction * sprint_speed
		is_sprinting = true
	else:
		is_sprinting = false

# Move and Slide function uses velocity of character body to move character on map
	move_and_slide()
	
	pick_new_state()
	
func update_animation_parameters( move_input : Vector2):
	# Dont change animation parameters if there is no move input.
	if(move_input != Vector2.ZERO):
		animation_tree.set("parameters/Walk/blend_position", move_input)
		animation_tree.set("parameters/Idle/blend_position", move_input)

# Choose state based on what is happening with the player
func pick_new_state():
	
	if(velocity != Vector2.ZERO):
		state_machine.travel("Walk")
	if is_sprinting:
		state_machine.travel("Run")
	else:
		state_machine.travel("Idle")

That part of the code seems to be wrong:

if(velocity != Vector2.ZERO):
    state_machine.travel("Walk")
if is_sprinting:
    state_machine.travel("Run")
else:
    state_machine.travel("Idle")

You’re basically saying : walk if there is a velocity, and then, run if sprinting or idle if not sprinting. The walk state will never be used as there’s an if/else statement below that will play either run or idle state.

Something like this would work better:

if velocity != Vector2.ZERO:
    if is_sprinting:
        state_machine.travel("Run")
    else:
        state_machine.travel("Walk")
else:
    state_machine.travel("Idle")

Handle the movement states in a single if statement block, and if there’s no velocity, you don’t have to care about the sprint, just play the idle state.

Hope that helps.

1 Like

Thank you! This suggestion is very helpful and it got the idle states working properly, but now I am also having issues with my running animations.

This is a really odd one, since the animations work as they are meant to from side to side but not up and down. I’m not really sure if I should make a new topic for this, since it is related to the sprint.

Here is my code once again just in case and a video for context of what I’m talking about:

extends CharacterBody2D

const sprint = 2
@export var move_speed : float = 60
@export var sprint_speed : float = 120
@export var is_sprinting = false
@export var starting_direction : Vector2 = Vector2(0, 1)

@onready var animation_tree = $AnimationTree
@onready var state_machine = animation_tree.get("parameters/playback")

func _ready():
	update_animation_parameters(starting_direction)

func _physics_process(_delta):
	#Get input direction.
	var input_direction = Vector2(
		Input.get_action_strength("right") - Input.get_action_strength("left"),
		Input.get_action_strength("down") - Input.get_action_strength("up")
		)
	
	update_animation_parameters(input_direction)
	# Update velocity.
	velocity = input_direction * move_speed
	if Input.get_action_strength("sprint"):
		is_sprinting = true
		velocity = input_direction * sprint_speed

	else:
		is_sprinting = false

# Move and Slide function uses velocity of character body to move character on map
	move_and_slide()
	
	pick_new_state()
	
func update_animation_parameters(move_input : Vector2):
	# Dont change animation parameters if there is no move input.
	if(move_input != Vector2.ZERO):
		animation_tree.set("parameters/Run/blend_position", move_input)
		animation_tree.set("parameters/Walk/blend_position", move_input)
		animation_tree.set("parameters/Idle/blend_position", move_input)

# Choose state based on what is happening with the player
func pick_new_state():
	if velocity != Vector2.ZERO:
		if is_sprinting:
			state_machine.travel("Run")
		else:
			state_machine.travel("Walk")
	else:
			state_machine.travel("Idle")

The code seems right, I don’t see why the up/down animations specifically are not working properly. Indeed, it may be a good call so start another thread as it seems more related to animations + the initial question has a post tagged as solution so I feel like it would be better not to flood that thread so much? Not sure about what would be the best call according to the forum moderators though :sweat_smile:

1 Like

I’ll go ahead and make a separate thread for this issue then! I think a more specific title is def needed. Thank you so much for all your help! I really appreciate it :heart::folded_hands:t5:

1 Like