What You’ll Learn in This Hour:
Using Godot’s notification system
Singletons inGodot’s API
Creating custom autoloads/singletons
Editor tools
Interfacing with other Godot scripting languages
In Hour 4, “Scripting,” you learned about the basics of scripting inGodot using GDScript, and in Hour 5, “Game 1: Space Shooter,” you
designed and implementing a space shooter game. By now, you should have some experience and familiarity withGDScript. GDScript and
Godot have a lot more to offer, so it’s time to expand that field of knowledge even more.
In this hour, you’ll learn about Godot’s notification system, a way to pass “messages” to certain objects and nodes. We will also explore the
world of singletons and autoloads, the latter of which provides a way to define custom singletons. Godot’s Editor is very flexible, and lets you
run scripts to make changes to scenes using code. The ability to have multiple languages in one project brings up the question of how these
languages should interact. This will be covered in this section.
Notifications
Godot has a notification system through which objects can send messages to other objects. Notifications are used to guide the initialization
and deletion of objects, and to receive events not available through the input singleton or the InputEvent system. The notification system is a
low-level system that’s usually not used very often in script code, but sometimes you need control over more things not exposed to script-
exposed classes.
A “notification” is just a number (see Listing 6.1). That number usually corresponds to a constant defined somewhere else (unless you send
your own notification that does not represent a constant).
LISTING6.1 Sample Notification
Click here to view code image
extends Node
func notification(what):
if what == MainLoop.NOTIFICATION_WM_FOCUS_IN:
print(“Welcome back!”)
elif what == MainLoop.NOTIFICATION_WM_FOCUS_OUT:
print(“Bye, have a great time!”)
elif what == NOTIFICATION_PREDELETE:
destructor()
func destructor():
print(“This object is about to be deleted.”)
# cleanup here
The methods _ready, _process, and many others are implemented using notifications. You will see how later in this section.
Sending Notifications
To send a notification to an object, the method Object.notification(int what, bool reverse=false) is used.
The what argument is the notification to send. The reverse argument specifies the order in which those notifications are processed. Every
class has the option to handle a notification; sometimes, it’s necessary for the parent classes to process it after the children have processed it.
Usually, you never need to specify that argument, as the default value causes the desired behavior in most cases.
Object.notification() only operates on one object, but since many things center around Scene Tree inGodot, every node has a
method, Node.propagate_notification(int what), that will “hand” the notification down the tree. It will first call _notification
on the node itself, then on all the children.
Receiving Notifications
In the introduction to this section, you saw a small example that used _notification. The _notification method receives all
notifications sent to an object.
The following GDScript code prints the value (number) of every notification received:
Click here to view code image
extends Node
func _notification(what):
print(“got notification: “, what)
111111111
22222222The what parameter is the number that represents the notification. Usually, if or match statements are used to branch execution based on
that argument. In the next subsection, we’ll see some common notifications and how to use them effectively.
Commonly Used Notifications
TIP
In-Editor Documentation
If you Ctrl + Left click on a class name, the editor help will open. You can see all the notifications defined by that class on those pages in the
code editor.
Object Notifications
As mentioned in Listing 6.1, NOTIFICATION_PREDELETE is useful in implementing a destructor-like functionality. This notification gets sent
just before the object gets deleted. Listing 6.2 is an example script that shows one possible usage of NOTIFICATION_PREDELETE.
LISTING6.2 Sample Notification
Click here to view code image
extends Object
# open files, log into network or something else…
func _notification(what):
if what == NOTIFICATION_PREDELETE:
print(“I’m about to be deleted!”)
some_file.close()
networking.unregister(self)
There’s also NOTIFICATION_POSTINITIALIZE, but it’s not usable from scripts, because at that point in time, the script has not yet been
attached to the object. For initialization, use _init or, in case of nodes, _ready.
Node Notifications
As we know, a node is something that can be part of a tree, so the node class defines many notifications that have to do with the Scene Tree.
NOTIFICATION_ENTER_TREE: Gets sent when the node enters the tree. It means the children have not yet been added and the node
has entered the tree on its own. It’s advisable to override the _en
t
er_tree method instead.
NOTIFICATION_EXIT_TREE: Gets sent when the node is about to leave the tree. You should override the _exit_tree method
instead.
NOTIFICATION_MOVED_IN_PARENT: Gets sent when the node gets moved using the Node.move_child method.
NOTIFICATION_READY: Gets sent when the node enters the tree and all the children get initialized as well. You should override
_ready instead.
NOTIFICATION_FIXED_PROCESS: When set_fixed_process(true) is called (or a _fixed_process method is present),
this notification gets sent every “fixed process frame.” It’s similar to _process, but it’s not running as fast as the frame rate. Instead, it’s
running as fast as the physics engine simulates the world. The delta time can be acquired with the
Node.get_fixed_process_delta_time method.
NOTIFICATION_PROCESS: This notification gets sent for every frame the engine processes (when set_process(true) was
called or a _process method is present). The delta parameter can be acquired with the Node.get_process_delta_time
method. You should override the _process method instead of using notifications.
NOTIFICATION_PARENTED: Gets sent when the node gets assigned a parent node.
NOTIFICATION_UNPARENTED: Gets sent when the node’s parent gets removed (it no longer has a parent, but it still exists).
NOTIFICATION_PAUSED: Gets sent when the node processing is paused (using get_tree().set_pause(true)). It stops the
node from running _process, _fixed_process, and the _input methods.
NOTIFICATION_UNPAUSED: Like NOTIFICATION_PAUSED, but gets called again when the node gets unpaused.
NOTIFICATION_INSTANCED: Gets sent when a scene gets instanced and all the nodes in it are created.
NOTIFICATION_TRANSLATION_CHANGED: Gets sent when the selected language in a game gets changed (by using
TranslationServer.set_locale). This gets used to update all the texts that can be localized.
Main Loop Notifications
A Main Loop is an object that defines how an application runs inGodot. In the case of games, it’s the Scene Tree (which inherits the Main
Loop). The Scene Tree includes the tree of nodes. It also decides when to quit the game, and it receives some unique notifications concerning
window focus.
NOTIFICATION_WM_MOUSE_ENTER: Gets sent when the mouse cursor enters the window.
NOTIFICATION_WM_MOUSE_EXIT: Gets sent when the mouse cursor leaves the window.
NOTIFICATION_WM_FOCUS_IN: Gets sent when the window gains focus. This happens when the window is out of focus and gains
focus by a user clicking into the window or by using a key combination (commonly alt + tab) or the taskbar.
111111111
22222222NOTIFICATION_WM_FOCUS_OUT: Gets sent when the window loses focus. Can be used to open a menu or pause the game to save
resources whenever the player doesn’t have the game window active.
NOTIFICATION_WM_QUIT_REQUEST: Gets sent when the game should close. By default, the game simply closes, but this
notification gets sent anyway. If the game should not close automatically, get_tree().set_auto_accept_quit(false) should
be used. This way, the game does not close and can, for example, display a “Saving?” dialog. To close the game manually,
get_tree().quit() must be used.
These are the most commonly used notifications, but of course, there are many other notification types.
In composition with Node.propagate_notification, it can be useful to pass custom notifications down the node tree, but be careful to
not “shadow” an existing notification. Usually, it’s better to use groups with SceneTree.call_group. You can also use the
Node.propagate_call method to call a method on all nodes in a branch.
Singletons and Autoloads
This section covers singletons, a commonly used design pattern, and autoloads, which are nodes loaded at startup that remain loaded during
an app’s runtime. Both are important to understand and use inGodot.
Singletons
One of the most popular and commonly used design patterns in object-oriented programming is the singleton pattern. The singleton pattern
ensures that there is only one object (sometimes called the _instance) of a certain class. This is desired when you need a globally accessible
state or don’t want programmers to create their own instances of that type. Often, this object is accessed through a class-method (meaning it
doesn’t require an object to call) that returns a reference to that object.
InGodot’s codebase (which is in C++), you’ll often see code like this:
Click here to view code image
OS::get_singleton()->print(“Hello World”);
Input::get_singleton()->is_action_pressed(“ui_accept”);
Engine::get_singleton()->get_frames_per_second();
You can see that a method get_singleton gets called. It’s the method that returns a reference to the only instance of that class.
In Hour 4, the Input singleton was used to check if events had been pressed. There are many more singletons inGodot that can be used in
GDScript and other scripting languages.
Because GDScript was made to make scripting as easy and straightfo
r
ward as possible, it has a simplified syntax for accessing singletons.
Instead of calling a method to access the singleton, GDScript knows which singletons exist, and lets you access them by writing the name of
the class as if it were the object.
Let’s take a look at a comparison of singleton usage in C++ (Godot source code) and GDScript.
Here is the C++ code:
Click here to view code image
// C++
Input::get_singleton()->is_action_pressed(“ui_accept”)
And here is the GDScript:
Click here to view code image
# GDScript
Input.is_action_pressed(“ui_accept”)
Commonly Used Singletons
Here is a list of commonly used singletons:
ResourceLoader: Has methods for loading resources from the file system. ResourceLoader.load_interactive can be used to
load resources without blocking the main thread.
OS: Has methods for using operating system functions, such as setting and getting the contents clipboard, starting new processes, and
changing window properties (full screen, title, size).
Engine: Has methods for getting information about some properties of the running engine, such as get_frames_per_second(),
get_version_info(), get_target_fps(), and get_frames_drawn().
ClassDB: This class has all the information about Godot classes you can use in any scripting language. It can be used to create
instances of classes you only know at runtime, for example, after prompting the user what to do. This can be done using
ClassDB.instance(class_name), where class_name is a string (that can be computed at runtime). It also has methods for
querying information about classes, such as class_get_method_list, class_get_signal_list, and
get_inheriters_from_class.
ProjectSettings: Can be used to set and get values from Project Settings. Using get and set, it is the programmatic counterpart to
the Project Settings window in the editor, and is useful for changing settings in an in-game menu. The save method saves the changes to
the project configuration file.
111111111
22222222InputMap: The programmatic counterpart to the Input Map tab in the Project Settings window; add_action and
action_add_event are the most commonly used methods with this singleton. They add a new input action to the input map (which can
be checked with Input.is_action_pressed), and add an event to an action. Thus, when an event known to that action occurs,
InputMap knows which event triggers which action. An “event” refers to an input event like a key press or a joystick button, which gets
mapped to an action.
Performance: Can be used to get information about various processes in the engine, such as the number of draw calls used in the last
frame, how many objects currently exist, how many nodes exist, how much memory is currently used, and much more. This information can
be queried using the get_monitor method, which takes one of the many constants that this class defines as the argument.
Input: Used to get information about all kinds of user input methods, this is easily the most-used singleton inGodot. There are methods
for getting joystick, gyroscope, and magnetometer states, and for controlling gamepad vibration and user-defined input events. There’s
more about these singletons in Hour 7, “Handling Input.”
AutoLoad
Having global objects accessible from everywhere can be really useful, so it would be a shame if the same wasn’t possible for objects with
scripts attached to them. Godot’s answer to that is AutoLoad. An autoload is a node that gets loaded at startup and stays loaded for the entire
runtime of the application/game.
Autoloads can also be scenes loaded from a path to a scene file. This means the scene can be created from the editor and have a scene tree
(multiple nodes), and a root of a specialized type (not just a Node).
An autoload is always located in the scene tree as a child of the root node (see Listing 6.3). The node itself is a simple Node node (because
the autoload is there for its custom behavior, not the node behavior). The name of the node is the name of the autoload. So, an autoload
named “GlobalGameState” would be located in /root/GlobalGameState.
LISTING6.3 Sample Autoload Code
Click here to view code image
extends Node
var coins_collected = 0
func coin_collected():
coins_collected += 1
func save_state():
# write the state into a file
func read_state():
# read the state from a file
To create an autoload, you have to create a script that extends the Node and saves it somewhere in the project directory. Because Godot
loads all the autoloads, it needs to know which scripts are supposed to be used under which name. This can be done in the Project Settings
window in the AutoLoad tab. Once you provide a name and the path to the script, the autoload will be loaded on start-up (see Figure 6.1).
FIGURE 6.1
Autoloading a script in the AutoLoad tab.
When that is done, you can use the autoload from every script (see Listing 6.4), just like engine singletons.
LISTING6.4 Autoloading fromEvery Script
Click here to view code image
extends KinematicBody2D
# player variables here
func _on_Area_entered(area):
if area.is_in_group(“coin”):
# collected a coin.
GameState.coin_collected()
emit_signal(“coin_collected”)
TIP
Autoload Location
Because autoloads are just nodes in the tree, you can access them with get_node too! The previous code also works when
$”/root/GameState .coin_collected()” is used.
Editor Tools
Scripts inGodot can operate in a so-called “tool” mode. The tool mode lets the script run in the editor instead of just when you run the scene.
111111111
22222222NOTE
It’s All a Game
The Godot editor itself is an application built using Godot. It’s the reasonGodot has so many control nodes—the editor uses all those UI
elements. Because the editor is just a “game,” it can run scripts too.
There are two common ways to use tool mode scripts: creating an EditorPlugin or an EditorScript. While editor plugins comprise a huge
topic, editor scripts are a lot easier to get into and grasp. Both share a common interface, so learning about using EditorScript will also help
when trying to write an editor plugin.
If a script inherits from EditorScript, it can run in the editor, make changes to the current scene, and perform other actions that the editor can
do, such as importing files or saving scenes and resources.
One use of an editor script is to let it make changes to your scene that are tedious to make by hand. For example, it can be used for
procedurally generating levels, which then get an “upgrade” by the designers and artists. So, it’s nice to have a basic framework with which to
work and have those levels generated automatically.
To showcase that functionality, we will add a circle of Sprite nodes to the current scene. The script can also be modified to help you create
beautiful images (see Listing 6.5).
LISTING6.5 Adding Sprite Nodes to the Scene
Click here to view code image
tool
extends EditorScript
var texture = preload(“res://icon.png”)
var origin = Vector2(500, 250)
var radius = 200
var num_sprites = 20
func _run():
for i in range(num_sprites):
var node = Sprite.new()
node.texture = texture
node.set_name(“sprite” + str(i))
get_scene().add_child(node)
node.set_owner(get_scene())
node.scale = Vector2(0.5, 0.5)
node.position = origin + Vector2(radius, 0).rotated((2 PI) / num_sprites i)
Figure 6.2 shows how you can use EditorScript.
FIGURE 6.2
Running EditorScript and seeing the change in the scene.
In Figure 6.3, you can see the output from successfully running the script in Figure 6.2.
FIGURE 6.3
The successful outcome from the script shown in Figure 6.2.
Every script that gets executed in the editor must have the tool keyword at the top of the file.
As you can see, the script must inherit EditorScript to access the editor functionality. The _run method gets called when the script runs in the
editor (see the Q&A section later for more). The _run method is special to EditorScript, so it can’t be used in any arbitrary script (well, it can,
but it won’t be called unless you call it manually).
Because the editor is an application inGodot, it uses the Scene Tree. And because the editor has its own Scene Tree, the autoloads
configured in Project Settings aren’t loaded, so you can’t use them in tool scripts.
The get_scene method returns a reference to the root node of the current scene. This reference can be used to add new child nodes to the
scene. The editor script creates new Sprite nodes, adds them as children, and sets the position so that they are all positioned in a way to
estimate the shape of a circle.
TIP
Adding Nodes to the Scene
111111111
22222222The previous editor script adds new nodes to the scene by using the add_child method. This adds the children to the tree, but the current
scene doesn’t know about it; the children are just living in the editor, and the scene doesn’t care about them much. To let the scene know about
those children so they don’t just hang around and get removed, the method set_owner needs to be used after the call to add_child. Then
the new child nodes will show up in the Scene tab.
The “owner” of a node indicates that the node belongs to the scene being edited, so it should be saved and shown in the Tree tab. Similarly,
when a subscene is instantiated inside the scene being edited, the owner of all the nodes of the subscene will be the root of the subscene, and
not the root of the scene being edited. That’s how the editor knows not to save those nodes when the scene is saved.
EditorInterface
EditorScript has access to the editor interface through the get_editor_interface method. Godot’s EditorInterface has many useful
methods for interacting with the editor. Here are a few methods that you might find useful:
get_editor_settings: Similar to ProjectSettings, this can be used to access and change the editor settings with code.
get_editor_viewport: Returns a reference to the viewport in the editor. This can be used to draw custom things on the viewport.
get_open_scenes: Returns an array of paths to scenes that are currently opened inside the editor.
open_scene_from_path: Used to open scenes like a user does when he uses the UI“Scene → Open Scene.”
save_scene and save_scene_as: Used to save modified scenes to a disk. This could be used in an editor plugin that automatically
saves all scenes in a certain interval.
Interfacing with Other Godot Scripting Languages
Godot’s internal architecture allows for multiple scripting languages and systems to co-exist in a project and even communicate with one
another.
The way the system works influences how GDScript works internally. It’s especially easy to call methods defined in other scripting languages,
because it’s specifically made for Godot.
The uniform way to call methods on an object is to use Object.call or Object.callv. This will work in all languages, no matter from
which language you’re calling.
The wayGDScript’s calling mechanism works is exactly the same. If you type some_object.some_method(a, b), internally it performs
some_object.callv(“some_method”, [a, b]). The same syntax can be used across all languages, as shown here and illustrated
in Figure 6.4.
FIGURE 6.4
A GDScript calling a method on a VisualScript.
Click here to view code image
extends Node
func _ready():
$”../VSNode”.a_visual_script_method()
Different languages might have different ways of calling callv, but that’s what it all comes down to. Alternatively, Variant.call can be
used. This also automatically handles methods of core types, such as String.length. As long as a language binding exposes these
methods, it can call into any other scripting language used inGodot.
Summary
In this hour, you learned more about singletons and their usage inGodot. You also saw how to make your own singletons by creating
autoloads, which are available in every script. Because the editor is built using Godot itself, it’s possible to script inside of it, which helps bring
procedural generation and many other things right to the editor and to scenes you’re working on. Godot’s object system allows different
scripting languages to interoperate seamlessly; you know how to call back and forth between languages.
Q&A
Q. What is the difference between autoloads and in-engine singletons?
A. Autoloads are user-defined nodes that are children of the /root/ node. Singletons are C++ classes that are made available to the
scripting system.
Q. How can you change project settings fromwithin a game?
A. The ProjectSettings singleton can be used to set and get settings. They can be updated by using
ProjectSettings.get(“setting/name”) and ProjectSettings.set(“setting/name”, new_value).
Q. How can a script run inside the editor?
A. Scripts can be in “tool” mode, which enables them to run inside the editor. The tool mode gets activated by writing the “tool” keyword at the
top of the GDScript file. Other scripting languages might have a different mechanism to activate tool mode.
Workshop
111111111
22222222Answer the following questions to make sure you understood the content of this hour.
Quiz
1. What method needs to be defined to receive notifications?
2. What notification gets sent when the current object is about to be destroyed?
3. How do you access an autoload’s method test when the autoload’s name is MyAutoLoad?
4. What singleton can be used to query information about the game’s performance?
5. How do you run an editor script inside the editor?
Answers
1. The _notification method.
2. The NOTIFICATION_PREDELETE notification.
3. The call would be MyAutoLoad.test().
4. The Performance singleton, but some methods on the Engine singleton are useful too.
5. Select the script to run in ScriptEditor, then click “run” in the “file” menu (or Ctrl + Shift + X).
Exercises
Editor scripts can be really useful. Try to get more familiar with them by letting one automatically generate scenes for different enemy types:
1. Create an editor script by using the “new” entry in the “file” menu inside the ScriptEditor. Name it however you want, but it needs to inherit
EditorScript, and it needs to be in tool mode.
2. Define an array of names for the enemy scenes. This can be done with var enemies = [“enemy_name_0”,
“enemy_name_1”, … ]. You can iterate over all the elements with for entry in enemies:, where entry will be the name of
the currently iterated element.
3. For each element, create a node with the name of the enemy (with set_name). You can get creative here. Create a Sprite and use the
“modulate” property to tint the texture. You can also add timers or other nodes—don’t be afraid to explore some nodes you haven’t seen
yet.
4. Create a scene with the PackedScene class. To create a PackedScene object, use PackedScene.new(). To use the recently
created node as the root node for the scene, you have to use the pack(Node node) method on the PackedScene object.
5. Save the scene by using ResourceSaver singleton’s method save with the file path as the first argument and the scene as the second
argument.