Dont know how to add a rolling animation

Godot Version

4.4.1

Question

I’m learning how to make a platformer and I’ve been stuck on adding a roll animation, I want the animation to only work while pressing a direction and down. Here is my code:

extends CharacterBody2D

const SPEED = 130.0
const JUMP_VELOCITY = -300.0

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D

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.
# As good practice, you should replace UI actions with custom gameplay actions.
var direction := Input.get_axis("move_left", "move_right")
if direction:
	velocity.x = direction * SPEED
else:
	velocity.x = move_toward(velocity.x, 0, SPEED)

#direction
if Input.is_action_just_pressed("move_left"):
	animated_sprite.flip_h = true
if Input.is_action_just_pressed("move_right"):
	animated_sprite.flip_h = false

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

move_and_slide()

Hi!

You can define an action in your input mapping for rolling, and check if player is rolling based on a few conditions. A typical example is that you first want the character to be on ground.
This would be something like this:

var rolling: bool = false

func _physics_process(delta: float) → void:
    # Gravity, jump, move, etc.

    # Check if character is rolling.
    if is_on_floor() and Input.is_action_pressed("roll"):  # and any other condition...
        rolling = true
    else:
        rolling = false

    # Animations
    if rolling:
        animated_sprite.play("rolling")
    else:
        # Other animations...

    move_and_slide()

And of course, if you need to check two inputs as you mentionned, simply add another “Input.is_something_pressed()” function in the condition.

Then, based on rolling value, I suppose you also need to change the speed of the character or anything else. Having a boolean is very convenient here as you only need to check it to know what to do with any parameter afterward.

I believe that would do the job for a roll that lasts as long as you’re pressing the correct inputs, which is what I understand here but I might be wrong.
Let me know if you were talking about a roll that’s triggered once when pressing a key and stopping after the roll itself is finished, as this would be done differently.

1 Like

Yea I don’t want it to be infinite

Okay, then you would need to store the info that the character is currently rolling.
Basically, you can define a boolean:

var is_rolling: bool = false

And change its value to true based on the same condition as above:

if is_on_floor() and Input.is_action_pressed("roll"):  # and any other condition...
    is_rolling = true

The difference is now, to set the boolean back to false, you need to wait the roll animation is done.
One simple way of doing that is using a Timer, and use its timeout signal to detect the roll is over.
To make it more readable, define two functions roll and roll_end, and call the roll function instead of setting is_rolling to true (which is now done in the function). Something like this:

func roll():
    is_rolling = true

    var timer: Timer = Timer.new()
    timer.wait_time = 1.0  # 1 second roll
    timer.timeout.connect(roll_end)
    add_child(timer)
    timer.start()


func roll_end():
	is_rolling = false

Then, use the is_rolling boolean to change animations and anything else.

Let me know if that helps. Keep in mind that you can write your own code if it suits your need better than mine, I just hope you have a better understanding on how you could do your roll mechanic :slight_smile:

1 Like

thank you so much! :grin:

I have a little problem it doesn’t take time to roll did i do something wrong in my code?

extends CharacterBody2D


const SPEED = 130.0
const JUMP_VELOCITY = -300.0

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var is_rolling: bool = false


var rolling: bool = false
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

func roll():
	is_rolling = true

	var timer: Timer = Timer.new()
	timer.wait_time = 1.0  # 1 second roll
	timer.timeout.connect(roll_end)
	add_child(timer)
	timer.start()


func roll_end():
	is_rolling = false

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction := Input.get_axis("move_left", "move_right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
	#rolling
	if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
		is_rolling = true
	elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
		is_rolling = true
	#movement
	if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
		rolling = true
	elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
		rolling = true
	else:
		rolling = false
	if Input.is_action_just_pressed("move_left"):
		animated_sprite.flip_h = true
	if Input.is_action_just_pressed("move_right"):
		animated_sprite.flip_h = false

	#animations
	if rolling:
			animated_sprite.play("rolling")
	elif is_on_floor():
		if direction == 0:
			animated_sprite.play("idle")
		elif is_on_floor() and rolling:
			animated_sprite.play("rolling")
		else:
			animated_sprite.play("run")
	else:
		animated_sprite.play("jump")

	move_and_slide()

Many issues here:

1/ You have two booleans for the same thing:

@onready var is_rolling: bool = false

var rolling: bool = false

You should have only one.

2/ You don’t seem to be calling roll() at all: you’re just setting the is_rolling boolean to true but not using the function (thus not triggering the timer).

In your code, you have:

#rolling
if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
    is_rolling = true
elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
    is_rolling = true

while you should have:

#rolling
if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
    roll()
elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
    roll()

3/ All of your logic is now in roll_end(), for some reason I don’t get. What was before in _physics_process() (animations, velocity, etc.) should not be in roll_end().
The roll end should only reset the is_rolling value to false, and then, the physics process is checking that boolean to know what animations to play. Nothing more.

Last detail: you probably don’t want to roll while already rolling, so you could add a condition before triggering the roll:

#rolling
if not is_rolling:
    if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
        roll()
    elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
        roll()

Let me know if that helps.

1 Like

So on 3 I should move only roll_end or move roll with it, also should I move every is_rolling call under roll_end?

Everything that’s related to the roll starting (is_rolling to true, starting the timer, etc.) is done in roll, which should be called once on start.
Everything that’s related to the roll ending (is_rolling to false, maybe a specific animation, etc.) is done in roll_end, which should be called once on end.

The rest can be done in _physics_process since you have a is_rolling boolean to know that you’re rolling, that function being called every on physics update.

1 Like

sorry I feel like I’m pestering you but I think I did everything correctly but it still doesn’t wait a second to play a new animation
here’s all my code:

extends CharacterBody2D


const SPEED = 130.0
const JUMP_VELOCITY = -300.0

@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
var is_rolling: bool = false

func roll():
	is_rolling = true

	var timer: Timer = Timer.new()
	timer.wait_time = 1.0  # 1 second roll
	timer.timeout.connect(roll_end)
	add_child(timer)
	timer.start()


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.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction := Input.get_axis("move_left", "move_right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
	
	#movement
	if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
		roll()
	elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
		roll()
	else:
		is_rolling = false
	if Input.is_action_just_pressed("move_left"):
		animated_sprite.flip_h = true
	if Input.is_action_just_pressed("move_right"):
		animated_sprite.flip_h = false

	#animations
	if is_rolling:
			animated_sprite.play("rolling")
	elif is_on_floor():
		if direction == 0:
			animated_sprite.play("idle")
		else:
			animated_sprite.play("run")
	else:
		animated_sprite.play("jump")

	move_and_slide()
	#rolling


func roll_end():
	is_rolling = false

That part is the problem:

if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
    roll()
elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
    roll()
else:
    is_rolling = false

That code is saying: “if is on floor and pressing roll input, then roll, else stop rolling”. However, you don’t want to stop rolling if no input is pressed.

You only want that part:

if is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_left"):
    roll()
elif is_on_floor() and Input.is_action_just_pressed("roll") and Input.is_action_pressed("move_right"):
    roll()

since the roll end will be triggered after the one second delay.


For readability and code factoring purpose, you can improve your code by nesting two conditions (that’s just an advice on the fly, that’s not mandatory):

if is_on_floor() and Input.is_action_just_pressed("roll"):
    if Input.is_action_pressed("move_left") or Input.is_action_pressed("move_right"):
        roll()
1 Like

Thank you so much it works! :grin:

1 Like

You’re welcome, I’m glad it does :slight_smile:

1 Like

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