Szenen-Baum

Einführung

In der vorangegangen Anleitung drehte sich alles um das Konzept der Nodes. Szenen sind Sammlungen von Nodes. Sie werden aktiv sobald sie in den Szenen-Baum (scene tree) eingefügt werden.

Hauptschleife

Godot arbeitet intern wie folgt. Eine OS-Klasse (OS) ist die einzige Instanz die zu Begin ausgeführt wird. Darauf folgend werden Treiber, Server, Scriptsprachen, Szenen-System etc. geladen.

Sobald die Initialisierung abgeschlossen ist, wird OS ein MainLoop übergeben um ausgeführt zu werden. Bis zu diesem Punkt ist alles interner Ablauf (siehe main/main.cpp im source code falls Interesse besteht die Internen Abläufe nachzuvollziehen).

Die Nutzer-Anwendung, oder das Spiel, startet im MainLoop. Diese Klasse definiert Methoden für die Initialisierung, Idle (Bild-synchrone Rückrufmethode), fixed (Physik-synchrone Rückrufmethode) und Eingabe (input). Dies sind tief gehende Funktionen, welche in der typischen Erstellung eines Spiels nur sehr selten verwendet werden.

Szenen-Baum

Godot kann so erklärt werden, dass es eine High Level Game Engine über einer Low Level Middleware ist.

Das Szenensystem ist die Game Engine, wohingegen das :ref:`OS <class_OS> und Server zur Low Level API gehören.

Das Szenen-System stellt eine Implemetierung des MainLoops dar (SceneTree) welcher OS so übergeben wird. Dies geschieht standardmäßig automatisch sobald eine Szene ausgeführt wird und erfordert kein manuelles Eingreifen.

Es ist wichtig zu wissen, dass es diese Klasse gibt, weil sie einige wichtige Verwendungszwecke hat:

  • Es beinhaltet die Wurzel Viewport, zu welcher eine Szene hinzugefügt wird, als Kind, welches beim ersten Öffnen Teil des Szenen-Baums (Scene Tree) wird.
  • Es enthält Informationen zu Gruppen und hat Möglichkeiten Node-Methoden einer Gruppe aufzurufen oder eine Liste dieser zu erstellen.
  • Es beinhaltet einige Funktionalität zum Globalen Zustand, wie dem Pausieren oder Beenden des Prozesses.

Wenn ein Node teil des Szenen-Baums (Scene Tree) ist, kann auf den Szenen-Baum (SceneTree) als Singleton über :ref:`Node.get_tree() <class_Node_method_get_tree>`zugegriffen werden.

Wurzel Fenster (Viewport)

Das Wurzel-Fenster (Viewport) ist immer am oberen Ende der Szene. Durch einen Node kann über folgende Wege auf es zugegriffen werden:

get_tree().get_root() # Access via scene main loop.
get_node("/root") # Access via absolute path.
GetTree().GetRoot(); // Access via scene main loop.
GetNode("/root"); // Access via absolute path.

Dieser Node enthält das Haupt-Fenster (Viewport - Viewport). Alles was diesem untergeordnet ist wird standardmäßig dargestellt, so macht es Sinn das die Wurzel aller Nodes immer ein Node dieses Typs ist, andererseits ist nichts sichtbar.

Wärend andere Fenster (Viewport) in der Szene erstellt werden können (für Split-screens z.B.), ist dieses das einzige das nicht vom Nutzer erstellt wird. Es wird automatisch durch den Szenen-Baum (SceneTree) bereitgestellt.

Szenen-Baum (SceneTree)

Wenn ein Node eingebunden wird, direkt oder indirekt, in das Wurzel-Fenster (Viewport) wird es Teil des Szenen-Baums (SceneTree).

Dies bedeutet, dass wie in der vorherigen Anleitung schon gezeigt, es die _enter_tree() und _ready() callbacks erhält (als auch _exit_tree()).

../../_images/activescene.png

Sobald Nodes den Szenenbaum betreten werden sie aktiv. Sie erhalten Zugang zu allem, was sie verarbeiten müssen, erhalten Eingabe, zeigen 2D- als auch 3D Grafiken, erhalten und senden Benachrichtigungen, spielen Töne ab, et cetera. Doch als bald sie vom Szenenbaum entfernt werden verlieren sie jene Fähigkeiten.

Baumstruktur

Die meisten Node Tätigkeiten in Godot, wie z.B. 2D Zeichen, Verarbeiten, oder Benachrichtigungen erhalten, werden nacheinander entsprechend der Baumstruktur abgearbeitet. Dies hat zur Folge, dass Eltern- als auch Geschwisterelemente mit einer tiefergelegenen Positionen benachrichtigt werden vor dem aktuellen Node.

../../_images/toptobottom.png

"Werden aktiv" durch das Betreten des Szenenbaums

  1. Eine Szene wird von der Festplatte geladen oder selbst geskriptet.
  2. Das Root-Node jener Szene (nur ein einziges Root-Element, schon vergessen?) wird entweder als Kind des "Root-Viewports" (vom Szenenbaum), oder als Kind oder Enkel hinzugefügt.
  3. Jedes Node der neu hinzugefügten Szene wird die "enter_tree"-Benachrichtigung (_enter_tree() callback in GDScript) von oben aus abwärts nach unten erhalten.
  4. Eine zusätzliche Mitteilung, "ready" ( _ready() Aufruf in GDScript) wird erstellt, wenn das Node und alle seine Kinder innerhalb der aktiven Szene sind.
  5. Sobald eine Szene (oder ein Teil davon) entfernt wird, erhalten sie die "exit scene"-Benachrichtigung (_exit_tree() callback in GDScript) in von unten nach oben gehenden Reihenfolge

Änderung der aktuellen Szene

Nachdem eine Szene geladen wurde ist es oftmals erwünscht jene Szene umzutauschen mit einer anderen. Der einfachste Weg dies zu tun ist mittels der SceneTree.change_scene() Funktion:

func _my_level_was_completed():
    get_tree().change_scene("res://levels/level2.tscn")
public void _MyLevelWasCompleted()
{
    GetTree().ChangeScene("res://levels/level2.tscn");
}

Anstelle Dateipfade zu nutzen kann man auch zum Gebrauch bereite, sogenannte "PackedScenes" verwenden. :ref: PackedScene <class_PackedScene> Ressourcen verwenden die entsprechende Funktion SceneTree.change_scene_to(PackedScene scene):

var next_scene = preload("res://levels/level2.tscn")

func _my_level_was_completed():
    get_tree().change_scene_to(next_scene)
public void _MyLevelWasCompleted()
{
    var nextScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn");
    GetTree().ChangeSceneTo(nextScene);
}

Hierbei handelt es sich um die schnellen und einfachen Mittel und Wege zwischen Szenen zu wechseln, haben aber den Nachteil das Spiel zu verzögern bis die neue Szene geladen wurde und spielbereit ist. Ab einem gewissen Punkt ist es wichtig, für Ihr Spiel richtige Ladescreens, Fortschrittsbalken etc. zu implementieren. Thread(Hintergrund-) Laden ist auch möglich. Dies müsste allerdings manuell erledigt werden durch die Nutzung von Autoloads (siehe nächstes Kapitel) und Laden im Hintergrund.