Engine singletons
It is important for you to understand the Singleton pattern to properly utilize this system.
The "Singleton pattern" is often referred to as an anti-pattern, because it violates several good practices for clean, modular code. However, it is also a tool that can be used to solve certain design problems. As such, it is used internally by Godot, and is available to godot-rust users as well.
Read more about criticisms here.
An engine singleton is registered through godot::classes::Engine
.
Custom engine singletons in Godot:
- are
Object
types - are always accessible to GDScript and GDExtension languages
- must be manually registered and unregistered in the
InitLevel::Scene
step
Godot provides many built-in singletons in its API. You can find a full list here.
Table of contents
- Defining a singleton
- Registering a singleton
- Calling from GDScript
- Calling from Rust
- Singletons and the
SceneTree
Defining a singleton
Defining a singleton is the same as registering a custom class.
#![allow(unused)] fn main() { #[derive(GodotClass)] #[class(init, base=Object)] struct MyEditorSingleton { base: Base<Object>, } #[godot_api] impl MyEditorSingleton { #[func] fn foo(&mut self) {} } }
Registering a singleton
Registering singletons is done during the InitLevel::Scene
stage of initialization.
To achieve this, we can customize our init/shutdown routines by overriding ExtensionLibrary
trait methods.
#![allow(unused)] fn main() { struct MyExtension; #[gdextension] unsafe impl ExtensionLibrary for MyExtension { fn on_level_init(level: InitLevel) { if level == InitLevel::Scene { // The StringName identifies your singleton and can be // used later to access it. Engine::singleton().register_singleton( StringName::from("MyEditorSingleton"), MyEditorSingleton::new_alloc().upcast(), ); } } fn on_level_deinit(level: InitLevel) { if level == InitLevel::Scene { // Get the `Engine` instance and `StringName` for your singleton. let mut engine = Engine::singleton(); let singleton_name = StringName::from("MyEditorSingleton"); // We need to retrieve the pointer to the singleton object, // as it has to be freed manually - unregistering singleton // doesn't do it automatically. let singleton = engine .get_singleton(singleton_name.clone()) .expect("cannot retrieve the singleton"); // Unregistering singleton and freeing the object itself is needed // to avoid memory leaks and warnings, especially for hot reloading. engine.unregister_singleton(singleton_name); singleton.free(); } } } }
Use a manually-managed class as a base (often Object
will be enough) for custom singletons to avoid prematurely freeing the object.
If for any reason you need to have an instance of a reference-counted object registered as a singleton, this
issue thread presents some possible workarounds.
Calling from GDScript
Now that your singleton is available (and once you've recompiled and reloaded), you should be able to access it from GDScript like so:
extends Node
func _ready() -> void:
MyEditorSingleton.foo()
Calling from Rust
You may also want to access your singleton from Rust as well.
#![allow(unused)] fn main() { godot::classes::Engine::singleton() .get_singleton(StringName::from("MyEditorSingleton")); }
For more information on this method, refer to the API docs.
Singletons and the SceneTree
Singletons cannot safely access the scene tree. At any given moment, they may exist without a scene tree being active.
While it is technically possible to access the tree through hacky methods, it is highly recommended to use a
custom EditorPlugin
for this purpose. Creating an EditorPlugin
allows for registering an "autoload singleton" which is a Node
(or
derived) type and is automatically loaded into the SceneTree
by Godot when the game starts.