Up to date

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

GDScript-Referenz

GDScript ist eine High-Level, objektorientierte, imperative und graduell typisierte Programmiersprache, die für Godot entwickelt wurde. Sie verwendet eine auf Einrückungen basierende Syntax, ähnlich zu Sprachen wie Python. Ihr Ziel ist es, für die Godot-Engine optimiert und eng in sie integriert zu werden, was eine große Flexibilität bei der Erstellung und Integration von Inhalten ermöglicht.

GDScript ist völlig unabhängig von Python und basiert nicht darauf.

Geschichte

Bemerkung

Ältere Artikel über GDScript wurden in die Häufig gestellten Fragen verschoben.

Beispiel für GDScript

Manche Leute können besser lernen, indem sie sich die Syntax ansehen. Hier ist ein Beispiel dafür, wie GDScript aussieht.

# Everything after "#" is a comment.
# A file is a class!

# (optional) icon to show in the editor dialogs:
@icon("res://path/to/optional/icon.svg")

# (optional) class definition:
class_name MyClass

# Inheritance:
extends BaseClass


# Member variables.
var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var other_dict = {key = "value", other_key = 2}
var typed_var: int
var inferred_type := "String"

# Constants.
const ANSWER = 42
const THE_NAME = "Charly"

# Enums.
enum {UNIT_NEUTRAL, UNIT_ENEMY, UNIT_ALLY}
enum Named {THING_1, THING_2, ANOTHER_THING = -1}

# Built-in vector types.
var v2 = Vector2(1, 2)
var v3 = Vector3(1, 2, 3)


# Functions.
func some_function(param1, param2, param3):
    const local_const = 5

    if param1 < local_const:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    match param3:
        3:
            print("param3 is 3!")
        _:
            print("param3 is not 3!")

    var local_var = param1 + 3
    return local_var


# Functions override functions with the same name on the base/super class.
# If you still want to call them, use "super":
func something(p1, p2):
    super(p1, p2)


# It's also possible to call another function in the super class:
func other_something(p1, p2):
    super.something(p1, p2)


# Inner class
class Something:
    var a = 10


# Constructor
func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

Wenn Sie bereits Erfahrung mit statisch typisierten Sprachen wie C, C++ oder C# haben, aber bisher keine dynamisch typisierte verwendet haben, wird das Lesen dieses Tutorials empfohlen: GDScript: Eine Einführung in dynamische Programmiersprachen.

Sprache

Im Folgenden wird ein Überblick über GDScript gegeben. Details, wie welche Methoden es für Arrays oder andere Objekte gibt, sollten in den verlinkten Klassenbeschreibungen nachgeschlagen werden.

Bezeichner

Jeder String, der beschränkt ist auf Zeichen des Alphabets (a bis z und A bis Z), Ziffern (0 bis 9) und _, ist ein gültiger Bezeichner. Zusätzlich dürfen Bezeichner nicht mit einer Ziffer beginnen. Bei Bezeichnern wird Groß- und Kleinschreibung unterschieden (foo unterscheidet sich von FOO).

Bezeichner können auch die meisten Unicode-Zeichen enthalten, die Teil von UAX#31 sind. Dies ermöglicht die Verwendung von Bezeichnernamen, die in anderen Sprachen als Englisch geschrieben sind. Unicode-Zeichen, die als "verwechselbar" mit ASCII-Zeichen und Emoji gelten, sind in Bezeichnern nicht erlaubt.

Schlüsselwörter

Das Folgende ist eine Auflistung von Schlüsselwörtern, die von der Sprache unterstützt werden. Da Schlüsselwörter reservierte Wörter (Tokens) sind, können sie nicht als Bezeichner verwendet werden. Operatoren (wie in, not, and oder or) und Namen von Built-in-Typen, die in den folgenden Abschnitten aufgelistet werden, sind ebenfalls reserviert.

Schlüsselwörter werden im GDSkript Tokenizer definiert, falls Sie einen Blick unter die Haube werfen möchten.

Schlüsselwort

Beschreibung

if

Siehe if/else/elif.

elif

Siehe if/else/elif.

else

Siehe if/else/elif.

for

Siehe for.

while

Siehe while.

match

Siehe match.

break

Beendet die Ausführung der aktuellen for oder while-Schleife.

continue

Springt sofort zur nächsten Iteration der for oder while-Schleife.

pass

Benutzt man, wo eine Anweisung zwar syntaktisch benötigt wird, aber die Ausführung von Code eigentlich ungewünscht ist, z.B. in leeren Funktionen.

return

Gibt einen Wert aus einer Funktion zurück.

class

Definiert eine innere Klasse. Siehe Innere Klassen.

class_name

Definiert das Skript als eine global zugängliche Klasse mit dem angegebenen Namen. Siehe Registrierung benannter Klassen.

extends

Definiert von welcher Klasse die aktuelle Klasse abgeleitet soll.

is

Testet, ob eine Variable von einer gegeben Klasse abgeleitet ist oder einem gegebenen Built-in-Typen entspricht.

in

Prüft, ob ein Wert innerhalb eines Strings, eines Arrays, einer Range, eines Dictionarys oder eines Nodes liegt. Wenn es mit for verwendet wird, durchläuft es sie, anstatt zu testen.

as

Wandelt den Wert in einen gegebenen Typ um, falls möglich.

self

Bezieht sich auf die aktuelle Klassen-Instanz.

signal

Definiert ein Signal.

func

Definiert eine Funktion.

static

Definiert eine statische Funktion oder eine statische Membervariable.

const

Definiert eine Konstante.

enum

Definiert ein Enum.

var

Definiert eine Variable.

breakpoint

Editor-Helfer für Debugger-Breakpoints. Im Gegensatz zu Breakpoints, die durch Klicken am Seitenrand erzeugt werden, wird breakpoint im Skript selbst gespeichert. Dadurch bleibt er bei Verwendung der Versionsverwaltung auf verschiedenen Rechnern erhalten.

preload

Lädt eine Variable oder Klasse im Voraus, siehe Klassen als Ressourcen.

await

Wartet auf das Ende eines Signals oder einer Coroutine. Siehe Warten auf Signale oder Coroutines.

yield

Wurde früher für Coroutinen verwendet. Wird übergangsweise als Schlüsselwort beibehalten.

assert

Fügt eine Assertion für eine Bedingung hinzu, protokolliert Fehler bei Fehlschlag. Wird in Nicht-Debug-Builds ignoriert. Siehe Assert-Schlüsselwort.

void

Wird verwendet, um darzustellen, dass eine Funktion keinen Wert zurückgibt.

PI

Die PI-Konstante.

TAU

Die TAU-Konstante.

INF

Unendlichkeitskonstante. Wird für Vergleiche und als Ergebnis von Berechnungen verwendet.

NAN

NAN (not a number, keine Zahl)-Konstante. Wird als unmögliches Ergebnis von Berechnungen verwendet.

Operatoren

Es folgt eine Liste der unterstützen Operatoren und ihrer Auswertungsreihenfolge.

Operator

Beschreibung

( )

Gruppierung (höchste Priorität)

Klammern sind eigentlich kein Operator, sondern ermöglichen es Ihnen, den Vorrang einer Operation explizit anzugeben.

x[index]

Element-Zugriff

x.attribute

Referenzierung eines Attributs

foo()

Funktionsaufruf

await x

Warten auf Signale oder Coroutinen

x is Node

Typprüfung

Siehe auch is_instance_of()-Funktion.

x ** y

Potenzierung

Multipliziert x mit sich selbst y mal, ähnlich dem Aufruf der Funktion pow().

Hinweis: In GDScript ist der Operator ** links-assoziativ. Siehe einen ausführlichen Hinweis nach der Tabelle.

~x

Bitweise Negation

+x
-x

identische Abbildung / Negation

x * y
x / y
x % y

Multiplikation / Division / Modulus (Rest)

Der Operator % wird zusätzlich für Format-Strings verwendet.

Hinweis: Diese Operatoren haben das gleiche Verhalten wie in C++, was für Benutzer, die aus Python, JavaScript usw. kommen, unerwartet sein kann. Siehe einen ausführlichen Hinweis nach der Tabelle.

x + y
x - y

Addition (oder Verkettung) / Subtraktion

x << y
x >> y

Bit-Shifting

x & y

Bitweises UND

x ^ y

Bitweises XOR

x | y

Bitweises ODER

x == y
x != y
x < y
x > y
x <= y
x >= y

Vergleich

Siehe eine ausführliche Anmerkung nach der Tabelle.

x in y
x not in y

Prüfung auf Enthaltensein

in wird auch mit dem Schlüsselwort for als Teil der Syntax verwendet.

not x
!x

Boolesches NOT und sein nicht empfohlener-Alias

x and y
x && y

Boolesches UND und sein unempfohlener Alias

x or y
x || y

Boolesches ODER und sein unempfohlener Alias

true_expr if cond else false_expr

Ternäres if/else

x as Node

Typ-Casting

x = y
x += y
x -= y
x *= y
x /= y
x **= y
x %= y
x &= y
x |= y
x ^= y
x <<= y
x >>= y

Zuweisung (niedrigste Priorität)

Sie können einen Zuweisungsoperator nicht innerhalb eines Ausdrucks verwenden.

Bemerkung

Das Verhalten einiger Operatoren kann von dem abweichen, was Sie erwarten:

  1. Wenn beide Operanden des Operators / int sind, dann wird eine Integer-Division anstelle einer mit Kommazahlen durchgeführt. Zum Beispiel 5 / 2 == 2, nicht 2.5. Wenn dies nicht erwünscht ist, verwenden Sie mindestens ein Float-Literal (x / 2.0), Cast (float(x) / y), oder multiplizieren Sie mit 1.0 (x * 1.0 / y).

  2. Der %-Operator ist nur für Ints verfügbar, für Floats verwenden Sie die Funktion fmod().

  3. Für negative Werte verwenden der %-Operator und fmod() das Abrunden, anstatt gegen negativ unendlich zu runden. Dies bedeutet, dass der Rest ein Vorzeichen hat. Wenn Sie den Rest im mathematischen Sinn benötigen, verwenden Sie stattdessen die Funktionen posmod() und fposmod().

  4. Der Operator ** ist links-assoziativ. Das bedeutet, dass 2 ** 2 ** 3 gleich (2 ** 2) ** 3 ist. Benutzen Sie Klammern, um die benötigte Rangfolge explizit anzugeben, zum Beispiel 2 ** (2 ** 3).

  5. Die Operatoren == und != erlauben es manchmal, Werte unterschiedlichen Typs zu vergleichen (z.B. 1 == 1.0 ist wahr), aber in anderen Fällen können sie einen Laufzeitfehler verursachen. Wenn Sie sich über die Typen der Operanden nicht sicher sind, können Sie die Funktion is_same() verwenden (beachten Sie aber, dass sie strenger ist, was Typen und Referenzen angeht). Um Float-Zahlen zu vergleichen, verwenden Sie stattdessen die Funktionen is_equal_approx() und is_zero_approx().

Literale

Beispiel(e)

Beschreibung

null

Null-Wert

false, true

Boolesche Werte

45

Integer zur Basis 10

0x8f51

Integer zur Basis 16 (hexadezimal)

0b101010

Integer zur Basis 2 (binär)

3.14, 58.1e-10

Gleitkomma-Zahl (real)

"Hallo", 'Hi'

Normale Strings

"""Hallo""", '''Hi'''

Normale Strings in drei Anführungszeichen

r"Hallo", r'Hi'

Raw-Strings

r"""Hallo""", r'''Hi'''

Raw-Strings in drei Anführungszeichen

&"name"

StringName

^"Node/Label"

NodePath

Es gibt auch zwei Konstrukte, die wie Literale aussehen, aber eigentlich keine sind:

Beispiel

Beschreibung

$NodePath

Kurzform für get_node("NodePath")

%UniqueNode

Kurzform für get_node("%UniqueNode")

Integer und Floats können durch _ getrennt werden um die Lesbarkeit zu verbessern. Die folgenden Möglichkeiten zum Schreiben von Zahlen sind alle gültig:

12_345_678  # Equal to 12345678.
3.141_592_7  # Equal to 3.1415927.
0x8080_0000_ffff  # Equal to 0x80800000ffff.
0b11_00_11_00  # Equal to 0b11001100.

Normale String-Literale können die folgenden Escape-Sequenzen enthalten:

Escape-Sequenz

wird erweitert zu

\n

Zeilenumbruch (Line Feed)

\t

Horizontaler Tabulator

\r

Wagenrücklauf (Carriage Return)

\a

Alarm (Piepton/Klingel)

\b

Rückschritt

\f

Formfeed-Seitenumbruch

\v

Vertikaler Tabulator

\"

Doppeltes Anführungszeichen

\'

Einfaches Anführungszeichen

\\

Rückwärtsschrägstrich

\uXXXX

UTF-16 Unicode-Codepunkt XXXX (hexadezimal, Groß- und Kleinschreibung wird nicht berücksichtigt)

\UXXXXXX

UTF-32 Unicode-Codepunkt XXXXXX (hexadezimal, Groß- und Kleinschreibung wird nicht berücksichtigt)

Es gibt zwei Möglichkeiten, ein escapetes Unicode-Zeichen über 0xFFFF darzustellen:

Außerdem ermöglicht die Verwendung von \ gefolgt von einem Zeilenumbruch innerhalb eines Strings, diesen in der nächsten Zeile fortzusetzen, ohne ein Zeilenumbruchzeichen in den String selbst einzufügen.

Ein String, der in Anführungszeichen eines Typs (z. B. ") eingeschlossen ist, kann Anführungszeichen eines anderen Typs (z.B. ') enthalten, ohne dass er escaped wird. Bei Strings in dreifachen Anführungszeichen können Sie darauf verzichten, bis zu zwei aufeinanderfolgende Anführungszeichen desselben Typs zu escapen (es sei denn, sie grenzen an die Ränder des Strings).

Raw-String-Literale kodieren den String immer so, wie er im Quellcode erscheint. Dies ist besonders nützlich für reguläre Ausdrücke. Raw-Strings verarbeiten keine Escape-Sequenzen, aber Sie können ein Anführungszeichen oder einen Backslash "escapen" lassen (sie ersetzen sich selbst).

print("\tchar=\"\\t\"")  # Prints `    char="\t"`.
print(r"\tchar=\"\\t\"") # Prints `\tchar=\"\\t\"`.

GDScript unterstützt auch format strings.

Annotationen

Es gibt einige spezielle Tokens in GDScript, die sich wie Schlüsselwörter verhalten, aber keine sind, sondern Annotationen. Jede Annotation beginnt mit dem Zeichen @ und wird durch einen Namen spezifiziert. Eine detaillierte Beschreibung und ein Beispiel für jede Annotation finden Sie in der GDScript Klassenreferenz.

Anmerkungen wirken sich darauf aus, wie das Skript von externen Tools behandelt wird, und ändern normalerweise nicht das Verhalten.

Sie können damit zum Beispiel einen Wert in den Editor exportieren:

@export_range(1, 100, 1, "or_greater")
var ranged_var: int = 50

Weitere Informationen über den Export von Propertys finden Sie im Artikel GDScript-Exporte.

Jeder konstante Ausdruck, der mit dem erforderlichen Argumenttyp kompatibel ist, kann als ein Annotationsargument-Wert übergeben werden:

const MAX_SPEED = 120.0

@export_range(0.0, 0.5 * MAX_SPEED)
var initial_speed: float = 0.25 * MAX_SPEED

Annotationen können einzeln pro Zeile oder alle in derselben Zeile angegeben werden. Sie wirken sich auf die nächste Anweisung aus, die keine Annotation ist. Annotationen können Argumente haben, die in Klammern gesetzt und durch Kommas getrennt werden.

Diese beiden sind das Gleiche:

@annotation_a
@annotation_b
var variable

@annotation_a @annotation_b var variable

@onready-Annotation

Bei der Verwendung von Nodes ist es üblich, Verweise auf Teile der Szene in einer Variablen zu behalten. Da Szenen nur beim Aufrufen des aktiven Szenenbaums konfiguriert werden müssen, können die Unter-Nodes nur abgerufen werden, wenn ein Aufruf von Node._ready() erfolgt.

var my_label


func _ready():
    my_label = get_node("MyLabel")

Dies kann ein wenig mühsam werden, besonders wenn sich Nodes und externe Referenzen häufen. Dafür gibt es in GDScript die @onready-Annotation, um die Initialisierung einer Mitgliedsvariable aufzuschieben, bis _ready() aufgerufen wird. Sie kann den obigen Code durch eine einzige Zeile ersetzen:

@onready var my_label = get_node("MyLabel")

Warnung

Die Anwendung von @onready und einer @export-Annotation auf dieselbe Variable funktioniert nicht so, wie man es erwarten könnte. Die @onready-Annotation bewirkt, dass der Default-Wert gesetzt wird, nachdem die @export-Annotation in Kraft getreten ist, und überschreibt ihn:

@export var a = "init_value_a"
@onready @export var b = "init_value_b"

func _init():
    prints(a, b) # init_value_a <null>

func _notification(what):
    if what == NOTIFICATION_SCENE_INSTANTIATED:
        prints(a, b) # exported_value_a exported_value_b

func _ready():
    prints(a, b) # exported_value_a init_value_b

Daher wird die Warnung ONREADY_WITH_EXPORT erzeugt, die standardmäßig als Fehler behandelt wird. Wir empfehlen nicht, sie zu deaktivieren oder zu ignorieren.

Kommentare

Alles ab einem # bis zum Ende der Zeile wird ignoriert und als Kommentar aufgefasst.

# This is a comment.

Tipp

Im Godot-Skript-Editor werden spezielle Schlüsselwörter in Kommentaren hervorgehoben, um die Aufmerksamkeit des Benutzers auf bestimmte Kommentare zu lenken:

  • Kritisch (erscheint in rot): ALERT, ATTENTION, CAUTION, CRITICAL, DANGER, SECURITY

  • Warnung (erscheint in gelb): BUG, DEPRECATED, FIXME, HACK, TASK, TBD, TODO, WARNING

  • Hinweis (erscheint in grün): INFO, NOTE, NOTICE, TEST, TESTING

Bei diesen Schlüsselwörtern wird zwischen Groß- und Kleinschreibung unterschieden, sie müssen also in Großbuchstaben geschrieben werden, damit sie erkannt werden:

# In the example below, "TODO" will appear in yellow by default.
# The `:` symbol after the keyword is not required, but it's often used.

# TODO: Add more items for the player to choose from.

Die Liste der hervorgehobenen Schlüsselwörter und ihre Farben können im Abschnitt Texteditor > Theme > Kommentarmarkierungen der Editoreinstellungen geändert werden.

Code-Regionen

Code-Regionen sind spezielle Arten von Kommentaren, die der Skript-Editor als ausklappbare Bereiche versteht. Das bedeutet, dass Sie nach dem Schreiben von Code-Region-Kommentaren die Region ein- und ausklappen können, indem Sie auf den Pfeil klicken, der links neben dem Kommentar erscheint. Dieser Pfeil erscheint in einem lilafarbenen Quadrat, damit er von der normalen Code-Ausklappung unterschieden werden kann.

Die Syntax lautet wie folgt:

# Important: There must be *no* space between the `#` and `region` or `endregion`.

# Region without a description:
#region
...
#endregion

# Region with a description:
#region Some description that is displayed even when collapsed
...
#endregion

Tipp

Um eine Code-Region schnell zu erstellen, markieren Sie mehrere Zeilen im Skript-Editor, klicken Sie mit der rechten Maustaste auf die Auswahl und wählen Sie Code-Region erstellen. Die Beschreibung des Bereichs wird automatisch zur Bearbeitung ausgewählt.

Es ist möglich, Code-Regionen innerhalb anderer Code-Regionen zu verschachteln.

Hier ist ein konkretes Beispiel für die Verwendung von Code-Regionen:

# This comment is outside the code region. It will be visible when collapsed.
#region Terrain generation
# This comment is inside the code region. It won't be visible when collapsed.
func generate_lakes():
    pass

func generate_hills():
    pass
#endregion

#region Terrain population
func place_vegetation():
    pass

func place_roads():
    pass
#endregion

Dies kann nützlich sein, um große Codeabschnitte in leichter zu verstehende Abschnitte zu gliedern. Denken Sie jedoch daran, dass externe Editoren diese Funktion in der Regel nicht unterstützen. Stellen Sie also sicher, dass Ihr Code auch dann leicht verständlich ist, wenn Sie sich nicht auf das Einklappen von Codebereichen verlassen.

Bemerkung

Einzelne Funktionen und eingerückte Abschnitte (wie if und for) können im Skripteditor immer eingeklappt werden. Das bedeutet, dass Sie es vermeiden sollten, eine Code-Region zu verwenden, um eine einzelne Funktion oder einen eingerückten Abschnitt zu enthalten, da dies keinen großen Nutzen bringt. Code-Regionen funktionieren am besten, wenn sie dazu dienen, mehrere Elemente zusammenzufassen.

Fortsetzung der Zeile

Eine Codezeile in GDScript kann in der nächsten Zeile fortgesetzt werden, indem ein Backslash (\) verwendet wird. Fügen Sie einen solchen Schrägstrich am Ende einer Zeile ein, und der Code in der nächsten Zeile verhält sich so, als befände er sich an der Stelle des Backslashs. Hier ist ein Beispiel:

var a = 1 + \
2

Eine Zeile kann auf diese Weise mehrfach fortgesetzt werden:

var a = 1 + \
4 + \
10 + \
4

Built-in-Typen

Built-in-Typen werden auf dem Stack abgelegt. Sie werden als Werte übergeben. Das bedeutet, dass bei jeder Zuweisung oder bei der Übergabe als Argument an Funktionen eine Kopie erstellt wird. Die Ausnahmen sind Object, Array, Dictionary und gepackte Arrays (wie PackedByteArray), die per Referenz übergeben werden, so dass sie gemeinsam genutzt werden können. Alle Arrays, Dictionary, und einige Objekte (Node, Resource) haben eine duplicate() Methode, die es erlaubt, eine Kopie zu erstellen.

Einfache Bult-in-Typen

Eine Variable in GDScript kann mehreren Built-in-Typen zugewiesen werden.

null

null ist ein leerer Datentyp, der keine Information enthält und dem kein anderer Wert zugewiesen werden kann.

bool

Kurzform für "Boolean", der nur true oder false enthalten kann.

int

Kurz für "Integer": es speichert ganze Zahlen (positiv und negativ). Sie werden als 64-Bit-Wert gespeichert werden, gleichbedeutend mit int64_t in C++.

float

Speichert reelle Zahlen, einschließlich Dezimalzahlen, unter Verwendung von Gleitkommazahlen. Er wird als 64-Bit-Wert gespeichert, was double in C++ entspricht. Anmerkung: Derzeit speichern Datenstrukturen wie Vector2, Vector3 und PackedFloat32Array 32-Bit Single-Precision Float-Werte.

String

Eine Folge von Zeichen im Unicode-Format.

StringName

Ein unveränderlicher String, der für jeden Namen nur eine Instanz zulässt. Sie sind langsamer zu erstellen und können beim Multithreading zum Warten auf Locks führen. Im Gegenzug sind sie sehr schnell zu vergleichen, was sie zu guten Kandidaten für Dictionary-Keys macht.

NodePath

Ein vor-geparster Pfad zu einem Node oder einer Node-Property. Er kann einfach einem String zugewiesen und von ihm abgeleitet werden. Sie sind nützlich, um mit dem Baum zu interagieren, um einen Node zu erhalten, oder um Propertys zu beeinflussen, wie mit Tweens.

Vektor-Built-in-Typen

Vector2

2D Vektor-Typ bestehend aus x und y. Zugriff kann auch als Array erfolgen.

Vector2i

Dasselbe wie Vector2, aber die Komponenten sind Integer. Nützlich für die Darstellung von Elementen in einem 2D-Raster.

Rect2

2D Rechteckstypen mit zwei Vektor Feldern: position und size. Besitzt alternativ ein end Feld, welches position+size entspricht.

Vector3

3D-Vektor-Typ besitzt x, y und z-Felder. Diese können auch als Array aufgerufen werden.

Vector3i

Dasselbe wie Vector3, aber die Komponenten sind Integer-Zahlen. Kann für die Indizierung von Elementen in einem 3D-Raster verwendet werden.

Transform2D

Eine 3x2 Matrix, welche für 2D Transformationen verwendet wird.

Ebene

3D Ebenentyp in normalisierter Form, welcher ein normal-Vektorfeld und eine skalare Distanz d besitzt.

Quaternion

Quaternion ist ein Datentyp, welcher für die Repräsentation von 3D-Rotationen verwendet wird. Er ist sehr nützlich für das Interpolieren von Rotationen.

AABB

Eine achsparallele Bounding Box (oder 3D-Box) enthält 2 Vektorfelder: position und size. Enthält auch ein end-Feld, das position + size entspricht.

Basis

3x3 Matrix, welche für 3D Rotationen und Sklarierung verwendet wird. Es besitzt drei Vektorfelder (x, y und z) und kann auch als Array von 3D-Vektoren aufgerufen werden.

Transform3D

3D-Transform besitzt ein Basis-Feld basis und ein Vector3-Feld origin.

Engine-Built-in-Typen

Color

Der Color Datentyp besitzt jeweils ein r, g, b und a Feld. Der Zugriff kann auch über h, s und v erfolgen, was für hue/saturation/value (Farbton, Sättigung, Wert) steht.

RID

Ressource-ID (RID). Server nutzen generische RIDs, um undurchsichtige Daten zu referenzieren.

Object

Basisklasse für alles, was kein Built-in-Typ ist.

Container-Built-in-Typen

Array

Generische Abfolge beliebiger Objekttypen, einschließlich anderer Arrays oder Dictionarys (siehe unten). Die Größe des Arrays kann dynamisch geändert werden. Arrays werden ausgehend vom Index 0 indiziert. Negative Indizes beginnen vom Ende mit dem Zählen.

var arr = []
arr = [1, 2, 3]
var b = arr[1] # This is 2.
var c = arr[arr.size() - 1] # This is 3.
var d = arr[-1] # Same as the previous line, but shorter.
arr[0] = "Hi!" # Replacing value 1 with "Hi!".
arr.append(4) # Array is now ["Hi!", 2, 3, 4].

Typisierte Arrays

Mit Godot 4.0 wurde die Unterstützung für typisierte Arrays hinzugefügt. Bei Schreiboperationen prüft Godot, ob die Elementwerte dem angegebenen Typ entsprechen, so daß das Array keine ungültigen Werte enthalten kann. Der statische Analyzer von GDScript berücksichtigt typisierte Arrays, aber Array-Methoden wie front() und back() haben immer noch den Rückgabetyp Variant.

Typisierte Arrays haben die Syntax Array[Typ], wobei Typ ein beliebiger Variant-Typ, eine native oder Benutzerklasse oder eine Enum sein kann. Verschachtelte Array-Typen (wie Array[Array[int]]) werden nicht unterstützt.

var a: Array[int]
var b: Array[Node]
var c: Array[MyClass]
var d: Array[MyEnum]
var e: Array[Variant]

Array und Array[Variant] sind dasselbe.

Bemerkung

Arrays werden per Referenz übergeben, so dass der Typ des Array-Elements auch ein Attribut der speicherinternen Struktur ist, auf die eine Variable zur Laufzeit verweist. Der statische Typ einer Variablen schränkt die Strukturen ein, auf die sie verweisen kann. Daher können Sie einem Array keinen anderen Elementtyp zuweisen, selbst wenn der Typ ein Subtyp des erforderlichen Typs ist.

Wenn Sie ein typisiertes Array konvertieren wollen, können Sie ein neues Array erstellen und die Methode Array.assign() verwenden:

var a: Array[Node2D] = [Node2D.new()]

# (OK) You can add the value to the array because `Node2D` extends `Node`.
var b: Array[Node] = [a[0]]

# (Error) You cannot assign an `Array[Node2D]` to an `Array[Node]` variable.
b = a

# (OK) But you can use the `assign()` method instead. Unlike the `=` operator,
# the `assign()` method copies the contents of the array, not the reference.
b.assign(a)

Die einzige Ausnahme wurde für den Typ Array (Array[Variant]) gemacht, aus Gründen der Benutzerfreundlichkeit und der Kompatibilität mit altem Code. Allerdings werden Operationen auf nicht typisierte Arrays als unsicher angesehen.

Gepackte Arrays

GDScript-Arrays werden aus Geschwindigkeitsgründen linear im Speicher alloziert. Große Arrays (mehr als Zehntausende von Elementen) können jedoch eine Speicherfragmentierung verursachen. Wenn dies ein Problem darstellt, stehen spezielle Arten von Arrays zur Verfügung. Diese akzeptieren nur einen einzigen Datentyp. Sie vermeiden Speicherfragmentierung und verbrauchen weniger Speicher, sind jedoch atomar und laufen tendenziell langsamer als generische Arrays. Sie werden daher nur für große Datenmengen empfohlen:

Dictionary

Assoziativer Container, der Values enthält, auf die durch eindeutige Keys verwiesen wird.

var d = {4: 5, "A key": "A value", 28: [1, 2, 3]}
d["Hi!"] = 0
d = {
    22: "value",
    "some_key": 2,
    "other_key": [2, 3, 4],
    "more_key": "Hello"
}

Die Tabellensyntax im Lua-Stil wird ebenfalls unterstützt. Der Lua-Stil verwendet = anstelle von : und verwendet keine Anführungszeichen, um String-Keys zu markieren (was etwas weniger Schreibaufwand bedeutet). Allerdings können die Keys in dieser Form nicht mit einer Ziffer beginnen (wie jeder GDScript-Bezeichner) und müssen String-Literale sein.

var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

Um einen Key zu einem bestehenden Dictionary hinzuzufügen, greifen Sie wie bei einem vorhandenen Key darauf zu und weisen Sie ihm etwas zu:

var d = {} # Create an empty Dictionary.
d.waiting = 14 # Add String "waiting" as a key and assign the value 14 to it.
d[4] = "hello" # Add integer 4 as a key and assign the String "hello" as its value.
d["Godot"] = 3.01 # Add String "Godot" as a key and assign the value 3.01 to it.

var test = 4
# Prints "hello" by indexing the dictionary with a dynamic key.
# This is not the same as `d.test`. The bracket syntax equivalent to
# `d.test` is `d["test"]`.
print(d[test])

Bemerkung

Die Klammer-Syntax kann verwendet werden, um auf die Propertys jedes Objects zuzugreifen, nicht nur bei Dictionaries. Beachten Sie, dass beim Indizieren einer nicht vorhandenen Property ein Skriptfehler auftritt. Um dies zu vermeiden, verwenden Sie stattdessen die Methoden Object.get() und Object.set().

Signal

Ein Signal ist eine Nachricht, die von einem Objekt an diejenigen gesendet werden kann, die sie hören wollen. Der Typ Signal kann für die Weitergabe des Emitters verwendet werden.

Signale lassen sich besser verwenden, wenn man sie von aktuellen Objekten bezieht, z.B. $Button.button_up.

Callable

Enthält ein Objekt und eine Funktion, was für die Übergabe von Funktionen als Werte nützlich ist (z. B. beim Anbinden an Signale).

Zugriff auf eine Methode als Member gibt eine Callable zurück. var x = $Sprite2D.rotate setzt den Wert von x auf eine Callable mit $Sprite2D als Objekt und rotate als Methode.

Sie können es mit der Methode call aufrufen: x.call(PI).

Daten

Variablen

Variablen können als Klassen-Member oder lokal in Funktionen existieren. Sie werden mit dem Schlüsselwort var erzeugt und können optional mit einem Wert initialisiert werden.

var a # Data type is 'null' by default.
var b = 5
var c = 3.8
var d = b + c # Variables are always initialized in direct order (see below).

Variablen können optional eine Typspezifikation haben. Wenn ein Typ angegeben wird, muss die Variable immer denselben Typ haben, und der Versuch, einen inkompatiblen Wert zuzuweisen, führt zu einem Fehler.

Typen werden in der Variablendeklaration mit einem : (Doppelpunkt) nach dem Variablennamen, gefolgt vom Typ, angegeben.

var my_vector2: Vector2
var my_node: Node = Sprite2D.new()

Wenn die Variable innerhalb der Deklaration initialisiert wird, kann der Typ abgeleitet werden, sodass der Typname weggelassen werden kann:

var my_vector2 := Vector2() # 'my_vector2' is of type 'Vector2'.
var my_node := Sprite2D.new() # 'my_node' is of type 'Sprite2D'.

Typ-Inferenz ist nur möglich, wenn der zugewiesene Wert einen definierten Typ hat, andernfalls wird ein Fehler ausgelöst.

Gültige Typen sind:

  • Built-in-Typen (Array, Vector2, int, String, etc.).

  • Engine-Klassen (Node, Resource, Reference, etc.).

  • Konstanten-Namen, wenn sie eine Skript-Ressource enthalten (MyScript, wenn man const MyScript = preload("res://my_script.gd") deklariert hat).

  • Andere Klassen im gleichen Skript, unter Berücksichtigung des Geltungsbereichs (InnerClass.NestedClass, wenn man class NestedClass innerhalb der class InnerClass im gleichen Geltungsbereich deklariert hat).

  • Skriptklassen, die mit dem Schlüsselwort class_name deklariert wurden.

  • Autoloads, die als Singletons registriert sind.

Bemerkung

Obwohl Variant eine gültige Typspezifikation ist, handelt es sich nicht um einen tatsächlichen Typ. Es bedeutet nur, dass es keinen festen Typ gibt und ist gleichbedeutend damit, dass sie gar keinen statischen Typ hat. Daher ist die Inferenz für Variant standardmäßig nicht erlaubt, da es sich wahrscheinlich um einen Fehler handelt.

Sie können diese Prüfung abschalten oder nur eine Warnung ausgeben, indem Sie sie in den Projekteinstellungen ändern. Siehe GDScript Warnungs-System für Details.

Initialization order

Member variables are initialized in the following order:

  1. Depending on the variable's static type, the variable is either null (untyped variables and objects) or has a default value of the type (0 for int, false for bool, etc.).

  2. The specified values are assigned in the order of the variables in the script, from top to bottom. - (Only for ``Node``-derived classes) If the @onready annotation is applied to a variable, its initialization is deferred to step 5.

  3. If defined, the _init() method is called.

  4. When instantiating scenes and resources, the exported values are assigned.

  5. (Only for ``Node``-derived classes) @onready variables are initialized.

  6. (Only for ``Node``-derived classes) If defined, the _ready() method is called.

Warnung

You can specify a complex expression as a variable initializer, including function calls. Make sure the variables are initialized in the correct order, otherwise your values may be overwritten. For example:

var a: int = proxy("a", 1)
var b: int = proxy("b", 2)
var _data: Dictionary = {}

func proxy(key: String, value: int):
    _data[key] = value
    print(_data)
    return value

func _init() -> void:
    print(_data)

Will print:

{ "a": 1 }
{ "a": 1, "b": 2 }
{  }

To fix this, move the _data variable definition above the a definition or remove the empty dictionary assignment (= {}).

Statische Variablen

Eine Klassen-Membervariable kann als statisch deklariert werden:

static var a

Statische Variablen gehören zur Klasse, nicht zu den Instanzen. Das bedeutet, dass statische Variablen im Gegensatz zu regulären Member-Variablen Werte zwischen mehreren Instanzen teilen.

Innerhalb einer Klasse können Sie von jeder Funktion aus auf statische Variablen zugreifen, sowohl auf statische als auch auf nicht-statische. Außerhalb der Klasse können Sie auf statische Variablen über die Klasse oder eine Instanz zugreifen (letzteres wird nicht empfohlen, da es weniger lesbar ist).

Bemerkung

Die Annotationen @export und @onready können nicht auf eine statische Variable angewendet werden. Lokale Variablen können nicht statisch sein.

Das folgende Beispiel definiert eine Klasse Person mit einer statischen Variable namens max_id. Wir inkrementieren die max_id in der _init() Funktion. Das macht es einfach, die Anzahl der Person-Instanzen in unserem Spiel zu tracken.

# person.gd
class_name Person

static var max_id = 0

var id
var name

func _init(p_name):
    max_id += 1
    id = max_id
    name = p_name

In diesem Code erzeugen wir zwei Instanzen unserer Klasse Person und überprüfen, dass die Klasse und jede Instanz den gleichen Wert für max_id haben, da die Variable statisch und für jede Instanz zugänglich ist.

# test.gd
extends Node

func _ready():
    var person1 = Person.new("John Doe")
    var person2 = Person.new("Jane Doe")

    print(person1.id) # 1
    print(person2.id) # 2

    print(Person.max_id)  # 2
    print(person1.max_id) # 2
    print(person2.max_id) # 2

Statische Variablen können Type-Hints, Setter und Getter haben:

static var balance: int = 0

static var debt: int:
    get:
        return -balance
    set(value):
        balance = -value

Auf eine statische Variable einer Basisklasse kann auch über eine Child-Klasse zugegriffen werden:

class A:
    static var x = 1

class B extends A:
    pass

func _ready():
    prints(A.x, B.x) # 1 1
    A.x = 2
    prints(A.x, B.x) # 2 2
    B.x = 3
    prints(A.x, B.x) # 3 3

@static_unload-Annotation

Da es sich bei GDScript-Klassen um Ressourcen handelt, verhindern statische Variablen in einem Skript, dass es entladen wird, auch wenn es keine Instanzen dieser Klasse und keine anderen Referenzen mehr gibt. Dies kann wichtig sein, wenn statische Variablen große Datenmengen speichern oder Referenzen auf andere Projektressourcen, wie z.B. Szenen, enthalten. Sie sollten diese Daten manuell bereinigen oder die Annotation @static_unload verwenden, wenn die statischen Variablen keine wichtigen Daten speichern und zurückgesetzt werden können.

Warnung

Derzeit werden Skripte aufgrund eines Bugs nie freigegeben, selbst wenn die Annotation @static_unload verwendet wird.

Beachten Sie, dass @static_unload für das gesamte Skript gilt (einschließlich der inneren Klassen) und am Anfang des Skripts stehen muss, vor class_name und extends:

@static_unload
class_name MyNode
extends Node

Siehe auch Statische Funktionen und Statischer Konstruktor.

Casting

Werte, die typisierten Variablen zugewiesen werden, müssen einen kompatiblen Typ haben. Wenn es erforderlich ist, einen Wert zu zwingen, einen bestimmten Typ zu haben, insbesondere bei Objekttypen, können Sie den Casting-Operator as verwenden.

Das Casten zwischen Objekttypen führt zu demselben Objekt, wenn der Wert vom selben Typ oder einem Untertyp des Cast-Typs ist.

var my_node2D: Node2D
my_node2D = $Sprite2D as Node2D # Works since Sprite2D is a subtype of Node2D.

Wenn der Wert kein Subtyp ist, führt die Casting-Operation zu einem null -Wert.

var my_node2D: Node2D
my_node2D = $Button as Node2D # Results in 'null' since a Button is not a subtype of Node2D.

Bei Built-in-Typen werden sie nach Möglichkeit zwangsweise konvertiert, da sonst die Engine einen Fehler auslöst.

var my_int: int
my_int = "123" as int # The string can be converted to int.
my_int = Vector2() as int # A Vector2 can't be converted to int, this will cause an error.

Casting ist auch nützlich, um bei der Interaktion mit dem Szenenbaum bessere typsichere Variablen zu haben:

# Will infer the variable to be of type Sprite2D.
var my_sprite := $Character as Sprite2D

# Will fail if $AnimPlayer is not an AnimationPlayer, even if it has the method 'play()'.
($AnimPlayer as AnimationPlayer).play("walk")

Konstanten

Konstanten sind Werte, die Sie nicht ändern können während das Spiel läuft, ihr Wert muss zur Kompilierungszeit bekannt sein. Mit dem Schlüsselwort const können Sie einem konstanten Wert einen Namen geben. Wenn Sie versuchen, einer Konstanten nach ihrer Deklaration einen Wert zuzuweisen, wird ein Fehler angezeigt.

Wir empfehlen die Verwendung von Konstanten, wenn sich ein Wert nicht ändern soll.

const A = 5
const B = Vector2(20, 20)
const C = 10 + 20 # Constant expression.
const D = Vector2(20, 30).x # Constant expression: 20.
const E = [1, 2, 3, 4][0] # Constant expression: 1.
const F = sin(20) # 'sin()' can be used in constant expressions.
const G = x + 20 # Invalid; this is not a constant expression!
const H = A + 20 # Constant expression: 25 (`A` is a constant).

Obwohl der Typ der Konstanten aus dem zugewiesenen Wert abgeleitet wird, ist es auch möglich, eine explizite Typspezifikation hinzuzufügen:

const A: int = 5
const B: Vector2 = Vector2()

Das Zuweisen eines Werts eines inkompatiblen Typs führt zu einem Fehler.

Sie können auch Konstanten innerhalb einer Funktion erstellen, was nützlich ist, um lokale "Magic Numbers"mit Namen zu versehen.

Bemerkung

Da Objekte, Arrays und Dictionarys per Referenz übergeben werden, sind Konstanten "flach". Das heißt, wenn Sie konstantes Array oder Dictionary deklarieren, kann dieses auch noch nachträglich geändert werden. Man kann ihnen jedoch keinen anderen Wert mehr zuweisen.

Enums

Enums sind im Grunde ein Kürzel für Konstanten und sind nützlich, wenn Sie aufeinanderfolgende Integer zu einer Kontanten zuweisen möchten.

enum {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

# Is the same as:
const TILE_BRICK = 0
const TILE_FLOOR = 1
const TILE_SPIKE = 2
const TILE_TELEPORT = 3

Wenn Sie dem Enum einen Namen übergeben, wird es alle Keys in ein konstantes Dictionary mit diesem Namen einfügen. Das bedeutet, dass alle konstanten Methoden eines Dictionarys auch mit einem benannten Enum verwendet werden können.

Wichtig

Keys in einem benannten Enum werden nicht als globale Konstanten registriert. Auf sie sollte mit dem Präfix des Enum-Namens zugegriffen werden (Name.KEY).

enum State {STATE_IDLE, STATE_JUMP = 5, STATE_SHOOT}

# Is the same as:
const State = {STATE_IDLE = 0, STATE_JUMP = 5, STATE_SHOOT = 6}
# Access values with State.STATE_IDLE, etc.

func _ready():
    # Access values with Name.KEY, prints '5'
    print(State.STATE_JUMP)
    # Use dictionary methods:
    # prints '["STATE_IDLE", "STATE_JUMP", "STATE_SHOOT"]'
    print(State.keys())
    # prints '{ "STATE_IDLE": 0, "STATE_JUMP": 5, "STATE_SHOOT": 6 }'
    print(State)
    # prints '[0, 5, 6]'
    print(State.values())

Funktionen

Funktionen gehören immer zu einer Klasse. Die Priorität des Geltungsbereichs für die Suche nach Variablen ist: lokal → Klassenmember → global. Die Variable self ist immer verfügbar und wird als Option für den Zugriff auf Klassenmember angeboten, ist aber nicht immer erforderlich (und sollte im Gegensatz zu Python nicht als erstes Argument der Funktion gesendet werden).

func my_function(a, b):
    print(a)
    print(b)
    return a + b  # Return is optional; without it 'null' is returned.

Eine Funktion kann jederzeit per return zurückkehren. Der Default-Rückgabewert ist null.

Wenn eine Funktion nur eine Zeile Code enthält, kann sie in eine Zeile geschrieben werden:

func square(a): return a * a

func hello_world(): print("Hello World")

func empty_function(): pass

Funktionen können auch eine Typspezifikation für die Argumente und für den Rückgabewert haben. Typen für Argumente können auf ähnliche Weise wie Variablen hinzugefügt werden:

func my_function(a: int, b: String):
    pass

Wenn ein Funktionsargument einen Defaultwert hat, kann auf den Typ geschlossen werden:

func my_function(int_arg := 42, String_arg := "string"):
    pass

Der Rückgabetyp der Funktion kann nach der Argumentliste mit dem Pfeiltoken (->) angegeben werden:

func my_int_function() -> int:
    return 0

Funktionen mit einem Rückgabetyp müssen einen richtigen Wert zurückgeben. Wenn Sie den Typ auf void setzen, gibt die Funktion nichts zurück. Void-Funktionen können mit dem Schlüsselwort return frühzeitig beendet werden, sie können jedoch keinen Wert zurückgeben.

func void_function() -> void:
    return # Can't return a value.

Bemerkung

Nicht-void-Funktionen müssen immer einen Wert zurückgeben. Wenn Ihr Code also Verzweigungsanweisungen enthält (z.B. ein if/else-Konstrukt), müssen alle möglichen Pfade eine Rückgabe haben. Wenn Sie beispielsweise ein return in einem if-Block haben, aber nicht danach, gibt der Editor einen Fehler aus, da die Funktion keinen gültigen Wert für die Rückgabe hat, wenn der Block nicht ausgeführt wird.

Funktionen referenzieren

Funktionen sind Elemente erster Klasse im Sinne des Callable-Objekts. Wenn man eine Funktion mit ihrem Namen referenziert, ohne sie aufzurufen, wird automatisch das richtige Callable erzeugt. Dies kann verwendet werden, um Funktionen als Argumente zu übergeben.

func map(arr: Array, function: Callable) -> Array:
    var result = []
    for item in arr:
        result.push_back(function.call(item))
    return result

func add1(value: int) -> int:
    return value + 1;

func _ready() -> void:
    var my_array = [1, 2, 3]
    var plus_one = map(my_array, add1)
    print(plus_one) # Prints [2, 3, 4].

Bemerkung

Callables müssen mit der Methode call aufgerufen werden. Sie können den () Operator nicht direkt verwenden. Dieses Verhalten ist implementiert, um Performanceprobleme bei direkten Funktionsaufrufen zu vermeiden.

Lambda-Funktionen

Mit Lambda-Funktionen können Sie Funktionen deklarieren, die nicht zu einer Klasse gehören. Stattdessen wird ein Callable-Objekt erstellt und direkt einer Variablen zugewiesen. Dies kann nützlich sein, um Callables zu erstellen und weiterzugeben, ohne den Geltungsbereich der Klasse zu verschmutzen.

var lambda = func(x): print(x)
lambda.call(42) # Prints "42"

Lambda-Funktionen können zu Debugging-Zwecken benannt werden:

var lambda = func my_lambda(x):
    print(x)

Note that if you want to return a value from a lambda, an explicit return is required (you can't omit return):

var lambda = func(x): return x ** 2
print(lambda.call(2)) # Prints `4`.

Lambda-Funktionen erfassen die lokale Umgebung. Lokale Variablen werden als Wert übergeben, so dass sie im Lambda nicht aktualisiert werden, wenn sie in der lokalen Funktion geändert werden:

var x = 42
var my_lambda = func(): print(x)
my_lambda.call() # Prints "42"
x = "Hello"
my_lambda.call() # Prints "42"

Bemerkung

Die Werte des äußeren Geltungsbereichs verhalten sich wie Konstanten. Wenn Sie also ein Array oder ein Dictionary deklarieren, können Sie es anschließend noch ändern.

Statische Funktionen

Eine Funktion kann als statisch deklariert werden. Wenn eine Funktion statisch ist, hat sie keinen Zugriff auf die Member-Variablen der Instanz oder self. Eine statische Funktion hat Zugriff auf statische Variablen. Auch statische Funktionen sind nützlich, um Bibliotheken von Hilfsfunktionen zu erstellen:

static func sum2(a, b):
    return a + b

Lambdas können nicht als statisch deklariert werden.

Siehe auch Statische Variablen und Statischer Konstruktor.

Anweisungen und Kontrollfluss

Ausdrücke sind Standard und können Zuweisungen sein, Funktionsaufrufe, Kontrollfluss-Strukturen, etc. (siehe unten). ; als Trennzeichen zwischen Ausdrücken ist optional.

Ausdrücke

Ausdrücke sind Folgen von Operatoren und ihren Operanden in geordneter Weise. Ein Ausdruck selbst kann auch eine Anweisung sein, allerdings sind nur Aufrufe als Anweisungen sinnvoll, da andere Ausdrücke keine Nebeneffekte haben.

Ausdrücke geben Werte zurück, die gültigen Zielen zugewiesen werden können. Operanden für einen Operator können ein anderer Ausdruck sein. Eine Zuweisung ist kein Ausdruck und gibt daher auch keinen Wert zurück.

Hier sind einige Beispiele für Ausdrücke:

2 + 2 # Binary operation.
-5 # Unary operation.
"okay" if x > 4 else "not okay" # Ternary operation.
x # Identifier representing variable or constant.
x.a # Attribute access.
x[4] # Subscript access.
x > 2 or x < 5 # Comparisons and logic operators.
x == y + 2 # Equality test.
do_something() # Function call.
[1, 2, 3] # Array definition.
{A = 1, B = 2} # Dictionary definition.
preload("res://icon.png") # Preload builtin function.
self # Reference to current instance.

Bezeichner, Attribute und Element-Zugriffe sind gültige Zuweisungsziele. Andere Ausdrücke können nicht auf der linken Seite einer Zuweisung stehen.

if/else/elif

Einfache Bedingungen werden mit der if/else/elif Syntax erstellt. Klammern um die Bedingungen zu setzten ist möglich, aber nicht zwingend notwendig. Angesichts der Tabulator-basierten Einrückung kann elif anstelle von else/if verwendet werden, um auf dem selben Level der Einrückung zu bleiben.

if (expression):
    statement(s)
elif (expression):
    statement(s)
else:
    statement(s)

Kurze Anweisungen können in dieselbe Zeile wie die Bedingung geschrieben werden:

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

Manchmal möchte man vielleicht einen unterschiedlichen initialen Wert basierend auf einem booleschen Ausdruck zuweisen. Hierfür ist der ternäre If-Ausdruck nützlich:

var x = (value) if (expression) else (value)
y += 3 if y < 10 else -1

Ternäre-if-Ausdrücke können verschachtelt werden, um mehr als 2 Fälle zu behandeln. Bei der Verschachtelung von ternären if-Ausdrücken wird empfohlen, den gesamten Ausdruck über mehrere Zeilen zu verteilen, um die Lesbarkeit zu erhalten:

var count = 0

var fruit = (
        "apple" if count == 2
        else "pear" if count == 1
        else "banana" if count == 0
        else "orange"
)
print(fruit)  # banana

# Alternative syntax with backslashes instead of parentheses (for multi-line expressions).
# Less lines required, but harder to refactor.
var fruit_alt = \
        "apple" if count == 2 \
        else "pear" if count == 1 \
        else "banana" if count == 0 \
        else "orange"
print(fruit_alt)  # banana

Man kann auch prüfen, ob ein Wert in etwas enthalten ist. Dazu können Sie eine if-Anweisung in Kombination mit dem in-Operator verwenden:

# Check if a letter is in a string.
var text = "abc"
if 'b' in text: print("The string contains b")

# Check if a variable is contained within a node.
if "varName" in get_parent(): print("varName is defined in parent!")

while

Einfache Schleifen werden mit der while-Syntax erstellt. Schleifen können mit break unterbrochen oder mit continue fortgesetzt werden (wodurch zur nächsten Iteration der Schleife übergegangen wird, ohne dass in der aktuellen Iteration weiterer Code ausgeführt wird):

while (expression):
    statement(s)

for

Um eine Range wie ein Array oder eine Tabelle zu durchlaufen, wird eine for -Schleife verwendet. Beim Iterieren über ein Array wird das aktuelle Array-Element in der Schleifenvariablen gespeichert. Beim Durchlaufen eines Dictionary wird der Key in der Schleifenvariablen gespeichert.

for x in [5, 7, 11]:
    statement # Loop iterates 3 times with 'x' as 5, then 7 and finally 11.

var dict = {"a": 0, "b": 1, "c": 2}
for i in dict:
    print(dict[i]) # Prints 0, then 1, then 2.

for i in range(3):
    statement # Similar to [0, 1, 2] but does not allocate an array.

for i in range(1, 3):
    statement # Similar to [1, 2] but does not allocate an array.

for i in range(2, 8, 2):
    statement # Similar to [2, 4, 6] but does not allocate an array.

for i in range(8, 2, -2):
    statement # Similar to [8, 6, 4] but does not allocate an array.

for c in "Hello":
    print(c) # Iterate through all characters in a String, print every letter on new line.

for i in 3:
    statement # Similar to range(3).

for i in 2.2:
    statement # Similar to range(ceil(2.2)).

Wenn Sie einem Array Werte zuweisen wollen, während es durchlaufen wird, ist es am besten, for i in array.size() zu verwenden.

for i in array.size():
        array[i] = "Hello World"

Die Schleifenvariable ist in der for-Schleife lokal, und die Zuweisung an sie ändert den Wert im Array nicht. Objekte, die als Referenz übergeben werden (wie z. B. Nodes), können weiterhin durch den Aufruf von Methoden auf der Schleifenvariable manipuliert werden.

for string in string_array:
    string = "Hello World" # This has no effect

for node in node_array:
    node.add_to_group("Cool_Group") # This has an effect

match

Eine match-Anweisung benutzt man, um die Ausführung des Programms zu verzweigen. Sie ist äquivalent zur switch-Anweisung, wie sie aus vielen anderen Sprachen bekannt ist, aber bietet darüberhinaus zusätzliche Funktionalität.

Warnung

match ist strenger als der Operator ==. Zum Beispiel entspricht 1 nicht 1.0. Die einzige Ausnahme ist die Übereinstimmung von String und StringName: zum Beispiel wird der String "hallo" als gleichwertig mit dem StringName &"hallo" angesehen.

Grundlegende Syntax
match <expression>:
    <pattern(s)>:
        <block>
    <pattern(s)> when <guard expression>:
        <block>
    <...>
Crashkurs für jene, die mit switch-Anweisungen vertraut sind
  1. Ersetze switch mit match.

  2. Entferne case.

  3. Entferne alle breaks.

  4. Ändere default zu einem einzelnen Unterstrich.

Kontrollfluss

Die Muster werden von oben nach unten geprüft. Wenn ein Muster übereinstimmt, wird der erste entsprechende Block ausgeführt. Danach wird die Ausführung unterhalb der match-Anweisung fortgesetzt.

Bemerkung

Das in Version 3.x unterstützte spezielle Verhalten von continue in match wurde in Godot 4.0 entfernt.

Die folgenden Mustertypen sind verfügbar:

  • Literal-Muster

    Passt auf ein Literal:

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • Ausdruck-Muster

    Passt auf einen konstanten Ausdruck, einen Bezeichner oder einen Attributzugriff (A.B):

    match typeof(x):
        TYPE_FLOAT:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Wildcard-Muster

    Dieses Muster passt auf alles. Es wird als einzelner Unterstrich geschrieben.

    Es kann als Äquivalent zu default in einer switch-Anweisung in anderen Sprachen verwendet werden:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        _:
            print("It's not 1 or 2. I don't care to be honest.")
    
  • Binding-Muster

    Ein Binding-Muster erzeugt eine neue Variable. Wie das Wildcard-Muster passt es auf alles und gibt diesem Wert einen Namen. Es ist besonders hilfreich beim Array- und Dictionary-Muster:

    match x:
        1:
            print("It's one!")
        2:
            print("It's one times two!")
        var new_var:
            print("It's not 1 or 2, it's ", new_var)
    
  • Array-Muster

    Passt auf ein Array. Jedes einzelne Element des Array-Musters ist selbst ein Muster, sodass Sie sie verschachteln können.

    Die Länge des Arrays wird zuerst getestet. Sie muss dieselbe Größe wie das Muster haben, andernfalls passt das Muster nicht.

    Array mit offenem Ende: Ein Array kann größer als das Muster sein, indem das letzte Untermuster mit .. erstellt wird.

    Jedes Untermuster muss durch Kommas getrennt werden.

    match x:
        []:
            print("Empty array")
        [1, 3, "test", null]:
            print("Very specific array")
        [var start, _, "test"]:
            print("First element is ", start, ", and the last is \"test\"")
        [42, ..]:
            print("Open ended array")
    
  • Dictionary-Muster

    Funktioniert auf die gleiche Weise wie das Array-Muster, jeder Key muss ein konstantes Muster haben.

    Die Größe des Dictionary wird zuerst getestet. Es muss dieselbe Größe wie das Muster haben, andernfalls passt das Muster nicht.

    Dictionary mit offenem Ende: Ein Dictionary kann größer als das Muster sein, indem das letzte Untermuster mit .. erstellt wird.

    Jede Untermuster muss durch ein Komma getrennt werden.

    Wenn Sie keinen Value angeben, wird nur das Vorhandensein des Keys geprüft.

    Ein Value-Muster wird durch ein : vom Key-Muster getrennt.

    match x:
        {}:
            print("Empty dict")
        {"name": "Dennis"}:
            print("The name is Dennis")
        {"name": "Dennis", "age": var age}:
            print("Dennis is ", age, " years old.")
        {"name", "age"}:
            print("Has a name and an age, but it's not Dennis :(")
        {"key": "godotisawesome", ..}:
            print("I only checked for one entry and ignored the rest")
    
  • Mehrere Muster

    Sie können auch mehrere durch Komma getrennte Muster angeben. Diese Muster dürfen keine Bindings enthalten.

    match x:
        1, 2, 3:
            print("It's 1 - 3")
        "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")
    
Muster-Guards

Pro match kann nur ein Zweig ausgeführt werden. Sobald ein Zweig ausgewählt wurde, werden die anderen nicht mehr geprüft. Wenn Sie dasselbe Muster für mehrere Verzweigungen verwenden wollen oder um zu verhindern, dass eine Verzweigung mit einem zu allgemeinen Muster gewählt wird, können Sie einen Guard-Ausdruck nach der Liste der Muster mit dem Schlüsselwort when angeben:

match point:
    [0, 0]:
        print("Origin")
    [_, 0]:
        print("Point on X-axis")
    [0, _]:
        print("Point on Y-axis")
    [var x, var y] when y == x:
        print("Point on line y = x")
    [var x, var y] when y == -x:
        print("Point on line y = -x")
    [var x, var y]:
        print("Point (%s, %s)" % [x, y])
  • Gibt es kein passendes Muster für die aktuelle Verzweigung, wird der guard-Ausdruck nicht ausgewertet und die Muster der nächsten Verzweigung werden geprüft.

  • Wenn ein passendes Muster gefunden wird, wird der Guard-Ausdruck ausgewertet.

    • Wenn er "true" ist, wird der Rumpf der Verzweigung ausgeführt und match endet.

    • Wenn sie "false" ist, werden die Muster der nächsten Verzweigung geprüft.

Klassen

Standardmäßig sind alle Skriptdateien namenlose Klassen. In diesem Fall können Sie sie nur über den Dateipfad referenzieren, entweder über einen relativen oder einen absoluten Pfad. Wenn Sie beispielsweise eine Skriptdatei character.gd benennen:

# Inherit from 'character.gd'.

extends "res://path/to/character.gd"

# Load character.gd and create a new node instance from it.

var Character = load("res://path/to/character.gd")
var character_node = Character.new()

Registrierung von benannten Klassen

Sie können Ihrer Klasse einen Namen geben, um sie als neuen Typ im Editor von Godot zu registrieren. Dazu verwenden Sie das Schlüsselwort class_name. Optional können Sie die Annotation @icon mit einem Pfad zu einem Bild verwenden, um es als Icon zu benutzen. Ihre Klasse wird dann mit ihrem neuen Icon im Editor erscheinen:

# item.gd

@icon("res://interface/icons/item.png")
class_name Item
extends Node
../../../_images/class_name_editor_register_example.png

Tipp

SVG images that are used as custom node icons should have the Editor > Scale With Editor Scale and Editor > Convert Icons With Editor Theme import options enabled. This allows icons to follow the editor's scale and theming settings if the icons are designed with the same color palette as Godot's own icons.

Hier ist ein Beispiel für eine Klassendatei:

# Saved as a file named 'character.gd'.

class_name Character


var health = 5


func print_health():
    print(health)


func print_this_script_three_times():
    print(get_script())
    print(ResourceLoader.load("res://character.gd"))
    print(Character)

Wenn Sie auch extends verwenden wollen, können Sie beides in der gleichen Zeile unterbringen:

class_name MyNode extends Node

Bemerkung

Godot initialisiert nicht-statische Variablen jedes Mal, wenn Sie eine Instanz erstellen, und dies schließt Arrays und Dictionarys ein. Dies ist im Sinne der Thread-Sicherheit, da Skripte in separaten Threads initialisiert werden können, ohne dass der Benutzer dies merkt.

Vererbung

Eine Klasse (gespeichert als Datei) kann erben von:

  • Einer globalen Klasse.

  • Einer anderen Klassendatei.

  • Eine inneren Klasse innerhalb einer anderen Klassendatei.

Mehrfach-Vererbung ist nicht erlaubt.

Vererbung verwendet das Schlüsselwort extends:

# Inherit/extend a globally available class.
extends SomeClass

# Inherit/extend a named class file.
extends "somefile.gd"

# Inherit/extend an inner class in another file.
extends "somefile.gd".SomeInnerClass

Bemerkung

Wenn die Vererbung nicht ausdrücklich definiert ist, erbt die Klasse standardmäßig von RefCounted.

Um zu prüfen, ob eine gegebene Instanz von einer gegebenen Klasse erbt, verwendet man das Schlüsselwort is:

# Cache the enemy class.
const Enemy = preload("enemy.gd")

# [...]

# Use 'is' to check inheritance.
if entity is Enemy:
    entity.apply_damage()

Um eine Funktion in einer Superklasse aufzurufen (d.h. eine, die in Ihrer aktuellen Klasse erweitert wurde), verwenden Sie das Schlüsselwort super:

super(args)

Dies ist besonders nützlich, weil Funktionen in erweiternden Klassen Funktionen mit demselben Namen in ihren Superklassen ersetzen. Wenn Sie sie trotzdem aufrufen wollen, können Sie super verwenden:

func some_func(x):
    super(x) # Calls the same function on the super class.

Wenn Sie eine andere Funktion aus der Superklasse aufrufen müssen, können Sie den Funktionsnamen mit dem Attribut-Operator angeben:

func overriding():
    return 0 # This overrides the method in the base class.

func dont_override():
    return super.overriding() # This calls the method as defined in the base class.

Warnung

Eines der häufigsten Mißverständnisse ist der Versuch, nichtvirtuelle Engine-Methoden wie get_class(), queue_free(), etc. zu überschreiben. Dies wird aus technischen Gründen nicht unterstützt.

In Godot 3 können Sie Methoden der Engine in GDScript per Shadowing überlagern, und es wird funktionieren, wenn Sie diese Methode in GDScript aufrufen. Allerdings wird die Engine Ihren Code nicht ausführen, wenn die Methode innerhalb der Engine bei einem Event aufgerufen wird.

In Godot 4 funktioniert sogar das Shadowing nicht immer, da GDScript die nativen Methodenaufrufe optimiert. Daher haben wir die Warnung NATIVE_METHOD_OVERRIDE hinzugefügt, die standardmäßig als Fehler behandelt wird. Wir raten dringend davon ab, diese Warnung zu deaktivieren oder zu ignorieren.

Beachten Sie, dass dies nicht für virtuelle Methoden wie _ready(), _process() und andere gilt (in der Dokumentation mit dem Qualifier virtual gekennzeichnet, und die Namen beginnen mit einem Unterstrich). Diese Methoden sind speziell für die Anpassung des Verhaltens der Engine gedacht und können in GDScript überschrieben werden. Signale und Benachrichtigungen können für diese Zwecke ebenfalls nützlich sein.

Klassen-Konstruktor

Der Klassenkonstruktor, der bei der Instanziierung der Klasse aufgerufen wird, heißt _init. Wenn Sie den Konstruktor der Basisklasse aufrufen wollen, können Sie auch die super-Syntax verwenden. Beachten Sie, dass jede Klasse einen impliziten Konstruktor hat, der immer aufgerufen wird (er definiert die Defaultwerte der Klassenvariablen). super wird verwendet, um den expliziten Konstruktor aufzurufen:

func _init(arg):
   super("some_default", arg) # Call the custom base constructor.

Dies lässt sich besser anhand von Beispielen erklären. Betrachten Sie dieses Szenario:

# state.gd (inherited class).
var entity = null
var message = null


func _init(e=null):
    entity = e


func enter(m):
    message = m


# idle.gd (inheriting class).
extends "state.gd"


func _init(e=null, m=null):
    super(e)
    # Do something with 'e'.
    message = m

Hier müssen ein paar Sachen berücksichtigt werden:

  1. Wenn die geerbte Klasse (state.gd) einen _init-Konstruktor definiert, der Argumente entgegennimmt (in diesem Fall e), dann muss die erbende Klasse (idle.gd) _init ebenfalls definieren und entsprechende Parameter an _init von state.gd übergeben.

  2. idle.gd kann eine andere Anzahl von Argumenten haben als die Basisklasse state.gd.

  3. Im obigen Beispiel ist e, das an den Konstruktor state.gd übergeben wird, das gleiche e, das an idle.gd übergeben wird.

  4. Wenn der _init-Konstruktor von idle.gd 0 Argumente annimmt, muss er trotzdem einen Wert an die Basisklasse state.gd übergeben, auch wenn er nichts tut. Das bringt uns zu der Tatsache, dass man auch Ausdrücke an den Basiskonstruktor übergeben kann, nicht nur Variablen, z.B.:

    # idle.gd
    
    func _init():
        super(5)
    

Statischer Konstruktor

Ein statischer Konstruktor ist eine statische Funktion _static_init, die automatisch beim Laden der Klasse aufgerufen wird, nachdem die statischen Variablen initialisiert worden sind:

static var my_static_var = 1

static func _static_init():
    my_static_var = 2

Ein statischer Konstruktor kann keine Argumente annehmen und darf keinen Wert zurückgeben.

Innere Klassen

Eine Klassendatei kann innere Klassen beinhalten. Innere Klassen werden durch das class-Schlüsselwort definiert. Sie werden mithilfe der ClassName.new()-Funktion instanziert.

# Inside a class file.

# An inner class in this class file.
class SomeInnerClass:
    var a = 5


    func print_value_of_a():
        print(a)


# This is the constructor of the class file's main class.
func _init():
    var c = SomeInnerClass.new()
    c.print_value_of_a()

Klassen als Ressourcen

Klassen, die als Datei gespeichert sind, werden als Ressourcen behandelt. Sie müssen von der Festplatte geladen werden, um auf sie in anderen Klassen zuzugreifen. Dies macht man mit den Funktionen load oder preload (siehe weiter unten). Das Instanziieren einer geladenen Klassenressource erledigt man durch Aufruf der new-Funktion auf dem Klassenobjekt:

# Load the class resource when calling load().
var MyClass = load("myclass.gd")

# Preload the class only once at compile time.
const MyClass = preload("myclass.gd")


func _init():
    var a = MyClass.new()
    a.some_function()

Exporte

Bemerkung

Die Dokumentation über Exportieren wurde nach GDScript exportierte Propertys verschoben.

Propertys (set/get)

Manchmal möchte man, dass eine Member-Variable einer Klasse mehr tut, als nur Daten zu speichern und bei jeder Änderung ihres Wertes eine Validierung oder Berechnung durchzuführen. Es kann auch erwünscht sein, den Zugriff auf sie in irgendeiner Weise zu kapseln.

Hierfür bietet GDScript eine spezielle Syntax zur Definition von Propertys mit den Schlüsselwörtern set und get nach einer Variablendeklaration. Anschließend können Sie einen Codeblock definieren, der ausgeführt wird, wenn auf die Variable zugegriffen wird oder sie zugewiesen wird.

Beispiel:

var milliseconds: int = 0
var seconds: int:
    get:
        return milliseconds / 1000
    set(value):
        milliseconds = value * 1000

Bemerkung

Im Gegensatz zu setget in früheren Godot-Versionen werden die Proprety-Setter und -Getter immer aufgerufen (außer wie unten angegeben), auch wenn auf sie innerhalb derselben Klasse zugegriffen wird (mit oder ohne Voranstellen von self.). Dies macht das Verhalten konsistent. Wenn Sie direkten Zugriff auf den Wert benötigen, verwenden Sie eine andere Variable für den direkten Zugriff und lassen den Property-Code diesen Namen verwenden.

Alternative Syntax

Es gibt auch eine andere Notation, um bestehende Klassenfunktionen zu verwenden, wenn Sie den Code von der Variablendeklaration trennen wollen oder den Code für mehrere Propertys wiederverwenden müssen (aber Sie können nicht unterscheiden, für welche Property der Setter/Getter aufgerufen wird):

var my_prop:
    get = get_my_prop, set = set_my_prop

Dies kann auch in der gleichen Zeile geschehen:

var my_prop: get = get_my_prop, set = set_my_prop

Der Setter und der Getter müssen dieselbe Notation verwenden, das Mischen von Stilen für dieselbe Variable ist nicht erlaubt.

Bemerkung

Für inline Setter und Getter können keine Typ-Hinweise angegeben werden. Dies geschieht absichtlich, um Boilerplate-Code zu reduzieren. Wenn die Variable typisiert ist, dann ist das Argument des Setters automatisch vom gleichen Typ und der Rückgabewert des Getters muss damit übereinstimmen. Getrennte Setter/Getter-Funktionen können Type-Hints haben, und der Typ muss mit dem Typ der Variablen übereinstimmen oder ein breiterer Typ sein.

Wann setter/getter nicht aufgerufen werden

Wenn eine Variable initialisiert wird, wird der Wert des Initialisierers direkt in die Variable geschrieben. Auch wenn die Annotation @onready auf die Variable angewendet wird.

Die Verwendung des Variablennamens, um ihn in seinem eigenen Setter zu setzen oder in seinem eigenen Getter zu holen, greift direkt auf den zugrundeliegenden Member zu, erzeugt also keine unendliche Rekursion und erspart Ihnen die explizite Deklaration einer weiteren Variable:

signal changed(new_value)
var warns_when_changed = "some value":
    get:
        return warns_when_changed
    set(value):
        changed.emit(value)
        warns_when_changed = value

Das gilt auch für die alternative Syntax:

var my_prop: set = set_my_prop

func set_my_prop(value):
    my_prop = value # No infinite recursion.

Warnung

Die Ausnahme wird nicht auf andere Funktionen übertragen, die im Setter/Getter aufgerufen werden. Der folgende Code zum Beispiel verursacht eine unendliche Rekursion:

var my_prop:
    set(value):
        set_my_prop(value)

func set_my_prop(value):
    my_prop = value # Infinite recursion, since `set_my_prop()` is not the setter.

Tool-Modus

Standardmäßig werden Skripte nicht im Editor ausgeführt und nur die exportierten Propertys können geändert werden. In einigen Fällen ist es erwünscht, dass sie innerhalb des Editors laufen (solange sie keinen Spielcode ausführen oder dies manuell vermeiden). Hierfür gibt es die Annotation @tool, die am Anfang der Datei platziert werden muss:

@tool
extends Button

func _ready():
    print("Hello")

Siehe Code im Editor ausführen für weitere Informationen.

Warnung

Seien Sie vorsichtig, wenn Sie Nodes mit queue_free() oder free() in einem Tool-Skript freigeben (insbesondere der Eigentümer des Skripts selbst). Da Tool-Skripte ihren Code im Editor ausführen, kann ihr Missbrauch zum Absturz des Editors führen.

Speicher-Management

Godot implementiert die Reference Counting, um bestimmte Instanzen, die nicht mehr verwendet werden, freizugeben, anstatt einen Garbage Collector einzusetzen oder eine rein manuelle Verwaltung zu erfordern. Jede Instanz der Klasse RefCounted (oder jeder Klasse, die sie erbt, wie z.B. Resource) wird automatisch freigegeben, wenn sie nicht mehr verwendet wird. Eine Instanz einer Klasse, die keine RefCounted ist (wie Node oder der Basistyp Object), bleibt im Speicher, bis sie mit free() (oder queue_free() für Nodes) gelöscht wird.

Bemerkung

Wenn ein Node mittels free() oder queue_free() gelöscht wird, werden auch alle seine Unter-Nodes rekursiv gelöscht.

Um Referenzzyklen zu vermeiden, die nicht freigegeben werden können, wird eine WeakRef-Funktion zur Verfügung gestellt, um schwache Referenzen zu erzeugen, die den Zugriff auf das Objekt erlauben, ohne ein RefCounted am Freigeben zu hindern. Hier ist ein Beispiel:

extends Node

var my_file_ref

func _ready():
    var f = FileAccess.open("user://example_file.json", FileAccess.READ)
    my_file_ref = weakref(f)
    # the FileAccess class inherits RefCounted, so it will be freed when not in use

    # the WeakRef will not prevent f from being freed when other_node is finished
    other_node.use_file(f)

func _this_is_called_later():
    var my_file = my_file_ref.get_ref()
    if my_file:
        my_file.close()

Wenn keine Referenzen verwendet werden, kann alternativ mit is_instance_valid(instance) überprüft werden, ob ein Objekt freigegeben wurde.

Signale

Signale sind ein Werkzeug zum Senden von Nachrichten von einem Objekt, auf die andere Objekte reagieren können. Verwenden Sie das Schlüsselwort signal, um benutzerdefinierte Signale für eine Klasse zu erstellen.

extends Node


# A signal named health_depleted.
signal health_depleted

Bemerkung

Signale sind ein Callback <https://de.wikipedia.org/wiki/R%C3%BCckruffunktion> _-Mechanismus. Sie übernehmen auch die Rolle von Observern, einem gängigen Programmier-Pattern. Weitere Informationen finden Sie im Observer-Tutorial (in englischer Sprache)<https://gameprogrammingpatterns.com/observer.html> _ im E-Book Game Programming Patterns.

Sie können diese Signale mit Methoden auf die gleiche Weise verbinden, wie Sie Built-in-Signale von Nodes wie Button oder RigidBody3D verbinden.

Im Beispiel unten verbinden wir das Signal health_depleted von einem Character-Node mit einem Game-Node. Wenn der Charakter-Node das Signal aussendet, wird der _on_character_health_depleted-Node des Spiels aufgerufen:

# game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.health_depleted.connect(_on_character_health_depleted)


func _on_character_health_depleted():
    get_tree().reload_current_scene()

Sie können so viele Argumente wie Sie möchten zusammen mit einem Signal aussenden.

Hier ist ein Beispiel, wo dies nützlich ist. Angenommen, wir möchten, dass eine Lebensleiste auf dem Bildschirm mit einer Animation auf Gesundheitsänderungen reagiert, aber wir möchten in unserem Szenenbaum die Benutzeroberfläche vom Spieler getrennt halten.

In unserem Skript Charakter.gd definieren wir ein Health_changed-Signal und senden es mit Signal.emit() aus, und von einem Game-Node weiter oben in unserem Szenenbaum verbinden wir es mit der Lifebar mit der Methode Signal.connect():

# character.gd

...
signal health_changed


func take_damage(amount):
    var old_health = health
    health -= amount

    # We emit the health_changed signal every time the
    # character takes damage.
    health_changed.emit(old_health, health)
...
# lifebar.gd

# Here, we define a function to use as a callback when the
# character's health_changed signal is emitted.

...
func _on_Character_health_changed(old_value, new_value):
    if old_value > new_value:
        progress_bar.modulate = Color.RED
    else:
        progress_bar.modulate = Color.GREEN

    # Imagine that `animate` is a user-defined function that animates the
    # bar filling up or emptying itself.
    progress_bar.animate(old_value, new_value)
...

Im Game-Node erhalten wir sowohl den Character als auch den Lifebar-Node und verbinden dann den Charakter, der das Signal aussendet, mit dem Empfänger, in diesem Fall dem Lifebar-Node .

# game.gd

func _ready():
    var character_node = get_node('Character')
    var lifebar_node = get_node('UserInterface/Lifebar')

    character_node.health_changed.connect(lifebar_node._on_Character_health_changed)

Dies ermöglicht es der Lifebar, auf Gesundheitsänderungen zu reagieren, ohne sie mit dem Character-Node zu koppeln.

Sie können optionale Argumentnamen in Klammern nach der Definition des Signals schreiben:

# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)

Diese Argumente werden im Node-Dock des Editors angezeigt und Godot kann sie verwenden, um Callback-Funktionen für Sie zu generieren. Sie können jedoch immer noch eine beliebige Anzahl von Argumenten angeben, wenn Sie Signale ausgeben. Es liegt an Ihnen, die richtigen Werte auszugeben.

../../../_images/gdscript_basics_signals_node_tab_1.png

GDScript kann ein Array von Werten an Verbindungen zwischen einem Signal und einer Methode binden. Wenn das Signal ausgegeben wird, empfängt die Callback-Methode die gebundenen Werte. Diese gebundenen Argumente sind für jede Verbindung einzigartig aber die Werte bleiben gleich.

Mit diesem Wertearray können Sie der Verbindung zusätzliche konstante Informationen hinzufügen, wenn Ihnen das ausgegebene Signal selbst keinen Zugriff auf alle benötigten Daten bietet.

Aufbauend auf dem obigen Beispiel möchten wir ein Protokoll des von jedem Charakter erlittenen Schadens auf dem Bildschirm anzeigen, z.B. Spieler1 hat 22 Schaden erlitten. Das health_changed-Signal gibt uns nicht den Namen des Charakters, der Schaden genommen hat. Wenn wir also das Signal mit der In-Game-Konsole verbinden, können wir den Namen des Charakters in das Binds-Array-Argument einfügen:

# game.gd

func _ready():
    var character_node = get_node('Character')
    var battle_log_node = get_node('UserInterface/BattleLog')

    character_node.health_changed.connect(battle_log_node._on_Character_health_changed, [character_node.name])

Unser BattleLog -Node empfängt jedes Element im Binds-Array als zusätzliches Argument:

# battle_log.gd

func _on_Character_health_changed(old_value, new_value, character_name):
    if not new_value <= old_value:
        return

    var damage = old_value - new_value
    label.text += character_name + " took " + str(damage) + " damage."

Warten auf Signale oder Coroutinen

Das Schlüsselwort await kann verwendet werden, um Coroutinen zu erzeugen, die warten, bis ein Signal ausgegeben wird, bevor sie die Ausführung fortsetzen. Die Verwendung des Schlüsselwortes await mit einem Signal oder einem Aufruf einer Funktion, die auch eine Coroutine ist, gibt die Kontrolle sofort an den Aufrufer zurück. Wenn das Signal ausgegeben wird (oder die aufgerufene Coroutine beendet wird), wird die Ausführung an dem Punkt fortgesetzt, an dem sie angehalten wurde.

Um zum Beispiel die Ausführung zu stoppen, bis der Benutzer eine Taste drückt, können Sie wie folgt vorgehen:

func wait_confirmation():
    print("Prompting user")
    await $Button.button_up # Waits for the button_up signal from Button node.
    print("User confirmed")
    return true

In diesem Fall wird die wait_confirmation zu einer Coroutine, was bedeutet, daß der Aufrufer auch darauf warten muß:

func request_confirmation():
    print("Will ask the user")
    var confirmed = await wait_confirmation()
    if confirmed:
        print("User confirmed")
    else:
        print("User cancelled")

Beachten Sie, dass die Abfrage des Rückgabewerts einer Coroutine ohne await einen Fehler auslöst:

func wrong():
    var confirmed = wait_confirmation() # Will give an error.

Wenn Sie jedoch nicht auf das Ergebnis angewiesen sind, können Sie die Funktion einfach asynchron aufrufen, wodurch die Ausführung nicht unterbrochen wird und die aktuelle Funktion nicht zu einer Coroutine wird:

func okay():
    wait_confirmation()
    print("This will be printed immediately, before the user press the button.")

Wenn Sie await mit einem Ausdruck verwenden, der weder ein Signal noch eine Coroutine ist, wird der Wert sofort zurückgegeben und die Funktion gibt die Kontrolle nicht an den Aufrufer zurück:

func no_wait():
    var x = await get_five()
    print("This doesn't make this function a coroutine.")

func get_five():
    return 5

Das bedeutet auch, dass die Rückgabe eines Signals von einer Funktion, die keine Coroutine ist, den Aufrufer auf dieses Signal warten lässt:

func get_signal():
    return $Button.button_up

func wait_button():
    await get_signal()
    print("Button was pressed")

Bemerkung

Im Gegensatz zu yield in früheren Godot-Versionen können Sie das Funktionsstatusobjekt nicht erhalten. Dies geschieht, um Typsicherheit zu gewährleisten. Mit dieser Typsicherheit kann eine Funktion nicht sagen, dass sie einen int zurückgibt, während sie in Wirklichkeit zur Laufzeit ein Funktionsstatusobjekt zurückgibt.

Assert-Schlüsselwort

Das assert-Schlüsselwort kann verwendet werden, um Bedingungen im Debug-Builds zu überprüfen. Diese Assertions werden in Nicht-Debug-Builds ignoriert. Dies bedeutet, dass der als Argument übergebene Ausdruck in einem Projekt, das im Release-Modus exportiert wird, nicht ausgewertet wird. Aus diesem Grund dürfen Assertions keine Ausdrücke enthalten, die Seiteneffekte haben. Andernfalls hängt das Verhalten des Skripts davon ab, ob das Projekt in einem Debug-Build ausgeführt wird.

# Check that 'i' is 0. If 'i' is not 0, an assertion error will occur.
assert(i == 0)

Wenn Sie ein Projekt über den Editor ausführen, wird das Projekt angehalten, wenn ein Assertion-Fehler auftritt.

Sie können optional eine benutzerdefinierte Fehlermeldung übergeben, die angezeigt wird, wenn die Assertion fehlschlägt:

assert(enemy_power < 256, "Enemy is too powerful!")