Tuesday, 9 May 2017

Gitting Gud At Godot - Part 3 - Scripts

Watch the video version of this tutorial here: https://www.youtube.com/watch?v=CouU0HDWeFo

In this tutorial, things are about to get interesting. We're going to use GDScript to add some functionality to the nodes from Part 2. Also, I'm going to assume that you have some knowledge of how a programming language works, though I'm still going to explain how to use GDScript.

First of all, here's a helpful tip: you can rename your nodes by double clicking on them in the scene tab. Let's name the Sprite node "player".

Doing this will help to keep things organized when we're working with a lot of nodes in the same scene. Trust me when I say that this is very important.

Next, we can add a script to our "player" node. Select the "player" node and scroll to the bottom of the Inspector tab. To the right of the "Script" property, click on the <null> dropdown menu and select "New Script".

This will open a dialogue box, but the only thing you need to edit is the "Path" field. Select a good place to save your new script and call it something like "player.gd".

You might also notice the "Built-In Script" button below. For larger or more complex scripts, do not use this. It makes it very difficult to access and your project will become fairly cluttered as a result. This button saves the script as a built-in part of the scene. This means that there is no ".gd" file, but all the code is embedded into the ".tscn" file. This is fine for smaller scripts which you are unlikely to ever need to access again, but be warned.

When you have created a script, the scene view will automatically open the script editor. It has a few automatically generated lines of code, namely an "extends" command and a "_ready()" function. Also, your "player" node now has a scroll next to it, indicating that a script is bound to it. You can click on this icon in order to open the script associated in the script editor.

For the sake of space, I'm going to be removing the comments that are automatically placed into the file.

So let's get to work. To start, let's make it so that the player sprite constantly moves right.

Currently, the script file has a "_ready()" function. This is called once as soon as the script is added to the scene. However, in order to be constantly moving the player we need a function that is called repeatedly. Fortunately, Godot has just the function for us- "_process(delta)".

"_process(delta)" will be called as fast as possible repeatedly, and passes the "delta" argument. "delta" is a float containing the number of seconds since the last call.

So in order to use this, we're going to put a single command in _ready() which will tell Godot to start calling _process(delta). This command is "set_process(true)". Make sure it's indented one level, as Godot handles code blocks in the same manner as Python.

After that, we can define the _process(delta) function. This is done by typing:

func _process(delta):

This will print out "delta" to the output log when we run the game, as shown below.

The output log is where all print() statements output to. If you've exported your game, print() statements will print to the system's standard output - i.e a terminal.

However, we want to make the player move to the right.

Since this script extends Sprite, we get all the commands and goodies that come with the Sprite class(and the Node2D class, and the CanvasItem class, etc). One such goody is the "set_pos(..)" function. As expected, this sets the position of the object to a certain (X, Y) coordinate. The set_pos(..) function takes in a Vector2 argument.

A Vector2 isn't a new thing, but for those who aren't aware; a Vector2 is a data type which takes two float values. Similarly, a Vector3 is a data type(used in 3D graphics so don't worry) which takes three float values.

Let's set the position of the Sprite to a known location. In _process(delta) I'm doing to put:

func _process(delta):
    set_pos(Vector2(500, 500))

Each time _process(delta) is called, this will set the Sprite to the same location. We can run the program and see that now the Godot mascot is stuck near the bottom of the window.

Not particularly exciting, is it? We see no visible movement, since he's constantly being placed at the same spot. However, he has moved from the position in the top-left that we defined in the editor.

In order to make him move to the right, we could get his position, add some number to his X coordinate and then set his position to that. If only there was a certain function to get the pos()...

Oh wait, there is! It's called get_pos(). get_pos() returns a Vector2 containing the (X, Y) coordinates.

Let's define a new variable and call it current_position, and set it equal to get_pos().

func _process(delta):
    var current_position = get_pos()
    set_pos(Vector2(500, 500))

Since we can access a Vector2's elements by using Vector2.x and Vector2.y, we can access current_position's elements by using current_position.x and current_position.y. This means that we can do the following:

func _process(delta):
    var current_position = get_pos()
    current_position.x += 1

This adds 1 to current_position's X coordinate, then sets the object's position to the new position. If we run our project now, we can see that the mascot will slowly move across the screen.

Hooray! We've done it!

There is one problem with using set_pos() for movement, and that's because it doesn't really gel with Godot's built-in collision detection. Since it's effectively a snap teleport being performed a hundred times a second, it doesn't check for walls. However, that's something we'll deal with later on. For now, be proud.

You can check out the documentation for more commands and things to do with the Sprite here: http://docs.godotengine.org/en/stable/classes/class_sprite.html

Be sure to also check out the available functions for Node2D, CanvasItem and Node in order to see the full set of functions that are available to the Sprite class. Play around! Most people won't learn best by just crunching through material, you need to practice and take initiative to get the most out of any tutorial.

Thanks for reading, and stay tuned for part 4 on the topic of input!

Part 2: http://alexhoratiogamedev.blogspot.com/2017/05/gitting-gud-at-godot-part-2-nodes.html


  1. Looking forward to the next tutorial

  2. These are great Alex. Core concepts explained simply and concisely. Thanks :)