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 3 of 4 of this article. Click here to view the first page.

Signals

A signal in Godot is a message emitted by a node when a certain event occurs. Nodes can subscribe these signals to do actions of their own. Some built-in examples if signals include:

  • The pressing of a button
  • A node entering the viewport
  • The collision of two areas
  • A sound effect that finished playing

This a powerful system that allows you to keep your code flexible and loosely coupled, as the node emitting the signal doesn’t care about its subscribers.

To start off, open the jumper scene in the scenes folder and add a new script to the Jumper node named jumper.gd. Like with the player_avatar script, make sure to place it in the scripts folder.

Jumper and Path are highlighted

After creating the script, it should be automatically opened in the script editor. Remove both the _ready and _process functions, as you won’t need them. You should be left with just this line:

extends AnimatedSprite2D

In contrast to the player avatar’s Node2D, the root node type for the jumper is an AnimatedSprite2D, which inherits from Node2D and adds support for animations.
You’ll be using a signal to detect when the avatar enters a jumper’s Area2D node. Signals can be linked to a script either via the editor or via code, in this case via the editor makes the most sense. Select the Area2D node in the Scene dock and open the Node tab next to the Inspector tab.

Node tab highlighted

You’ll now see a list of signals the selected node supports.

Signals list

In the case of Area2D, this list is massive and full of useful signal to connect to. To detect when another Area2D enters this one, you’ll need the area_entered signal, so double click it to start connecting it. This will show a dialog window with some options for the connection.

Signal connect window

By default, it will select the root node, Jumper as the node to connect to. This is what you want, so there’s nothing to change there.
Below that is the Receiver Method, this is the name of the function that will be called when the area_entered signal is emitted. The default value, _on_area_2d_area_entered works fine in this case. Now click the Connect button to connect the signal, this will add the new function to the jumper script.

Green symbol before function code

From now on, whenever another Area2D enters the Jumper’s Area2D, this function will be called. You can tell the function is connected to a signal by the green “exit” symbol to the left of it. Clicking that shows what signals will call this function.

Area2D, area_entered, Jumper

To test if this actually works, replace the pass keyword with the line below:

print("Got hit!")

By now I’m sure you know exactly what this does: printing text to the console, a programmer’s best friend.
Save the script and open the game scene in 2D mode. Now drag a few instances of the jumper scene from the FileSystem dock onto the viewport, next to the player avatar.

Dragging jumper scene

What you just did there is called instantiating scenes, which is using a scene as a blueprint and creating instances of it in another scene. This powerful system allows you to create a scene once, and use it wherever you need.
With the jumpers added, press F5 to play the project. Move the avatar against the jumpers and you should see messages popping up in the console as expected.

Got Hit in console

Now to make things more interesting, the following should happen when a jumper gets hit:

  • Play a shatter sound
  • Play a shatter animation
  • Destroy the jumper at the end of the animation

How do you do all of this? By scripting of course! Open the jumper scene again and switch to the script editor. To play a sound effect, you need a reference to an AudioStreamPlayer node first. If you remember, jumper happens to have one named ShatterSound.

ShatterSound

There are two ways to add a reference to a node in your scripts when working in the built-in script editor: the boring manual method or the awesome speedy method. I’ll start off with the first one, which is adding the following line below extends AnimatedSprite2D:

@onready var shatter_sound : AudioStreamPlayer2D = $"ShatterSound"

I recommend typing this one out yourself if you haven’t been doing so already, to see how the auto completion helps you by listing all nodes in the scene once you get to the last part.

GIF of autocompletion

Here’s a breakdown of this variable declaration:

  • @onready is an annotation like @export. This makes it so the variable gets assigned when the node enters the node tree for the first time. This might ring a bell, as that’s the same time as the _ready function is called you’ve seen before. In essence, @onready allows you to write one-liners to create and assign a node to a variable.
  • var shatter_sound declares a new variable.
  • : AudioStreamPlayer2D specifies the type of the variable as a AudioStreamPlayer2D.
  • = $"ShatterSound" gets a reference to the node named ShatterSound. The dollar sign ($) is shorthand for the get_node() function.

Now you know what this line does, it’s time to learn about the more exciting way to add a reference to a node.
Remove the line you just added and start dragging the ShatterSound node from the Scene dock into the script editor. Hold CTRL/CMD before releasing your mouse button to complete the drop. This will add a node reference automatically, how cool is that?

Drag node to script editor with instructions to hold CTRL or CMD before releasing

You’re now one of the few people that know about this arcane knowledge, use this power well!
With the reference to the AudioStreamPlayer set up, you can now make it play its sound effect whenever the avatar hits the jumper by replacing the print call with this:

shatter_sound.play()

This calls the play function on the AudioStreamPlayer, which will make a nice glass shattering sound. Save the script and run the project to give it a try. Whenever the avatar passes over a jumper, you should hear the sound effect play.
If that’s working as expected, you can move on to playing the shatter animation, which is as simple as adding this line below the line you just added:

animation = "destroy"

Because Jumper is an AnimatedSprite2D node, it has a property called animation, which sets the current animation by its name. If you select the Jumper node and take a look at the bottom of the editor, you’ll see a list of available animations. The default animation is aptly called “default”, while the animation you’re setting here is called “destroy”.

default and destroy animations

Once again, it’s time to test if this is working as expected, so play the project and try moving into the jumpers again. The jumpers should now shatter when hit!

GIF of robot hitting orbs, which shatter

On to the actual self-destructing, which should happen after the animation finishes playing. There’s way of knowing when the animation ends at the moment, and while you could use some sort of timer, a much more elegant way is by using a signal. AnimationSprite2D has an animation_finished signal you can connect to, but connecting it via the editor now would result in it being emitted constantly as the default animation is playing on a loop.

The solution is to connect the signal via code after starting the destroy animation, as that guarantees perfect timing and the function is only being called once. To start off, create a new function that you want to be called by the signal to the end of the script:

func _destroy_animation_finished() -> void:
    queue_free()

As this function isn’t intended to be used outside of the script, it starts with an underscore. It does one thing: call the queue_free() method, which queues up the Jumper node for removal from the node tree, effectively destroying it.
To connect the animation_finished signal to the _destroy_animation_finished function, add the following line below animation = "destroy" in the _on_area_2d_area_entered function:

animation_finished.connect(_destroy_animation_finished)

This connects animation_finished to _destroy_animation_finished, just like how you did it before via the editor. The benefit of connecting signals via code is that it makes it easy to “rewire” signals just by editing a few variables and you can connect and disconnect them whenever you please. All of this plays into the modular nature of working with Godot and its nodes.
Play the project once again to test if the jumpers disappear after their animation finishes.
There is a small bug though that might not be obvious straight away: if you hit a jumper, move away and hit it again before the animation finishes, the sound effect will play a second time and you’ll get an error. Here’s what it says:

<code>jumper.gd:9 @ _on_area_2d_area_entered(): Signal 'animation_finished' is already connected to given callable 'AnimatedSprite2D(jumper.gd)::_destroy_animation_finished' in that object.</code>

This error is thrown because there was already a connection made on the animation_finished signal. In other words, the _on_area_2d_area_entered function got called twice on the same jumper, which is undesirable. To fix this, you can set a flag on the jumper that states whether the jumper is active or not. When inactive, it shouldn’t react to any collisions.
To implement this, add the following variable below extends AnimatedSprite2D:

var active : bool = true

This boolean will act as the flag and it starts as true, meaning jumper will be active out of the gate. Next, change the code inside the _on_area_2d_area_entered function as follows to use the flag:

if active: # 1
    active = false # 2
    shatter_sound.play()
    animation = "destroy"
    animation_finished.connect(_destroy_animation_finished)

Here’s what this does:

  1. An if-statement that checks if the jumper is active before doing anything else.
  2. Set the active flag to false. This will prevent the code being ran more than once.

Be sure to save the script once you’re done. With this safety check added, the script is bug-free and you’re ready to make the avatar jump.