3 min read

Singleton Pattern und Autoloads

Singleton Pattern und Autoloads
Photo by Philipp Trubchenko / Unsplash

Heute soll es kurz und knapp um das Singleton Pattern gehen und warum man zwar von "Autoload Singleton" im Godot Kontext spricht, diese aber eigentlich "nichts" mit dem Pattern zu tun haben.

Außerdem möchte ich dir noch ein kleines Code Snippet an die Hand geben, mit dem du sehr einfach die Vorteile dieses Patterns genießen kannst.

💡
Godot macht ein Autoload nicht zu einem "echten" Singleton gemäß dem Singleton-Design-Pattern. Es kann immer noch mehr als einmal vom Benutzer instanziiert werden, falls gewünscht.
Singletons (AutoLoad)
Einführung: Das Szenensystem von Godot ist zwar leistungsfähig und flexibel, hat aber einen Nachteil: Es gibt keine Methode, um Informationen zu speichern (z.B. den Punktestand eines Spielers oder…

Autoloads sind gar keine Singletons?!

Richtig! Die Dokumentation sagt selbst, dass die Autoloads nicht das eigentliche Pattern implementieren, weil es möglich ist, ein Autoload mehrfach zu instanziieren. Das spricht gegen das Pattern.

Vorteile von Autoloads

Neben der Tatsache, dass man auf Autoloads von überall aus erreichen kann, ist es auch ein großer Vorteil, dass man diese im Godot Editor konfigurieren kann. Der Zusammenhang zu den Singletons kommt daher, dass die Autoloads wie Singletons verwendet werden soll(t)en. Im Endeffekt sind Autoloads auch einfach nur Nodes, die unter /root/ angehangen werden. Darüber sind sie auch immer schnell erreichbar.

Singleton Pattern

Das Singleton Pattern auf der anderen Seite bietet den Vorteil, dass technisch sichergestellt wird, dass es eine einzige Instanz einer Klasse gibt. Das ist besonders dann nützlich, wenn man weiß, dass es etwas nur exakt einmal im Spiel gibt und geben wird.

In einem Singleplayer Spiel wird das - wie der Begriff schon sagt - u.a. der Player sein. Wenn dieser das Singleton Pattern implementiert, kann total einfach von überall aus auf den Player zugegriffen werden.

Singleton Code Snippet

Ich implementiere das Pattern sehr gerne folgendermaßen:

class_name Player
extends CharacterBody3D

# Hier is das "Herzstück":
static var instance: Player = null

func _enter_tree() -> void:
  if not instance:
    instance = self

func _exit_tree() -> void:
  if instance:
    instance = null

# ... Rest von der Player Klasse ...

Nachtrag: "technically" implementiert man darüber auch nicht das Pattern, weil eine existierende Instanz lediglich auf null gesetzt wird. Damit auch wirklich ausschließlich eine Instanz aktiv sein kann, müsste man diese korrekt aufräumen. Da es keinen "richtigen" Konstruktor gibt, müsste man hier selbst etwas implementieren und per Konvention arbeiten. Ein statisches construct() könnte bspw. entweder die existierende Instanz zurückgeben oder eine neue Instanz erzeugen, dann könnte man halbwegs sicher stellen, dass es nur eine Instanz dieser Klasse gibt.

Wichtig ist hierbei, dass die instance Property statisch ist, das bedeutet nämlich, dass wir folgendes machen können: Player.instance.do_stuff(). So einfach können wir von überall aus auf die Player-Instanz zugreifen.

Wenn es also bspw. einen NPC gibt, der sein Verhalten abhängig vom Spielerzustand ändern soll, dann kann er einfach auf den Player zugreifen ohne ihn erst über eine Gruppe abzufragen oder ihn über Signale oder get_parent()-Ketten referenzieren zu müssen.

Der große Vorteil: Es ist total egal, wo sich der Player aktuell im SceneTree befindet. Die Stelle, die auf den Player zugreifen will, muss nicht den SceneTree-Aufbau kennen und sich auch nicht um die richtige Abarbeitung von Signalen kümmern o.ä.

Fazit

Ich finde das Pattern total wichtig und sinnvoll. Ich persönlich nutze es gerne um Systemen, die die Player-Klasse nicht kennen soll, Zugriff zum Player zu geben. Ein System welches eine Cutscene steuert kann so relativ einfach den Input vom Player disablen oder auch mal das HUD ausblenden. Ohne, dass die Player-Klasse dieses Cutscene-System kennen muss.

Was ist Deine Meinung dazu? Wie nutzt du Autoloads? Interessiert dich mein Ansatz und würdest du ihn mal in deinem eigenen Projekt ausprobieren? Wenn ja, was gefällt dir daran und wenn nein, warum nicht?