Up to date

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

Einführung in GUI-Skinning

Für ein Spiel ist es wichtig, seinen Spielern eine klare, informative und dennoch optisch ansprechende Benutzeroberfläche zu bieten. Während Control-Nodes von Haus aus mit einem anständig funktionalen Aussehen ausgestattet sind, gibt es immer Raum für Einzigartigkeit und fallspezifisches Tuning. Zu diesem Zweck enthält die Godot-Engine ein System für das GUI-Skinning (oder Theming), mit dem Sie das Aussehen jedes Controls in Ihrer Benutzeroberfläche, einschließlich Ihrer benutzerdefinierten Controls, anpassen können.

Hier ist ein Beispiel für dieses System in Aktion - ein Spiel mit einer grafischen Benutzeroberfläche, die sich radikal vom Standard-UI-Theme der Engine unterscheidet:

../../_images/tank-kings-by-winterpixel-games.png

Ein "Ausrüsten!"-Bildschirm in Tank Kings, mit freundlicher Genehmigung von Winterpixel Games

Mit diesem System können Sie nicht nur ein einzigartiges Aussehen für Ihr Spiel erzielen, sondern auch Anpassungsoptionen für die Endbenutzer bereitstellen, einschließlich Einstellungen für die Barrierefreiheit. UI-Themen werden kaskadierend angewendet (d. h. sie werden von Parent-Controls auf ihre Child-Controls übertragen), was bedeutet, dass Schrifteinstellungen oder Anpassungen für farbenblinde Benutzer an einer einzigen Stelle vorgenommen werden können und den gesamten UI-Baum betreffen. Natürlich kann dieses System auch für Gameplay-Zwecke verwendet werden: Ihr heldenbasiertes Spiel kann seinen Stil für den ausgewählten Spielercharakter ändern, oder Sie können den Seiten in Ihrem teambasierten Projekt verschiedene Geschmacksrichtungen geben.

Theme-Grundlagen

Das Skinning-System wird durch die Ressource Theme gesteuert. Jedes Godot-Projekt hat ein vorgegebenes Default-Theme, das die von den eingebauten Control-Nodes verwendeten Einstellungen enthält. Dadurch erhalten die Controls ihr unverwechselbares Aussehen. Ein Theme beschreibt jedoch nur die Konfiguration, und es ist immer noch die Aufgabe jedes einzelnen Controls, diese Konfiguration so zu verwenden, wie es für seine Darstellung erforderlich ist. Dies ist wichtig zu bedenken, wenn Sie Ihre eigenen benutzerdefinierten Controls implementieren.

Bemerkung

Sogar der Godot-Editor selbst basiert auf dem Default-Theme. Aber er sieht nicht so aus wie ein Godot-Projekt, weil er sein eigenes, stark angepasstes Theme auf das Default-Theme anwendet. Im Prinzip funktioniert das genau so wie in Ihrem Spiel, wie hier erklärt.

Theme-Elemente

Die Konfiguration, die in einem Theme gespeichert wird, besteht aus Theme-Elementen. Jedes Element hat einen eindeutigen Namen und muss einem der folgenden Datentypen entsprechen:

  • Color

    Ein Color-Wert, der oft für Schriftarten und Hintergründe verwendet wird. Farben können auch für die Anpassung von Controls und Icons verwendet werden.

  • Konstante

    Ein Integer-Wert, der entweder für numerische Propertys von Controls (z. B. den Elementabstand in einem BoxContainer) oder für boolesche Flags (z.B. das Zeichnen von Beziehungslinien in einem Tree) verwendet werden kann.

  • Font

    Eine Font-Ressource, die von Controls verwendet wird, die Text anzeigen. Schriftarten enthalten die meisten Einstellungen für die Textdarstellung, mit Ausnahme von Größe und Farbe. Darüber hinaus werden Ausrichtung und Textrichtung von einzelnen Controls gesteuert.

  • -Schriftgröße

    Ein Integer-Wert, der zusammen mit einer Schriftart verwendet wird, um die Größe zu bestimmen, in der ein Text angezeigt werden soll.

  • Icon

    Eine Textur-Ressource, die normalerweise verwendet wird, um ein Icon darzustellen (zum Beispiel auf einem Button).

  • StyleBox

    Eine StyleBox-Ressource, eine Sammlung von Konfigurationsoptionen, die festlegen, wie ein UI-Panel angezeigt werden soll. Dies ist nicht auf das Panel-Contol beschränkt, da Styleboxen von vielen Controls für ihre Hintergründe und Overlays verwendet werden.

Theme-Typen

Um die Organisation der Elemente zu erleichtern, ist jedes Theme in Typen unterteilt, und jedes Element muss zu einem einzigen Typ gehören. Mit anderen Worten: Jedes Theme wird durch seinen Namen, seinen Datentyp und seinen Theme-Typ definiert. Diese Kombination muss innerhalb des Themes eindeutig sein. Zum Beispiel kann es nicht zwei Farbelemente mit dem Namen font_color in einem Typ namens Label geben, aber es kann ein weiteres font_color-Element in einem Typ LineEdit geben.

Das Default-Godot-Theme enthält mehrere bereits definierte Theme-Typen, einen für jeden eingebauten Control-Node, der UI-Skinning verwendet. Das obige Beispiel enthält tatsächliche Theme-Elemente, die im Default-Theme vorhanden sind. Sie können im Abschnitt Theme-Propertys in der Klassenreferenz für jedes Control nachsehen, welche Elemente für dieses und seine Child-Klassen verfügbar sind.

Bemerkung

Child-Klassen können Theme-Elemente verwenden, die für ihre Parent-Klasse definiert sind (Button und seine Derivate sind ein gutes Beispiel dafür). Tatsächlich kann jedes Control bei Bedarf jedes Theme-Element eines beliebigen Theme-Typs verwenden (aber aus Gründen der Übersichtlichkeit und Vorhersehbarkeit versuchen wir, dies in der Engine zu vermeiden).

Es ist wichtig, sich daran zu erinnern, dass dieser Prozess für Child-Klassen automatisiert ist. Wann immer ein Built-in-Control ein Theme-Element aus dem Theme anfordert, kann es den Theme-Typ weglassen, und stattdessen wird sein Klassenname verwendet. Darüber hinaus werden auch die Klassennamen der Parent-Klassen verwendet. Dies ermöglicht es, dass Änderungen an der Parent-Klasse, wie z.B. Button, sich auf alle abgeleiteten Klassen auswirken, ohne daß jede einzelne von ihnen angepasst werden muss.

Sie können auch Ihre eigenen Theme-Typen definieren und zusätzlich sowohl die Built-in-Controls als auch Ihre eigenen Controls anpassen. Da die Built-in-Controls keine Kenntnis von Ihren benutzerdefinierten Theme-Typen haben, müssen Sie Skripte verwenden, um auf diese Elemente zuzugreifen. Alle Control-Nodes verfügen über mehrere Methoden, die es ermöglichen, Theme-Elemente aus dem auf sie angewendeten Theme zu holen. Diese Methoden akzeptieren den Theme-Typ als eines der Argumente.

var accent_color = get_theme_color("accent_color", "MyType")
label.add_color_override("font_color", accent_color)

Um mehr Anpassungsmöglichkeiten zu bieten, können Typen auch als Typvariationen miteinander verknüpft werden. Dies ist ein weiterer Anwendungsfall für benutzerdefinierte Theme-Typen. Zum Beispiel kann ein Theme einen Typ Header enthalten, der als eine Variation des Basistyps Label markiert werden kann. Ein individuelles Label-Control kann dann so eingestellt werden, daß es die Header-Variation für seinen Typ verwendet, und jedesmal, wenn ein Theme-Element von einem Theme angefordert wird, wird diese Variation vor jedem anderen Typ verwendet. Dies erlaubt es, verschiedene Vorgaben von Theme-Elementen für die gleiche Klasse des Control-Nodes in einer einzigen Theme-Ressource zu speichern.

Warnung

Nur Variationen, die im Default-Theme verfügbar oder im benutzerdefinierten Projektthema definiert sind, werden im Inspektor-Dock als Optionen angezeigt. Sie können den Namen einer Variation, die außerhalb dieser beiden Orte definiert ist, immer noch manuell eingeben, aber es wird empfohlen, alle Variationen im Projekt-Theme zu halten.

Sie können mehr über die Erstellung und Verwendung von Theme-Typ-Variationen in einem eigenen Artikel erfahren.

Benutzerdefiniertes Anpassen eines Controls

Jeder Control-Node kann direkt ohne die Verwendung von Themes angepasst werden. Dies wird als lokale Überschreibungen bezeichnet. Jede Theme-Property aus der Klassenreferenz des Controls kann direkt auf dem Control selbst überschrieben werden, entweder über das Inspektor-Dock oder über Skripte. Dies ermöglicht es, granulare Änderungen an einem bestimmten Teil der Benutzeroberfläche vorzunehmen, ohne dass sich dies auf andere Teile des Projekts auswirkt, einschließlich der Child-Nodes dieses Controls.

../../_images/themecheck.png

Lokale Überschreibungen sind weniger nützlich für das visuelle Flair Ihrer Benutzeroberfläche, insbesondere wenn Sie auf Konsistenz achten. Für Layout-Nodes sind sie jedoch unerlässlich. Nodes wie BoxContainer und GridContainer verwenden Theme-Konstanten zur Definition der Trennung zwischen ihren Child-Nodes, und MarginContainer speichert seine anpassbaren Seitenränder in seinen Theme-Elementen.

Wenn ein Control ein lokales Theme-Element überschreibt, wird dieser Wert verwendet. Vom Theme bereitgestellte Werte werden ignoriert.

Benutzerdefiniertes Anpassen eines Projekts

Jedes Projekt nimmt standardmäßig das von Godot bereitgestellte Projekt-Theme an. Das Default-Theme selbst ist fest und kann nicht geändert werden, aber seine Elemente können mit einem benutzerdefinierten Theme überschrieben werden. Benutzerdefinierte Themes können auf zwei Arten angewendet werden: Als Projekteinstellung und als Node-Property im gesamten Baum der Control-Nodes.

Es gibt zwei Projekteinstellungen, die so angepasst werden können, dass sie das gesamte Projekt betreffen: gui/theme/custom ermöglicht es Ihnen, ein benutzerdefiniertes projektweites Theme festzulegen, und gui/theme/custom_font macht dasselbe mit der Default-Fallback-Schriftart. Wird ein Theme-Element von einem Control-Node angefordert, so wird das benutzerdefinierte Projekt-Theme, falls vorhanden, zuerst geprüft. Nur wenn das Element nicht vorhanden ist, wird das Default-Theme geprüft.

Auf diese Weise können Sie die Default-Darstellung jedes Godot-Controls mit einer einzigen Theme-Ressource konfigurieren, aber Sie können detaillierter vorgehen. Jeder Control-Node verfügt außerdem über eine Theme-Property, mit der Sie ein benutzerdefiniertes Theme für den Zweig von Nodes festlegen können, der mit diesem Control beginnt. Dies bedeutet, dass das Control und alle seine Child-Elemente und deren Child-Elemente zuerst diese benutzerdefinierte Theme-Ressource überprüfen, bevor sie auf das Projekt und die Default-Themes zurückgreifen.

Bemerkung

Anstatt die Projekteinstellung zu ändern, können Sie die Ressource für das benutzerdefinierte Theme auf den obersten Control-Node Ihres gesamten UI-Zweigs setzen, um fast denselben Effekt zu erzielen. Während es sich im laufenden Projekt wie erwartet verhält, werden einzelne Szenen immer noch mit dem Default-Theme angezeigt, wenn sie in der Vorschau angezeigt oder direkt ausgeführt werden. Um dies zu beheben, können Sie dieselbe Theme-Ressource auf das Root-Control jeder einzelnen Szene setzen.

Sie können zum Beispiel einen bestimmten Stil für Buttons in Ihrem Projektthema haben, aber ein anderes Aussehen für Buttons innerhalb eines Popup-Dialogs wünschen. Sie können eine benutzerdefinierte Theme-Ressource auf das Root-Control Ihres Popups setzen und einen anderen Stil für die Buttons innerhalb dieser Ressource definieren. Solange die Kette der Control-Nodes zwischen dem Root des Popups und den Buttons nicht unterbrochen ist, verwenden diese Buttons die Stile, die in der Theme-Ressource definiert sind, die ihnen am nächsten ist. Alle anderen Controls werden weiterhin mit dem projektweiten Thema und den Default-Theme-Stilen formatiert.

Zusammenfassend lässt sich sagen, dass für ein beliebiges Control das Aussehen der Theme-Elemente etwa so ablaufen würde:

  1. Prüfung auf lokale Überschreibungen desselben Datentyps und Namens.

  2. Verwenden der Typvariation des Controls, des Klassennamens und der Namen der Parent-Klassen:

    1. Überprüfen jedes Controls, beginnend mit sich selbst, ob es eine Theme-Property hat;

    2. Wenn dies der Fall ist, wird dieses Theme nach dem passenden Element mit demselben Namen, denselben Daten und demselben Theme-Typ überprüft;

    3. Wenn es kein benutzerdefiniertes Theme gibt oder das Element nicht vorhanden ist, wird zum Parent-Control gewechselt;

    4. Wiederholen der Schritte a-c, bis die Wurzel des Baums erreicht ist oder ein Nicht-Control-Node erreicht ist.

  3. Anhand der Typvariation des Controls, des Klassennamens und der Namen der Parent-Klassen wird das projektweite Theme überprüft, sofern es vorhanden ist.

  4. Mit der Typvariation des Controls, des Klassennamen und der Namen der Parent-Klassen wird das Default-Theme überprüft.

Selbst wenn das Element in keinem Theme vorhanden ist, wird ein entsprechender Default-Wert für diesen Datentyp zurückgegeben.

Jenseits von Controls

Natürlich sind Themes eine ideale Art von Ressource, um die Konfiguration für etwas Visuelles zu speichern. Während die Unterstützung für das Theme in Control-Nodes integriert ist, können andere Nodes sie ebenfalls verwenden, genau wie jede andere Ressource.

Ein Beispiel für die Verwendung von Themes für etwas, das über die Steuerung hinausgeht, kann die Veränderung von Sprites für dieselben Einheiten in verschiedenen Teams in einem Strategiespiel sein. Eine Theme-Ressource kann eine Sammlung von Farben definieren, und Sprites können (mit Hilfe von Skripten) diese Farben verwenden, um die Textur zu zeichnen. Der Hauptvorteil besteht darin, dass man verschiedene Themes mit denselben Theme-Elementen für rote, blaue und grüne Teams erstellen und sie mit einer einzigen Ressourcenänderung austauschen kann.