Let’s Learn Godot 4 by Making an RPG — Part 2: Player Setup & Movement🤠

Christine Coomans
12 min readJun 21, 2023

--

Now that we have our project setup, we can now go on to creating our player scene. Our player scene will contain nodes and scripts that will allow us to see a character and control them so that we can run around and interact with the world.

WHAT YOU WILL LEARN IN THIS PART:

How to create, run, and instance new scenes.

How to add input actions, collisions, and animations to a node.

How to add scripts and code to a scene.

How to add movement to a node.

PLAYER SETUP

In our Main scene, we can go ahead and delete that Sprite2D node that we added earlier. We will create a whole new scene for our Player, and then instantiate it in our Main scene. Instancing allows us to replicate an object from a template so that we can modify them separately. In other words, we will have our Player separate from our Main scene, so we can treat it as a separate object. You’ll see how this works more clearly in a minute.

In your workspace, click the plus icon next to Main(*) to create a new scene. In the Scene Dock, add a new root node. We need this root node to be a CharacterBody2D node, which is a specialized 2D physics body node for characters that can be moved via scripts. We use this node for entities that need physics processing for movement.

Double-click the CharacterBody2D node to rename it to “Player”. We can also go ahead and save our new scene as “Player” in our Scenes folder.

You will notice that there is a warning icon next to your root node. If you hover over it, you will see the warning message. It tells us that our character has no shape, so it cannot collide or interact with others. To fix this we need to add a CollisionShape2D node. This node will allow our player to collide with other collision bodies which will block, damage, or trigger events in our player.

This node will also return a warning message because we haven’t assigned it a shape in the Inspector panel yet.

Let’s fix this by clicking on the <empty> box next to Shape and selecting a shape. The type of shape that you choose for your object will depend on what you want them to accomplish (from where they need to collide), as well as their body shape. So, a circular character might have a circle collision, and so on. For our sprite, let’s choose the CapsuleShape2D shape. You will now see that we have a collision shape, so we can collide with other objects in our game, but we don’t even have a body yet!

To see our character, we need to assign it to a Sprite. Now, we can go about this two ways, via the Sprite2D node, and the AnimatedSprite2D node. The Sprite2D node is perfect if we want to have a static object, i.e., an object that doesn’t move or that has no animations. The AnimatedSprite2D object, however, is the exact opposite. It is perfect for when we have an object that requires sprite sheets to be animated.

With your Player’s root node selected (make sure you select it and not your CollisionShape2D node because otherwise, it will add our node to it as its child), add a new AnimatedSprite2D node.

If you ever choose the wrong node, right-click the node you want to replace and say change type.

This node will once again return a warning, and that is because it has no sprite sheet assigned to it, so it cannot return animation frames. Let’s get rid of this warning by going into our Inspector panel, and underneath <empty> next to Sprite Frames, select New SpriteFrames.

This SpriteFrames resource allows us to import image files (or a folder containing said files) to provide the animation frames for the sprite. To configure our SpriteFrames resource, click on it, and you will see a new option pop up in our panel below.

It is in this panel where we can add new animations, delete animations, play animations, or edit animations. The default animation can be deleted or renamed. We will add all of our player’s animations in the next part so that we can explore this node in more detail. For now, let’s just assign a sprite to our default animation by clicking the “Add Frames from Sprite Sheet” option.

A window will pop up so that we can select the Sprite Sheet we want to add, so head into Assets > Player, and select the Front Sheet.

Another window will pop up, and it is here where we need to crop our individual sprites to make an animation. The default horizontal and vertical values are 4. Horizontally, we can count that our player frames go up to 14, so change the horizontal value to 14.

Vertically, we can count that our player frames go up to 5, so change the vertical value to 5.

Now we can select the frames we want (in order) to create one animation. This sheet has five possible animations: idle, walk, attack, damage, and death. For now, let’s just select the second row to create a basic walk animation. Make sure you select it in order, otherwise, you will have to rearrange it in the SpriteFrames panel.

Our default animation now has 8 frames added to it. If you press play, we can see the animation play in our workspace. Our player walks a bit slow, so let’s up their FPS value from 5 to 10. Leave the looping value to be enabled.

MOVEMENT INPUTS

With our walk animation added, we can now add the inputs and functionality to move them around. Before we can jump the gun and start coding, we need to add an input action. This is the physical controls, such as our keyboard keys, joysticks, buttons, etc. that we will map to our functions to move our character around the game.

To add input actions, we go to Project Settings > Input Map.

Godot 4 comes with pre-existing input actions. To see them, enable the “Show Built-in Actions” toggle. The ui_ stands for “user input”, and you don’t have to stick to this naming convention for your future input actions, but for this project we will just to keep our code consistent.

You’ll see Godot has already pre-configured the inputs to move a character left, right, up, and down. They assigned the keyboard keys UP, DOWN, LEFT, and DOWN to these actions. You can edit them by pressing on the pencil icon next to them, or even delete them. You can also assign other keys to this input by clicking on the plus icon next to it.

Let’s assign our WASD keys to ui_up (W key), ui_down (S key), ui_left (A key), and ui_right (D key).

We will come back to add our own input actions for attacking, consuming, interacting, and sprinting later on. For now, there is nothing that we need to do here since most actions have already been added for us, so you can close this menu entirely.

Let’s now add a script to the player so that we can move them around with these inputs. You can assign a script to any node but remember that — that script will only impact that node and only that node’s class objects will be callable. Except, however, root nodes. If you add a script to a root node, it will be able to impact and call the classes of all its child nodes.

To assign a script to a node, select the node you want to add functionality to (which in this case is our root node), and in your Scene dock, click the little scroll icon next to it. Save this script as Player.gd underneath your Scripts folder.

Once a node has a script attached to it, you will see the scroll image appear next to it. If you right-click on your node, you can detach this script and attach a new one. Click on the scroll to open your script, which will open the Script workspace.

At the top, it says “extends CharacterBody2D”, because it is extending from the base class CharacterBody2D. That means all the properties and sub-functions of CharacterBody2D will be callable from this script.

Now to make our character move, we need to first define a movement speed variable.

### Player.gd
extends CharacterBody2D
# Player movement speed
@export var speed = 50

The @export means that we can export the variable to be editable in the Inspector panel. So, with your node selected, you can now change the speed value in the properties without having to assign it a constant value. This comes in helpful for when we create things like enemies and want to instance them multiple times in a scene and want them all to have different movement speeds.

Next up, we will use our _physics_process(delta) function, which processes the character’s movement physics constantly. In this function, we need to get the player input direction (left, right, up, down). This input needs to be normalized for smoother diagonal movement because our player moves on a cartesian plane level. This means they need to move 1 space up, down, left, or right, and normalization will set the vector to the same length in any direction.

When to use _processing() and _physics_processing()?

Use the _process() function for things that are graphical or need to respond quickly. Use the _physics_process() function for things that are physics-based or need to happen at a consistent, predictable rate.

What is a Vector?

Vectors are objects that represent quantities like force, velocity, and position. In 2D games, we use Vectors to determine and calculate the position of entities on the X and Y axes.

Figure 5: What vector movements look like on a plane scale.

We will then use the built-in move_and_collide() method from Godot to move and to enable the physics to move our player around, whilst also enforcing collisions so that they come to a stop when bouncing into other objects.

When to use move_and_slide and move_and_collide()?

Use move_and_slide() when you want the character to move in general directions, such as in platformer games. Use move_and_collide() when you want detailed information on your character’s collisions to perform custom actions, such as in puzzle games where characters need to move or dodge obstacles.

### Player.gd

extends CharacterBody2D

# Player movement speed
@export var speed = 50

func _physics_process(delta):
# Get player input (left, right, up/down)
var direction: Vector2
direction.x = Input.get_action_strength("ui_right") -Input.get_action_strength("ui_left")

direction.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")

# If input is digital, normalize it for diagonal movement
if abs(direction.x) == 1 and abs(direction.y) == 1:
direction = direction.normalized()

# Apply movement
var movement = speed * direction * delta
# Moves our player around, whilst enforcing collisions so that they come to a stop when colliding with another object.
move_and_collide(movement)

Save your script and let’s go back to our Main scene. Click the chain icon next to the plus icon, which will help us instance (add) our Player scene to our Mode scene. Select the Player scene.

Press F to focus on the newly instanced Player scene in our Main scene, and you will see that it has been added, but we cannot see its children (the collision and sprite nodes). That’s because it is treated as a complete object now, and not individual nodes.

Now zoom out until you see a blue border in your Main scene. This blue window is the border of your visible game screen or viewport, which in simple terms, is the screen size you will see when you run the game.

Our players will spawn in the top left corner, so we won’t be able to see them properly. To prevent this, let’s select them and drag them to a space closer to the middle or inner edges. Ensure your select tool is enabled for this, which is the cursor.

If we were to run our game now, the player will be very small. That is because our game window is zoomed out pretty far from where our player is. This is because our viewport size is too big, and our stretch mode needs to change.

Go into Project Settings > Display > Window, and make the following changes:

Our character will be zoomed in now, and we can run our game by clicking on the play button or by pressing F5.

The player is moving around the map when we enter WASD or our direction keys but without any animations! That’s because we haven’t yet assigned any animations for our inputs, but we will do that in the next part when we focus on animations as an isolated topic.

With our player character set up, we can move on to animations. You might be wondering about the world map, as well as the camera setup for our character, but don’t worry, we’ll get to everything in due time. Remember to save your game project and to make a backup, and I’ll see you in the next part.

Your final code for this section should look like this.

TUTORIAL RELEASE

LINKS TO OTHER PARTS

The tutorial series has 23 chapters. I’ll be releasing all of the chapters in sectional daily parts over the next couple of weeks.

You can find the updated list of the tutorial links for all 23 parts in this series here.

FULL TUTORIAL

If you like this series or want to skip the wait and access the offline, full version of the tutorial series, you can support me by buying the offline booklet for just $4 on Ko-fi!😊

--

--

Christine Coomans
Christine Coomans

Written by Christine Coomans

Just a *redacted* trying to human. 👾Visit my website for more cool resources: https://oops-i-devd.gitbook.io/christinec-dev

Responses (1)