DEV Community

Cover image for Make your player roll and animations!
Eduardo Julião
Eduardo Julião

Posted on • Updated on

Make your player roll and animations!

In today's article, I'm going to show you how to make your player roll, manage states and basics of animation.

Creating a new action in the Input Map

Before we start writing our code, let's create a new action into our Input Map named roll. I've attached the letter K on the keyboard, but you can bind any key you prefer.

Image description

Adding a new state

Open the Player.gd script and lets update our states enum, adding the action ROLL.

enum states {
   MOVE,
   ROLL
}
Enter fullscreen mode Exit fullscreen mode

Using _input(event) to change states

In the previous article I introduced the _input(event) method, now, we're going to use it to change the player state based on the player input!

## Called when player input is detected.
## @param event: InputEvent
## @return void
func _input(event):
    if event is InputEventMouseButton or event is InputEventMouseMotion:
        return;

    # Only process input if the player is in the MOVE state.
    if _current_state == states.MOVE:
        # If the player presses the roll button, then the player should roll.
        if event.is_action_pressed("roll"):
            # sets the _current_state to states.ROLL
            _current_state = states.ROLL;
Enter fullscreen mode Exit fullscreen mode

First, we're going to check if the player is in the MOVE state, this will prevent the player from rolling mid attack, or attack mid rolling.

Then, we check if the player pressed what is mapped to the roll, if so, we change the current state to ROLL.

Updating player movement

Now that we changed our state to ROLL, we need to tell the script what to do when the player is in this state.

Let's start by creating a roll function, like we created with the move(delta). And as we did in the move(delta) function, we'll create a new variable, the _roll_speed.

@export var _roll_speed: float = 130;
Enter fullscreen mode Exit fullscreen mode

Now, we update the _physics_process(delta) function to add the call to the roll function.

# delta = time between frames
func _physics_process(delta):
    match _current_state:
        states.MOVE:
            move(delta);
        states.ROLL:
            roll();
Enter fullscreen mode Exit fullscreen mode

The editor will throw you an error since we don't have a roll() function created, so let's scroll all the way down on our script and create a new function.

func roll():
    velocity = _roll_speed;
    move_and_slide();
Enter fullscreen mode Exit fullscreen mode

But wait, we can't simply say the velocity is the _roll_speed, we need a vector, just as we used the input_vector in our move(delta) function.

So let's create a new variable that will receive the input vector and we can use in our roll function.

var _roll_vector: Vector2 = Vector2.RIGHT;
Enter fullscreen mode Exit fullscreen mode

This will create a _roll_vector variable we can use and will initialise its value to right.

Now, our roll() function will look like this:

func roll():
    velocity = _roll_vector * _roll_speed;
    move_and_slide();
Enter fullscreen mode Exit fullscreen mode

And we can't forget to update our move(delta) function to set our new _roll_vector. Let's add the following code before we update our velocity.

# This will be used in the roll() function.
# This will be used to determine which direction the player should roll.
_roll_vector = input_vector;
Enter fullscreen mode Exit fullscreen mode

And why we update only when the player moves? Because this will keep the player "facing diretion", so if the player decides to walk to the left, we store this "last facing direction" to our _roll_vector, if the player stops, this variable will keep this state, when the player rolls, it will roll to the left.

Run the player

We're ready to run our game! But notice one thing, if we roll, the player keeps going, and going and we can't stop it! But why this happens?

In the _input(event) function we only accept input when the player state is MOVE, but we don't have anything telling the script to revert to the MOVE state.

To solve this issue, we're going to use animations!

Creating the animation node.

Let's get back to our player node in Godot and add two new nodes AnimationPlayer and AnimationTree.

Image description

Notice the AnimationTree have a warning sign next to it, this is because it needs to be told how it should handle animations. Select the AnimationTree node and on the right side, in the inspector, and update the Tree Root to use AnimationNodeStateMachine

The next step, is to assign the Anim Player to the animation player we created.

Image description

This will open an AnimationTree window on the bottom of Godot, we're going to create a couple of animations (even though they're just the godot icon 😅), and update the animations through our script!

Creating new animations

First, select the Animation tab in the bottom panel, then, in new panel, click on Animation at the top and then select the option New.

Image description

Let's create animations for idle, walk and roll. Each of them having the facing direction, for example, idle_left, idle_right, so on.

You should have something like this

Image description

Understaing The animation window

Let's start by updating the roll_right animation, select it on the drop down and the first thing we're going to do, is to add our sprite to it!
To do this, keep the animation window open, select the Sprite2D node in the scene and under Animation in the inspector, select frame 0 and press the key icon to the right of it.

Image description

Image description

This will open a new window, just press create.

Image description

This will add our sprite to the tracks!

Image description

Cool! we have our first track with our sprite! But before continuing, let's update some options in the window, to make it easier for us to work.

In bottom right of the animation window, change from seconds to FPS.

Image description

Here is where we tell how many frames the animation will have, for the roll animation, lets pretend we have 4 frames, so let's update the value on the left to 4!

Image description

If you press the play button in the animation window, you'll notice that the track will stop on frame 4 (it counts 0,1,2,3), signaling the engine that the animation ended.

Adding a Call Method Track

The Call Method Track lets you call a methoid in a script at anypoint of the animation you want! Which is great! because we're going to use this to change the state of our player.

CLick on the + Add Track and select Call Method Track.

Image description

This will open a new window for you to select a node to animate, in this window, select the player. We're selecting the player node becuase we're adding the script there.

Image description

Once you're done, this will create a new track in the Animation window.

Image description

Let's get back to our Player.gd and create a simple function that will set the state back to move, let's call it on_roll_ended().

func on_roll_ended():
    _current_state = states.MOVE;
Enter fullscreen mode Exit fullscreen mode

Now, to add your script, make sure the blue vertical bar is at the last frame of your animation (in this case, the end of the third frame beginning of the forth), right click it and press Add Key.

In the new window, select the method we just created:

Image description

This will tell the Engine that when the roll_right animation ends, it should call the method we assigned.

Image description

Before we continue, do this for all roll animations 😄.

The animation Tree

We're almost done, we need to set up the animations in the animation tree so we can use it on our scripts.

Select both animation tree under the player node and in the bottom panel.

You'll see a Start/End blocks, we're going to create a few more, one for idle, one for walk and one for roll. Right click anywhere between Start and End and select the Add Blend Space 2D option, once it's created, double click the text and rename it to Idle, do the same for walk and roll.

Now, let's link these aniumations together!
There are a couple of tool in to top left corner of the Animation tree

Image description

From left to right:

  • Select Tool With this selected, you can click on any node in the animation tree.
  • Add Tool When this is active, clicking anywhere in the animation tree with the left mouse button will open the add pop-up.
  • Connect Tool When Connect Tool is selected, you can link animations together.
  • Delete Tool Deletes the selected node.

Select the Connect Tool and let's start linkin them. To link animations, click on the node you want to link and drag to the next node.

You should have something like this.

Image description

Before we proceed, schange to the select tool and select the lines between nodes, now in the inspector, navigate to Advance and change the Mode property to Enabled.

Image description

Do this to all connections!
One last step!! (I swear, it's almost at the end) press the pencil icon in the Roll node, this will open a window with the details to the Roll (or any animation you pressed) animation.
Let's add our animations we created in the Animation window here, and we're ready to use them!

First, change the blend mode to the tree dots here:

Image description

Now, select the select tool in the top left corner.

Image description

Right click as close as you can to the 0 in the left of the graph > add animation > roll_left. Now, when we set the blend position in our script, it'll update the animation based on the X and Y positions. Do this for the remaining animations!
At the end, you will have something like this:

Image description

!! IMPORTANT !!

in Godot, up = -1 and down = 1, that's the reason we add the down animation up and up animation down.

Updating animations in the script

This is our last step, calling different animations in our script! Open the player script and let's start working on calling these animations!

Open the Player Script, first things first, lets get our nodes in the script, we can do so like this

@onready var animationTree = $AnimationTree;
@onready var animationState = animationTree.get("parameters/playback");
Enter fullscreen mode Exit fullscreen mode

There are a couple of new keywords here, let's explore them.

  • @onready Tells the engine that this variable will be loaded once the player (or the node) is loaded into the scene tree.
  • $AnimationTree having the $ sign is a syntax sugar that would be the same as calling get_node("AnimationTree").

Enable the AnimationTree

In the _ready() function, add the following code, this will enable the animation tree once the player is ready.

animationTree.active = true;
Enter fullscreen mode Exit fullscreen mode

Setting up the blend position

In this step, is very important to use the exact names in the animation tree, otherwise you'll get some errors in the run time.

The recipe to set the blend position is:

animationTree.set("parameters/<Animation_Name>/blend_position", <movement_vector>);

Let's add the following code in the _physics_process function. First, the idle animation, just beneath the if input_vector == Vector2.ZERO:

animationTree.set("parameters/Idle/blend_position", input_vector);
Enter fullscreen mode Exit fullscreen mode

And for walk and roll animation we can set after the _roll_vector = input_vector;

_roll_vector = input_vector;

    animationTree.set("parameters/Walk/blend_position", input_vector);
    animationTree.set("parameters/Roll/blend_position", input_vector);
Enter fullscreen mode Exit fullscreen mode

Calling animations

In this last step, we need to tell the animation state which animation should be playing.

Let's call our animations, Idle, Walk and Roll.

The code is really simple, just use:

# For Idle
animationState.travel("Idle");

# For Walk
animationState.travel("Walk");

# For Roll 
animationState.travel("Roll");
Enter fullscreen mode Exit fullscreen mode

For the Idle, call it after setting the Idle blend_position. For Walk, call it after setting the Walk blend_position and for Roll, call it in the roll() function.

if input_vector == Vector2.ZERO:
        animationTree.set("parameters/Idle/blend_position", input_vector);
        animationState.travel("Idle");
Enter fullscreen mode Exit fullscreen mode
_roll_vector = input_vector;

    animationTree.set("parameters/Walk/blend_position", input_vector);
    animationTree.set("parameters/Roll/blend_position", input_vector);

    animationState.travel("Walk");
Enter fullscreen mode Exit fullscreen mode
func roll():
    velocity = _roll_vector * _roll_speed;
    animationState.travel("Roll");
    move_and_slide();
Enter fullscreen mode Exit fullscreen mode

CONGRATULATIONS!

Phew, that was a long one, but now you now how powerful the animation can be in Godot!
Now, when you run the game, once you press the Roll action, your player will roll to the last facing position!

Thank you!

I hope you're enjoying this series as much as I'm enjoying writing them!

Thank you for reading it, any problems or questions drop a comment below and I'll get to it!

Thanks again, and I'll see you in the next article!

Top comments (0)