Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
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.

Um eine Szene oder ein Skript automatisch zu laden, beginnen Sie im Menü und navigieren Sie zu Projekt > Projekteinstellungen > Globals > Autoload.

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.

Wenn die Spalte Aktivieren angekreuzt ist (was der Default ist), dann kann auf das Singleton direkt in GDScript zugegriffen werden:
PlayerVariables.health -= 10
Die Spalte Aktivieren hat in C#-Code keine Wirkung. Wenn das Singleton jedoch ein C#-Skript ist, kann ein ähnlicher Effekt erreicht werden, indem man eine statische Property namens Instance
einfügt und sie in _Ready()
zuweist:
public partial class PlayerVariables : Node
{
public static PlayerVariables Instance { get; private set; }
public int Health { get; set; }
public override void _Ready()
{
Instance = this;
}
}
Dies ermöglicht den Zugriff auf das Singleton aus C#-Code ohne GetNode()
und ohne Typecast:
PlayerVariables.Instance.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:

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.
A window notifying you that the project was last opened in an older Godot version may appear, that's not an issue. Click Ok to open the project.
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:

The next step is to add this script to the autoLoad list.
Starting from the menu, open
Project > Project Settings > Globals > Autoload and
select the script by clicking the browse button or typing its path:
res://global.gd
. Press Add to add it to the autoload list
and name it "Global", which is required for scripts to access it
by the name "Global":

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
# Using a negative index counts from the end, so this gets the last child node of `root`.
current_scene = root.get_child(-1)
using Godot;
public partial class Global : Node
{
public Node CurrentScene { get; set; }
public override void _Ready()
{
Viewport root = GetTree().Root;
// Using a negative index counts from the end, so this gets the last child node of `root`.
CurrentScene = root.GetChild(-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:
_deferred_goto_scene.call_deferred(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
public void GotoScene(string path)
{
// This function will usually be called from a signal callback,
// or some other function from 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:
CallDeferred(MethodName.DeferredGotoScene, path);
}
public void DeferredGotoScene(string path)
{
// It is now safe to remove the current scene.
CurrentScene.Free();
// Load a new scene.
var nextScene = GD.Load<PackedScene>(path);
// Instance the new scene.
CurrentScene = nextScene.Instantiate();
// Add it to the active scene, as child of root.
GetTree().Root.AddChild(CurrentScene);
// Optionally, to make it compatible with the SceneTree.change_scene_to_file() API.
GetTree().CurrentScene = CurrentScene;
}
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")
// Add to 'Scene1.cs'.
private void OnButtonPressed()
{
var global = GetNode<Global>("/root/Global");
global.GotoScene("res://Scene2.tscn");
}
und
# Add to 'scene_2.gd'.
func _on_button_pressed():
Global.goto_scene("res://scene_1.tscn")
// Add to 'Scene2.cs'.
private void OnButtonPressed()
{
var global = GetNode<Global>("/root/Global");
global.GotoScene("res://Scene1.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.