Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

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 sein Inventar), die von mehr als einer Szene benötigt werden.

Es ist möglich, dieses Problem mit einigen Workarounds zu beheben, aber diese haben ihre eigenen Grenzen:

  • Sie können eine "Master"-Szene verwenden, die andere Szenen als ihre Child-Elemente lädt und entlädt. Das bedeutet jedoch, dass Sie diese Szenen nicht mehr einzeln ausführen und erwarten können, dass sie korrekt funktionieren.

  • Informationen können auf der Festplatte unter user:// gespeichert und dann von Szenen geladen werden, die sie benötigen, aber häufiges Speichern und Laden von Daten ist umständlich und kann langsam sein.

Das Singleton-Pattern ist ein nützliches Werkzeug, um den häufigen Anwendungsfall zu lösen, bei dem Sie persistente Informationen zwischen Szenen speichern müssen. In unserem Fall ist es möglich, die gleiche Szene oder Klasse für mehrere Singletons wiederzuverwenden, solange sie unterschiedliche Namen haben.

Durch die Verwendung dieses Konzepts wird es Ihnen ermöglicht, Objekte zu instanziieren die:

  • Immer geladen werden, unabhängig davon, welche Szene gerade abgespielt wird.

  • Globale Variablen abspeichern können, wie z.B. Spielerinformationen.

  • Szenenwechsel und Szenenübergänge verarbeiten können.

  • Sich verhalten wie ein Singleton, da GDScript von Haus aus keine globalen Variablen unterstützt.

Das Autoloading von Nodes und Skripten kann uns diese Merkmale liefern.

Bemerkung

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.

Tipp

Wenn Sie einen Autoload als Teil eines Editor-Plugins erstellen, sollten Sie in Betracht ziehen, ihn automatisch in den Projekteinstellungen zu registrieren, wenn das Plugin aktiviert ist.

Autoload

Sie können ein Autoload erstellen, um eine Szene oder ein Skript zu laden, das von Node erbt.

Bemerkung

Wenn ein Skript automatisch geladen wird, wird ein Node erstellt und das Skript wird daran angehängt. Dieser Node wird dem Root-Viewport hinzugefügt, bevor weitere Szenen geladen werden.

../../_images/singleton.webp

Um eine Szene oder ein Skript automatisch zu laden, wählen Sie im Menü Projekt > Projekteinstellungen und wechseln Sie zum Autoload-Tab.

../../_images/autoload_tab.webp

Hier können Sie eine beliebige Anzahl von Szenen oder Skripten hinzufügen. Jeder Eintrag in der Liste benötigt einen Namen, der als Name-Property des Nodes zugewiesen wird. Die Reihenfolge der Einträge, wenn sie dem globalen Szenenbaum hinzugefügt werden, kann mit den oben/unten-Pfeiltasten geändert werden. Wie bei regulären Szenen liest die Engine diese Nodes in der Reihenfolge von oben nach unten.

../../_images/autoload_example.webp

Das bedeutet, dass ein Node auf ein Singleton namens "SpielerVariablen" zugreifen kann mit:

var player_vars = get_node("/root/PlayerVariables")
player_vars.health -= 10

Wenn die Spalte Aktivieren angekreuzt ist (was der Default ist), dann kann auf das Singleton direkt in GDScript zugegriffen werden, ohne get_node() zu benötigen:

PlayerVariables.health -= 10

Beachten Sie, dass auf automatisch geladene Objekte (Skripte und/oder Szenen) wie auf alle anderen Nodes im Szenenbaum zugegriffen wird. Wenn Sie sich den laufenden Szenenbaum ansehen, werden Sie die automatisch geladenen Nodes sehen:

../../_images/autoload_runtime.webp

Warnung

Autoloads dürfen nicht mit free() oder queue_free() zur Laufzeit entfernt werden, sonst stürzt die Engine ab.

Benutzerdefinierter Szenenwechsler

Dieses Tutorial demonstriert den Aufbau eines Szenenwechslers mit Autoloads. Für einfache Szenenwechsel können Sie die Methode SceneTree.change_scene_to_file() verwenden (siehe Verwendung von ScreneTree für Details). Wenn Sie jedoch ein komplexeres Verhalten beim Ändern von Szenen benötigen, bietet diese Methode mehr Funktionalität.

Um zu beginnen, laden Sie die Vorlage von hier herunter: singleton_autoload_starter.zip und öffnen Sie sie in Godot.

Das Projekt enthält zwei Szenen: scene_1.tscn und scene_2.tscn. Jede Szene enthält eine Beschriftung, die den Namen der Szene anzeigt, und einen Button, dessen pressed() Signal angeschlossen ist. Wenn Sie das Projekt starten, beginnt es in scene_1.tscn. Das Drücken des Buttons bewirkt jedoch nichts.

Erstellen des Skripts

Öffnen Sie das Skript-Fenster und erstellen Sie ein neues Skript mit dem Namen global.gd. Stellen Sie sicher, dass es von Node erbt:

../../_images/autoload_script.webp

Der nächste Schritt besteht darin, dieses Skript zur AutoLoad-Liste hinzuzufügen. Öffnen Sie Projekt > Projekteinstellungen aus dem Menü, wechseln Sie zum Autoload-Tab und wählen Sie das Skript aus, indem Sie auf den Durchsuchen-Button klicken oder den Pfad eingeben: res://global.gd. Drücken Sie Hinzufügen, um es der Autoload-Liste hinzuzufügen:

../../_images/autoload_tutorial1.webp

Ab jetzt würde bei jeder Ausführung des Projekts das Skript automatisch geladen werden.

Um zum Skript zurückzukehren, muss es sich die aktuelle Szene mit der Funktion _ready() holen. Sowohl die aktuelle Szene (die mit dem Button) als auch global.gd sind Child-Elemente von root, aber automatisch geladene Nodes kommen immer zuerst. Das bedeutet, dass das letzte Child-Element von root immer die geladene Szene ist.

extends Node

var current_scene = null

func _ready():
    var root = get_tree().root
    current_scene = root.get_child(root.get_child_count() - 1)

Als nächstes benötigen wir eine Funktion, um die Szene zu modifizieren. Diese Funktion muss die aktuelle Szene leeren und diese mit der gewünschten ersetzen.

func goto_scene(path):
    # This function will usually be called from a signal callback,
    # or some other function in the current scene.
    # Deleting the current scene at this point is
    # a bad idea, because it may still be executing code.
    # This will result in a crash or unexpected behavior.

    # The solution is to defer the load to a later time, when
    # we can be sure that no code from the current scene is running:

    call_deferred("_deferred_goto_scene", path)


func _deferred_goto_scene(path):
    # It is now safe to remove the current scene.
    current_scene.free()

    # Load the new scene.
    var s = ResourceLoader.load(path)

    # Instance the new scene.
    current_scene = s.instantiate()

    # Add it to the active scene, as child of root.
    get_tree().root.add_child(current_scene)

    # Optionally, to make it compatible with the SceneTree.change_scene_to_file() API.
    get_tree().current_scene = current_scene

Durch Nutzung von Object.call_deferred() wird die zweite Funktion nur ausgeführt, wenn das komplette Programm der aktuellen Szene beendet ist. Daher wird die aktuelle Szene nicht gelöscht, solange sie noch genutzt wird (z.B. wenn sein Code noch läuft).

Schlussendlich müssen wir noch die leeren Callback-Funktionen in den zwei Szenen ausfüllen:

# Add to 'scene_1.gd'.

func _on_button_pressed():
    Global.goto_scene("res://scene_2.tscn")

und

# Add to 'scene_2.gd'.

func _on_button_pressed():
    Global.goto_scene("res://scene_1.tscn")

Führen Sie das Projekt aus und überprüfen Sie, dass Sie durch die Betätigung des Buttons zwischen den Szenen hin- und her springen können.

Bemerkung

Bei kleinen Szenen erfolgt der Übergang sofort. Wenn Ihre Szenen jedoch komplexer sind, kann es eine merkliche Zeit dauern, bis sie erscheinen. Wie man damit umgeht, erfahren Sie im nächsten Tutorial: Laden im Hintergrund.

Wenn die Ladezeit relativ kurz ist (weniger als 3 Sekunden oder so), können Sie alternativ eine "Ladeplakette" anzeigen, indem Sie kurz vor dem Ändern der Szene irgendein 2D-Element anzeigen. Sie können es dann direkt nach dem Ändern der Szene ausblenden. Dies kann verwendet werden, um dem Spieler anzuzeigen, dass eine Szene gerade geladen wird.