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.
Adding a new state
Open the Player.gd
script and lets update our states
enum, adding the action ROLL
.
enum states {
MOVE,
ROLL
}
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;
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;
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();
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();
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;
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();
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;
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
.
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.
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.
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
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.
This will open a new window, just press create.
This will add our sprite to the tracks!
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.
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!
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.
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.
Once you're done, this will create a new track in the Animation window.
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;
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:
This will tell the Engine that when the roll_right animation ends, it should call the method we assigned.
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
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.
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.
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:
Now, select the select tool in the top left corner.
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:
!! 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");
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 callingget_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;
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);
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);
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");
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");
_roll_vector = input_vector;
animationTree.set("parameters/Walk/blend_position", input_vector);
animationTree.set("parameters/Roll/blend_position", input_vector);
animationState.travel("Walk");
func roll():
velocity = _roll_vector * _roll_speed;
animationState.travel("Roll");
move_and_slide();
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)