Using signals

In this lesson, we will look at signals. They are messages that nodes emit when something specific happens to them, like a button being pressed. Other nodes can connect to that signal and call a function when the event occurs.

Signals are a delegation mechanism built into Godot that allows one game object to react to a change in another without them referencing one another. Using signals limits coupling and keeps your code flexible.

For example, you might have a life bar on the screen that represents the player’s health. When the player takes damage or uses a healing potion, you want the bar to reflect the change. To do so, in Godot, you would use signals.

Note

As mentioned in the introduction, signals are Godot's version of the observer pattern. You can learn more about it here: https://gameprogrammingpatterns.com/observer.html

We will now use a signal to make our Godot icon from the previous lesson (Listening to player input) move and stop by pressing a button.

Scene setup

To add a button to our game, we will create a new main scene which will include both a button and the Sprite2D.tscn scene that we scripted in previous lessons.

Create a new scene by going to the menu Scene -> New Scene.

../../_images/signals_01_new_scene.png

In the Scene dock, click the 2D Scene button. This will add a Node2D as our root.

../../_images/signals_02_2d_scene.png

In the FileSystem dock, click and drag the Sprite2D.tscn file you saved previously onto the Node2D to instantiate it.

../../_images/signals_03_dragging_scene.png

We want to add another node as a sibling of the Sprite2D. To do so, right-click on Node2D and select Add Child Node.

../../_images/signals_04_add_child_node.png

Search for the Button node type and add it.

../../_images/signals_05_add_button.png

The node is small by default. Click and drag on the bottom-right handle of the Button in the viewport to resize it.

../../_images/signals_06_drag_button.png

If you don't see the handles, ensure the select tool is active in the toolbar.

../../_images/signals_07_select_tool.png

Click and drag on the button itself to move it closer to the sprite.

You can also write a label on the Button by editing its Text property in the Inspector. Enter "Toggle motion".

../../_images/signals_08_toggle_motion_text.png

Your scene tree and viewport should look like this.

../../_images/signals_09_scene_setup.png

Save your newly created scene. You can then run it with F6 (Cmd + R on macOS). At the moment, the button will be visible, but nothing will happen if you press it.

Connecting a signal in the editor

Here, we want to connect the Button's "pressed" signal to our Sprite2D, and we want to call a new function that will toggle its motion on and off. We need to have a script attached to the Sprite2D node, which we do from the previous lesson.

You can connect signals in the Node dock. Select the Button node and, on the right side of the editor, click on the tab named "Node" next to the Inspector.

../../_images/signals_10_node_dock.png

The dock displays a list of signals available on the selected node.

../../_images/signals_11_pressed_signals.png

Double-click the "pressed" signal to open the node connection window.

../../_images/signals_12_node_connection.png

There, you can connect the signal to the Sprite2D node. The node needs a receiver method, a function that Godot will call when the Button emits the signal. The editor generates one for you. By convention, we name these callback methods "_on_NodeName_signal_name". Here, it'll be "_on_Button_pressed".

Note

When connecting signals via the editor's Node dock, you can use two modes. The simple one only allows you to connect to nodes that have a script attached to them and creates a new callback function on them.

../../_images/signals_advanced_connection_window.png

The advanced view lets you connect to any node and any built-in function, add arguments to the callback, and set options. You can toggle the mode in the window's bottom-right by clicking the Advanced button.

Click the Connect button to complete the signal connection and jump to the Script workspace. You should see the new method with a connection icon in the left margin.

../../_images/signals_13_signals_connection_icon.png

If you click the icon, a window pops up and displays information about the connection. This feature is only available when connecting nodes in the editor.

../../_images/signals_14_signals_connection_info.png

Let's replace the line with the pass keyword with code that'll toggle the node's motion.

Our Sprite2D moves thanks to code in the _process() function. Godot provides a method to toggle processing on and off: Node.set_process(). Another method of the Node class, is_processing(), returns true if idle processing is active. We can use the not keyword to invert the value.

func _on_Button_pressed():
    set_process(not is_processing())

This function will toggle processing and, in turn, the icon's motion on and off upon pressing the button.

Before trying the game, we need to simplify our _process() function to move the node automatically and not wait for user input. Replace it with the following code, which we saw two lessons ago:

func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

Your complete Sprite2D.gd code should look like the following.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_Button_pressed():
    set_process(not is_processing())

Run the scene now and click the button to see the sprite start and stop.

Connecting a signal via code

You can connect signals via code instead of using the editor. This is necessary when you create nodes or instantiate scenes inside of a script.

Let's use a different node here. Godot has a Timer node that's useful to implement skill cooldown times, weapon reloading, and more.

Head back to the 2D workspace. You can either click the "2D" text at the top of the window or press Ctrl + F1 (Alt + 1 on macOS).

In the Scene dock, right-click on the Sprite2D node and add a new child node. Search for Timer and add the corresponding node. Your scene should now look like this.

../../_images/signals_15_scene_tree.png

With the Timer node selected, go to the Inspector and enable the Autostart property.

../../_images/signals_18_timer_autostart.png

Click the script icon next to Sprite2D to jump back to the scripting workspace.

../../_images/signals_16_click_script.png

We need to do two operations to connect the nodes via code:

  1. Get a reference to the Timer from the Sprite2D.

  2. Call the connect() method on the Timer's "timeout" signal.

Note

To connect to a signal via code, you need to call the connect() method of the signal you want to listen to. In this case, we want to listen to the Timer's "timeout" signal.

We want to connect the signal when the scene is instantiated, and we can do that using the Node._ready() built-in function, which is called automatically by the engine when a node is fully instantiated.

To get a reference to a node relative to the current one, we use the method Node.get_node(). We can store the reference in a variable.

func _ready():
    var timer = get_node("Timer")