Introduction to GDScript in Godot 4 Part 1

Get started learning GDScript in this two-part tutorial. You’ll learn about the built-in script editor, using variables and player input. By Eric Van de Kerckhove.

4 (2) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Scripting Basics

Let’s put the theory and background information aside for now and focus on what you came here to do: write scripts!

Creating New Scripts

To start off, create a new folder at the root of the project’s filesystem by right-clicking res:// and selecting New ▸ Folder.

New and Folder are highlighted

This folder will hold all the scripts, so name it scripts. The first script you’re going to create will be for the player avatar, so open the player_avatar scene. Now select the root PlayerAvatar node and press the attach script button that looks like a scroll with a green plus sign.

White scroll with green plus icon is highlighted

This opens the Attach Node Script window, which you can use to set the initial settings for the script.

Window with several fields, path is highlighted

Here’s a rundown of these settings:

  • Language: Godot supports other languages besides GDScript, which you can choose here. The default build of Godot only comes with GDScript, while the .NET build also has C# included.
  • Inherits: The Node class to inherit the script from. This gives your script access to variables and functions unique to that node type.
  • Class Name: The class name of the script. This only applies to different languages than GDScript. For C# this would be PlayerAvatar for example.
  • Template: What script template to apply. This changes how the new script will be structured, with or without comments for example. Godot comes with a Default and an Empty template out of the box. You can also create your own script templates.
  • Built-in Script: By default, Godot saves scripts as files. When this is checked, the script is built into the scene itself. This can be useful in rare cases, but having external scripts is more intuitive and better for source control.
  • Path: Project path where Godot will create the new script.

Notice that the path points to the scenes folder by default. Make sure to replace “scenes” with “scripts” in the path, either by hand or by clicking the folder icon and navigating to the scripts folder. The default filename “player_avatar.gd” is fine as Godot uses snake_case for its files and folders. With the correct path set, click the Create button to create the script.

Path and create button

This will automatically open the script editor with the new script opened. Press CTRL/CMD + S to save the scene and the script and then take a look around the different parts of the editor.

Script editor overview

Below is a summary of the different sections:

  1. The toolbar located at the top of the screen contains a menu on the left with typical commands found in text editors plus some useful scripting options like changing the syntax highlighting. On the right side are two buttons; Online Docs which opens Godot’s online documentation in your default web browser, and Search Help which opens a search window that allows you to search and find info about the built-in classes.
  2. Opened scripts are displayed at the top, while the bottom list shows the functions of the script you’re editing.
  3. The text editor displays the content of the script, and is the main workspace you will use throughout this tutorial. At the top right is a minimap, which makes it easy to quickly navigate larger scripts.
  4. The panel at the bottom has an arrow button to hide the two lists at the left, and shows any warnings and errors at the right, along with the line number and column the caret is placed on.

Now you’re a bit more familiar with the interface, it’s time to look at the script itself.

Script Anatomy

Godot’s default script template makes for a great starting point to build upon further. It even contains some useful comments! I’ll bridge the gap to make it more obvious what the unexplained parts do.
The first line reads extends Node2D, which means this script inherits from the Node2D class. The extends keyword allows your script to use the variables and functions from the inherited class.

Below that are two of the most used functions in Godot: _ready and _process. As the comments suggest, _ready is called once at the start of the node’s lifespan, while _process is called every frame. These are overrides for the virtual functions of the same name in the Node class, the class all other node-based classes inherit from. You can see which functions override their base class by little blue arrow in front of the line number.

Blue arrows pointing at code

Both functions start with an underscore to mark them as private, meaning you shouldn’t call these functions from another script. Godot doesn’t enforce this in any way though, it’s a convention to keep your code clean.
The pass keyword in both functions is a placeholder, it doesn’t do anything except for making sure there’s something indented inside the function so Godot doesn’t throw errors. Try removing the whole pass line in the _ready function to see for yourself.

Function with pass keyword removed

This will show an error at the bottom saying: “Expected indented block after function declaration”. GDScript uses indentation with tabs (or spaces) to specify what code belongs to what function or statement. Now change the _ready function as follows:

func _ready() -> void:
    print("Player avatar has loaded!")

The print function prints arguments of any type to the output console. Save the script and run the game by pressing F5 to see it in action.
If everything went well, you should now see the text being displayed at the bottom. If you don’t see the console, click the Output button to toggle it on.

Console

With the fundamentals covered, you’re now ready to tap into the power of GDScript!

Variables

The player avatar needs to move left and right towards the position of the cursor. To do this, you’ll need keep track of the avatar’s current velocity, its movement speed and the mouse position. You can store these values as variables in your script.
You can define variables in Godot using the var keyword. To start with, define a new variable named velocity that can be accessed throughout the script by adding this line below extends Node2D:

var velocity : Vector2 = Vector2.ZERO

There are a few things happening here:

  • var velocity defines a new variable and names it velocity. This by itself is enough to create a new variable if you don’t care about the type or default value.
  • : Vector2 sets the type of variable, in this case a Vector2, which has a x and y coordinate to hold the horizontal and vertical velocity respectively.
  • = Vector2.ZERO gives the variable a default value. Vector.ZERO is a constant with a value of 0 for both x and y.

Providing a type for variables is optional in Godot, this called dynamic typing. Setting the type while defining new variables like you just did is called static typing. You should prefer static typing over dynamic typing for several reasons:

  • It makes Godot’s compiler understand what you’re tying to do better, which results in better autocompletion in the script editor.
  • The code will be more readable as it’s clear what value a variable can hold. This in turn makes your code less error-prone.
  • You get a huge performance boost for free, as statically typed code runs between 25 and 100 percent faster than its dynamic counterpart.

All variables throughout this tutorial will use static typing to make it easier to follow along and learn the different types.

Next on the list is the movement speed, which is the speed at which the avatar will move horizontally in pixels per second. Unlike the velocity, you should be able to tweak its value via the inspector. Add the following line below extends Node2D to add a new move_speed variable:

@export var move_speed : float = 600.0

By adding the @export annotation before the variable declaration, it exposes the variable to the editor. This is extremely useful for quickly changing values to improve the balance or feel of your game. The move_speed variable is a float, which is short for floating-point number. It can store numerical values with decimal digits and is often used for positional calculations.

Save the script player_avatar script, make sure you’ve got the PlayerAvatar node selected in the Scene dock and take a look at the Inspector on the right. You should now see a new property named Move Speed with the a value of 600.

Move speed 600

I found 600 pixels per second to be a sensible speed, but you can change this value once the avatar can move to make the game more easy or difficult. Next on the list is getting the player to move the mouse cursor.
For this to work, you’ll need to get the position of the cursor, check whether it’s left or right from the avatar and then adjust the velocity accordingly every frame. To get the position of the mouse cursor, replace the pass keyword in the _process function with this:

var mouse_pos : Vector2 = get_global_mouse_position()

This grabs the position of the cursor via the built-in get_global_mouse_position() function, which returns a Vector2. To test if this is working, you can add the following print call below the line you just added:

print(mouse_pos)

This will print the cursor position every frame. Go ahead and give it a go by pressing F5 on your keyboard to run the scene. Move your mouse around a bit and you’ll see the positions appearing in the console.

647, 275 in console

Now you know that’s working, remove the print line you added for testing purposes and replace it with the following to make the avatar move to the cursor:

Note: If you get an error saying “Mixed use of tabs and spaces for indentation” while copying any of the code snippets to your scripts, press CTRL/CMD + I on your keyboard while in the script editor. This will automatically fix any incorrect indentation. The reason you might get this error when copying snippets from a web browser is because they tend to convert tabs to spaces, while Godot expects tabs.
    var x_distance_to_cursor = mouse_pos.x - global_position.x # 1
    var cursor_right : bool = x_distance_to_cursor > 0 # 2

    if cursor_right: # 3
        velocity.x = move_speed
    else:
        velocity.x = -move_speed

    global_position += velocity * delta # 4
  1. Calculate the distance to the cursor by subtracting the avatar’s X position from the cursor’s X position. For example, if the avatar’s position is (X:10, Y:0) and the cursor’s position is (X:50, Y:0), the distance would be 40 pixels. Store this value in a new variable named x_distance_to_cursor.
  2. Check if the cursor is right or left from the avatar by getting the distance calculated above and seeing if it’s a positive or negative value. A positive value means the cursor is to the right. Store this in a boolean variable named cursor_right. A boolean has two possible values: true or false.
  3. This is an if-statement that changes the X-value of the velocity to the movement speed if the cursor is to the right, or to the negative movement speed if the cursor is to the left. If-statements in GDScript follow the same rules as functions, so anything that applies to the statement should be indented with a tab and the statements end with a colon.
  4. The global_position variable is part of the Node2D class and stores the node’s position as a Vector2. The code applies the velocity to the avatar’s global position and multiplies it with delta to make it framerate-independent.

That was a lot to take in at once! Your efforts are rewarded though, as you’ll see when running the game by pressing F5.

GIF of robot moving left and right

The avatar now smoothly follows your cursor! You might have noticed a little quirk though: when you move the cursor outside of the borders of the window, the avatar follows and disappears off-screen. To fix this, you can get the rectangle that makes up the viewport, add a border to the sides and limit the avatar’s horizontal movement inside.

Window with red borders left and right

The illustration above shows the concept of a virtual border that the avatar can’t pass with a red color. To implement this in code, you need the size of the viewport, the border width and the avatar’s position. To get the viewport’s size, add this line to the _process function, below mouse_pos:

var viewport_size : Vector2 = get_viewport_rect().size

This line gets the viewport’s rectangle, which is the position and size of the game window, and extracts just the size from it. This value then gets stored in viewport_size.
For the border, you’re going to add another exported variable, so you can tweak its value in the Inspector. To do that, add the following to the top of the script, right below the move_speed variable declaration:

@export var viewport_border : float = 80.0

This is the width of the border in pixels. To actually limit the avatar’s movement, adjust the velocity-changing if-statement in _process as follows:

    velocity.x = 0 # 1

    if cursor_right:
        if global_position.x < viewport_size.x - viewport_border: # 2
            velocity.x = move_speed
    else:
        if global_position.x > viewport_border: # 3
            velocity.x = -move_speed

Here’s what this does:

  1. Reset the horizontal velocity so the avatar doesn’t move if there’s no horizontal velocity applied further down.
  2. If the avatar wants to move to the right, only allow it if its position hasn’t passed the border on the right. The border calculation takes the horizontal size of the window and subtracts the size of the border from it.
  3. This does the same as above, but for moving to the left. Instead of taking the size of the viewport into account, you simply use the border size as the leftmost viewport position is 0.

With this added, run the game again and move your cursor outside of the window. The avatar will now wait patiently at one of the borders.

GIF of robot moving left and right

With the horizontal movement done, you’re now ready to explore Godot’s signal system.