Coding the player¶
In this lesson, we'll add player movement, animation, and set it up to detect collisions.
To do so, we need to add some functionality that we can't get from a built-in
node, so we'll add a script. Click the
Player node and click the "Attach
In the script settings window, you can leave the default settings alone. Just click "Create":
If you're creating a C# script or other languages, select the language from the language drop down menu before hitting create.
If this is your first time encountering GDScript, please read Scripting languages before continuing.
Start by declaring the member variables this object will need:
export keyword on the first variable
speed allows us to set
its value in the Inspector. This can be handy for values that you want to be
able to adjust just like a node's built-in properties. Click on the
node and you'll see the property now appears in the "Script Variables" section
of the Inspector. Remember, if you change the value here, it will override the
value written in the script.
If you're using C#, you need to (re)build the project assemblies whenever you want to see new export variables or signals. This build can be manually triggered by clicking the word "Mono" at the bottom of the editor window to reveal the Mono Panel, then clicking the "Build Project" button.
_ready() function is called when a node enters the scene tree, which is
a good time to find the size of the game window:
Now we can use the
_process() function to define what the player will do.
_process() is called every frame, so we'll use it to update elements of our
game, which we expect will change often. For the player, we need to do the
Check for input.
Move in the given direction.
Play the appropriate animation.
First, we need to check for input - is the player pressing a key? For this game, we have 4 direction inputs to check. Input actions are defined in the Project Settings under "Input Map". Here, you can define custom events and assign different keys, mouse events, or other inputs to them. For this game, we will just use the default events called "ui_right" etc that are assigned to the arrow keys on the keyboard.
You can detect whether a key is pressed using
true if it's pressed or
false if it isn't.
We start by setting the
(0, 0) - by default, the player
should not be moving. Then we check each input and add/subtract from the
velocity to obtain a total direction. For example, if you hold
down at the same time, the resulting
velocity vector will be
In this case, since we're adding a horizontal and a vertical movement, the
player would move faster diagonally than if it just moved horizontally.
We can prevent that if we normalize the velocity, which means we set its
1, then multiply by the desired speed. This means no more fast
If you've never used vector math before, or need a refresher, you can see an explanation of vector usage in Godot at Vector math. It's good to know but won't be necessary for the rest of this tutorial.
We also check whether the player is moving so we can call
stop() on the AnimatedSprite.
$is shorthand for
get_node(). So in the code above,
$AnimatedSprite.play()is the same as
$ returns the node at the relative path from the
current node, or returns
null if the node is not found. Since
AnimatedSprite is a child of the current node, we can use
Now that we have a movement direction, we can update the player's position. We
can also use
clamp() to prevent it from leaving the screen. Clamping a
value means restricting it to a given range. Add the following to the bottom of
_process function (make sure it's not indented under the else):
The delta parameter in the _process() function refers to the frame length - the amount of time that the previous frame took to complete. Using this value ensures that your movement will remain consistent even if the frame rate changes.
Click "Play Scene" (F6, Cmd + R on macOS) and confirm you can move the player around the screen in all directions.
If you get an error in the "Debugger" panel that says
Attempt to call function 'play' in base 'null instance' on a null
this likely means you spelled the name of the AnimatedSprite node
wrong. Node names are case-sensitive and
$NodeName must match
the name you see in the scene tree.
Now that the player can move, we need to change which animation the
AnimatedSprite is playing based on its direction. We have the "walk" animation,
which shows the player walking to the right. This animation should be flipped
horizontally using the
flip_h property for left movement. We also have the
"up" animation, which should be flipped vertically with
flip_v for downward
movement. Let's place this code at the end of the
The boolean assignments in the code above are a common shorthand for programmers. Since we're doing a comparison test (boolean) and also assigning a boolean value, we can do both at the same time. Consider this code versus the one-line boolean assignment above:
Play the scene again and check that the animations are correct in each of the directions.
A common mistake here is to type the names of the animations wrong. The
animation names in the SpriteFrames panel must match what you type in
the code. If you named the animation
"Walk", you must also use a
capital "W" in the code.
When you're sure the movement is working correctly, add this line to
_ready(), so the player will be hidden when the game starts:
Preparing for collisions¶
Player to detect when it's hit by an enemy, but we haven't made any
enemies yet! That's OK, because we're going to use Godot's signal
functionality to make it work.
Add the following at the top of the script, after
This defines a custom signal called "hit" that we will have our player emit
(send out) when it collides with an enemy. We will use
Area2D to detect the
collision. Select the
Player node and click the "Node" tab next to the
Inspector tab to see the list of signals the player can emit:
Notice our custom "hit" signal is there as well! Since our enemies are going to
RigidBody2D nodes, we want the
body_entered(body: Node) signal. This
signal will be emitted when a body contacts the player. Click "Connect.." and
the "Connect a Signal" window appears. We don't need to change any of these
settings so click "Connect" again. Godot will automatically create a function in
your player's script.
Note the green icon indicating that a signal is connected to this function. Add this code to the function:
Each time an enemy hits the player, the signal is going to be emitted. We need
to disable the player's collision so that we don't trigger the
more than once.
Disabling the area's collision shape can cause an error if it happens
in the middle of the engine's collision processing. Using
set_deferred() tells Godot to wait to disable the shape until it's
safe to do so.
The last piece is to add a function we can call to reset the player when starting a new game.
With the player working, we'll work on the enemy in the next lesson.