Manually registering Singletons at runtime

Godot Version

4.x

Question

The docs mention that Global Singletons are not the same as Autoloads but it doesn’t specify how exactly they differ.

We can use Engine.register_singleton() to manually add Singletons at runtime that will, with one line of code, behave just like an Autoload.

var signal_bus: Object = load("res://signal_bus.gd").new()
Engine.register_singleton("SignalBus", signal_bus)

This should allow us to use SignalBus globally. However, because we are registering it at runtime, we cannot make use of said feature in the editor (since it’s not yet registered).

In order to access it, we need to fetch it using:

var signal_bus: Object = Engine.get_singleton("SignalBus")

Now it will work as intended and we can use any of the methods/signals/properties available to the class.

There is at least one other problem with this approach. It bricks the autocomplete feature, since it doesn’t know what the Object has access to.

To fix this, we can simply use class_name SignalBus in the script file we want to register (in this case signal_bus.gd), and then refactor the above to:

var signal_bus: SignalBus = load("res://signal_bus.gd").new()
Engine.register_singleton("SignalBus", signal_bus)
var singal_bus: SignalBus = Engine.get_singleton("SignalBus")

Works as intended, but it looks like there should be a naming conflict between the Singleton named SignalBus and the Class named SignalBus.

There sure is one when you try to do the same for Autoloads:

I am unsure if this is a bug or intended, since there is no mention of what the differences are between Global Singletons and Autoloads. I assume as long as it works it should be fine. Changing one of the names just to be 100% certain is also fine.

In this case class_name makes a globally identifiable name, which autoload is trying to do the same. You can avoid the class_name by preloading your script as it’s own const

const SignalBus = preload("res://signal_bus.gd")
var signal_bus: SignalBus = SignalBus.new()
Engine.register_singleton("SignalBus", signal_bus)

####

const SignalBus = preload("res://signal_bus.gd")
var singal_bus: SignalBus = Engine.get_singleton("SignalBus")