GDScript Grundlagen

Einführung

GDScript ist eine high-level, dynamisch typisierte Programmiersprache zum Erstellen von Inhalten. Es verwendet eine ähnliche Syntax wie Python (Blöcke basieren auf Einrückung und viele Schlüsselwörter sind ähnlich). Es zielt auf eine Optimierung und enge Integration mit der Godot Engine ab, um große Flexibilität bei der Erstellung von Inhalten und deren Integration zu ermöglichen.

Verlauf

Bemerkung

Ältere Artikel über GDScript wurden nach Frequently Asked Questions verschoben.

Beispiel von GDScript

Manche Leute lernen besser, indem sie einfach die Syntax betrachten, deshalb hier ein Beispiel wie GDScript aussieht.

# A file is a class!

# Inheritance

extends BaseClass

# (optional) class definition with a custom icon

class_name MyClass, "res://path/to/optional/icon.svg"


# Member variables

var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
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)


# Function

func some_function(param1, param2):
    var local_var = 5

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

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

    while param2 != 0:
        param2 -= 1

    var local_var2 = param1 + 3
    return local_var2


# Functions override functions with the same name on the base/parent class.
# If you still want to call them, use '.' (like 'super' in other languages).

func something(p1, p2):
    .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 dieser Anleitung empfohlen: GDScript: Eine Einführung in dynamische Programmiersprachen.

Sprache

Im Folgenden sei 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

Jede Zeichenkette, die 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 Bezeichner wird Groß- und Kleinschreibung beachtet (foo unterscheidet sich von FOO).

Schlüsselwörter

Das Folgende ist eine Auflistung Schlüsselwörter, die von der Sprache unterstützt werden. Da Schlüsselwörter reservierte Begriffe (Tokens) sind, können sie nicht als Bezeichner verwendet werden. Operatoren (wie in, not, and oder or) und Namen eingebauter 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.

Klasse

Definiert eine innere Klasse.

class_name

Definiert einen Klassennamen und ein optionales Symbol für Ihr Skript.

extends

Definiert die Basisklasse, von welcher diese Klasse abgeleitet werden soll.

is

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

as

Versucht den Wert einem vorgegebenen Type zuzuweisen falls möglich.

self

Bezieht sich auf die aktuelle Klassen Instanz.

tool

Führt ein Skript im Editor aus.

signal

Definiert ein Signal.

func

Definiert eine Funktion.

static

Definiert eine statische Funktion. Statische Member-Variablen sind nicht erlaubt.

const

Definiert eine Konstante.

enum

Definiert eine Aufzählung.

var

Definiert eine Variable.

onready

Initialisiert eine Variable sobald das Node, an dem das Skript angehängt wurde, und ihre Unterobjekte Teil des Szenenbaums sind.

export

Speichert eine Variable zusammen mit der Ressource, an der sie hängt, und macht sie im Editor sicht- und modifizierbar.

setget

Definiert setter und getter Funktionen für eine Variable.

breakpoint

Helfer für den Editor für Haltepunkte des Debuggers.

preload

Lädt eine Variable oder Klasse im Voraus, siehe Classes as resources.

yield

Coprogramm Unterstützung, siehe Coroutines with yield.

assert

Aktiviert eine Bedingung und protokolliert Fehler . Wird bei Nicht-Debug-Builds ignoriert. Siehe Assert keyword.

remote

Anmerkungen zu RPC Netzwerke, siehe High-Level Mehrspieler Dokumentation.

master

Anmerkungen zu RPC Netzwerke, siehe High-Level Mehrspieler Dokumentation.

puppet

Anmerkungen zu RPC Netzwerke, siehe High-Level Mehrspieler Dokumentation.

remotesync

Anmerkungen zu RPC Netzwerke, siehe High-Level Mehrspieler Dokumentation.

mastersync

Anmerkungen zu RPC Netzwerke, siehe High-Level Mehrspieler Dokumentation.

puppetsync

Anmerkungen zu RPC Netzwerke, siehe High-Level Mehrspieler Dokumentation.

PI

Die π-Konstante.

TAU

TAU-Konstante.

INF

Konstante "Unendlichkeit". Wird für Vergleiche genutzt.

NAN

Konstante "NAN" (not a number - Keine Zahl). Wird für Vergleiche genutzt.

Operatoren

Das folgende ist einer Liste der unterstützen Operatoren und ihrer Auswertungsreihenfolge.

Operator

Beschreibung

x[index]

Indexierung (höchste Priorität)

x.attribute

Referenzierung eines Attributs

foo()

Funktionsaufruf

is

Prüfung des Instanztyps

~

Bitweise Negation

-x

Negative / Unäre Negation

* / %

Multiplikation / Division / Modulus (Restbetrag)

These operators have the same behavior as C++. Integer division is truncated rather than returning a fractional number, and the % operator is only available for ints ("fmod" for floats), and is additionally used for Format Strings

+

Addition / Verkettung von Arrays

-

Subtraktion

<< >>

Bit-Schieben

&

Bitweises UND

^

Bitweises exklusiv-ODER (XOR)

|

Bitweises (inklusiv-) ODER

< > == != >= <=

Vergleiche

in

When used with the if keyword it checks if a value is within a string, list, range, dictionary, or node. When used with the for keyword it is used to iterate though the contents of a string, list, range, dictionary or node.

! not

Boolesches NICHT

and &&

Boolesches UND

or ||

Boolesches ODER

if x else

Bedingte (ternäre) if/else Anweisung

as

Typumwandlung

= += -= *= /= %= &= |= <<= >>=

Zuweisung (niedrigste Priorität)

Literale

Literal

Typ

45

Dezimale Ganzzahl (Basis 10)

0x8f51

Basis 16 (Hexadezimal) Ganzzahl

0b101010

Basis 2 (Binär) Ganzzahl

3.14, 58.1e-10

Fließkommazahl (reell)

"Hallo", "Hey"

Zeichenketten

"""Hallo"""

Mehrzeilige Zeichenkette

@"Node/Label"

NodePath oder StringName

$NodeWeg

Kurzform für get_node("NodePath")

Ganzzahlen und Gleitkommazahlen 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.

Kommentare

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

# This is a comment.

Eingebaute Typen

Eingebaute Typen werden stapelweise zugewiesen. Sie werden als Werte übergeben. Dies bedeutet, dass bei jeder Zuweisung oder bei der Übergabe als Argumente an Funktionen eine Kopie erstellt wird. Die einzigen Ausnahmen sind Arrays und Dictionaries, die als Referenz übergeben werden, damit sie gemeinsam genutzt werden. (zusammengesetzte Arrays wie "PoolByteArray" werden weiterhin als Werte übergeben.)

Einfache eingebaute Typen

Eine Variable in GDScript kann mehreren eingebauten 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-Datentyp, der nur true oder false enthalten kann.

int

Kurz für "Integer" (Ganzzahl): es kann eine Ganzzahl (positiv oder negativ) als 64-Bit Wert gespeichert werden, gleichbedeutend mit "int64_t" in C++.

float

Speichert eine reelle Zahl (Fließkommazahl) als 64-Bit Wert (doppelte Genauigkeit), gleichbedeutend mit "double" in C++. Anmerkung: Datenstrukturen wie Vector2, Vector3 und PoolRealArray speichern 32-Bit Fließkommazahlen (einfache Genauigkeit).

String

Eine Folge von Zeichen im Unicode-Format. Zeichenketten können die folgenden Escape-Sequenzen enthalten:

Escape Sequenz

wird zu

\n

Zeilenumbruch (Line Feed)

\t

Horizontaler Tabulator

\r

Wagenrücklauf (Carriage Return)

\a

Alarm (Piepton/Klingel)

\b

Rücktaste

\f

neue Seite (Page Break)

\v

Vertikaler Tabulator

\"

Doppelte Anführungszeichen

\'

Einfaches Anführungszeichen

\\

Backslash (Rückstrich oder umgekehrter Schrägstrich)

\uXXXX

Unicode Codepoint XXXX (Hexadezimal, Case-Insensitive = Groß/Kleinschreibung ist egal)

GDScript unterstützt auch GDScript Zeichenketten formatieren.

eingebaute Vektor-Typen

Vector2

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

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.

Transform2D

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

Plane

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

Quat

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

Entlang der Achse ausgerichtete Begrenzungs-Box (oder 3D Box) besitzt 2 Vektorfelder: position und size. Besitzt alternativ ein end-Feld, welches 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.

Transform

3D Transform besitzen ein Basis Feld basis und ein Vector3 Feld origin.

In die Engine eingebaute 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 (Farbwert)/saturation (Sättigung)/value (Dunkelstufe) steht.

NodePath

Hauptsächlich im Szenen-System genutzter kompilierter Pfad zu einem Node. Zuweisung kann einfach zu und von einer Zeichenkette erfolgen.

RID

Ressourcen ID (RID). Server nutzen generische RIDs, um unklare Daten zu referenzieren.

Object

Basisklasse für alles, was kein eingebauter Typ ist.

Container eingebaute Typen

Array

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

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].

GDScript-Arrays werden aus Geschwindigkeitsgründen linear im Speicher zugewiesen. 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

Zugehöriger Container, der durch eindeutige Schlüssel referenzierte Werte enthält.

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"
}

Eine Tabellensyntax im Lua-Stil wird ebenfalls unterstützt. Der Lua-Stil verwendet = anstelle von : und benutzt keine Anführungszeichen, um Zeichenketten-Schlüssel zu markieren (was etwas weniger Schreibarbeit macht). Schlüssel, die in dieser Form geschrieben werden, können jedoch nicht mit einer Ziffer beginnen (wie jeder GDScript-Bezeichner).

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

Um einen Schlüssel zu einem bestehenden Dictionary hinzuzufügen, greifen Sie wie ein vorhandener Schlüssel darauf zu und weisen Sie ihm einen 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 Eigenschaften von jedem Object zuzugreifen, nicht nur aus Dictionaries. Beachten Sie, dass beim Indizieren einer nicht vorhandenen Eigenschaft ein Skriptfehler auftritt. Um dies zu vermeiden, verwenden Sie stattdessen die Methoden Object.get() und Object.set().

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 order.

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 = Sprite.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 := Sprite.new() # 'my_node' is of type 'Sprite'.

Ein Rückschluss auf den Typ ist nur möglich, wenn der zugewiesene Wert einen definierten Typ hat, andernfalls wird ein Fehler ausgelöst.

Gültige Typen sind:

  • eingebaute Typen (Array, Vector2, int, String, etc.).

  • Engine-Klassen (Node, Ressource, Referenz, usw.).

  • Konstante 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.

Casting (Typumwandlung)

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

Das Umwandeln zwischen Objekttypen führt zu demselben Objekt, wenn der Wert vom selben Typ oder einem Untertyp des Umwandlungstyps ist.

var my_node2D: Node2D
my_node2D = $Sprite as Node2D # Works since Sprite 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 eingebauten 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 Sprite.
var my_sprite := $Character as Sprite

# 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.

Bemerkung

Da Arrays und Dictionaries als Referenz übergeben werden, sind Konstanten "flach". Das heißt, wenn Sie ein konstantes Array oder Dictionary deklarieren, kann es danach noch geändert werden. Sie können jedoch keinem anderen Wert zugewiesen werden.

Enumeratoren (Aufzählungen)

Enumeratoren sind im Grunde ein Kürzel für Konstanten und sind nützlich, wenn aufeinanderfolgende Ganzzahlen Kontanten zugeordnet werden sollen.

Wenn Sie der Aufzählung einen Namen übergeben, werden alle Schlüssel in ein konstantes Dictionary dieses Namens aufgenommen.

Wichtig

In Godot 3.1 und höher werden Schlüssel in einer benannten Aufzählung nicht als globale Konstanten registriert. Auf sie sollte vor dem Namen der Aufzählung (Name.KEY) zugegriffen werden. siehe ein Beispiel unten.

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

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.

Funktionen

Funktionen gehören immer zu einer Klasse. Die Priorität für die variable Suche ist: local → class member → global. Die Variable self ist immer verfügbar und wird als Option für den Zugriff auf Klassenmitglieder bereitgestellt, wird aber nicht immer benötigt (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 zurückkehren. Der Standard-Rückgabewert ist null.

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 Standardwert 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

Im Gegensatz zu Python sind Funktionen keine erste Klasse Objekte in GDScript. Das bedeutet, sie können nicht in Variablen gespeichert werden, als Argument für eine andere Funktion übergeben werden oder von anderen Funktionen zurückgegeben werden – aus Gründen der Geschwindigkeit.

Um eine Funktion zur Laufzeit namentlich zu referenzieren (z.B. um sie in einer Variablen zu speichern oder als Argument an eine andere Funktion zu übergeben), müssen die Helfer call oder funcref verwendet werden:

# Call a function by name in one step.
my_node.call("my_function", args)

# Store a function reference.
var my_func = funcref(my_node, "my_function")
# Call stored function reference.
my_func.call_func(args)

Statische Funktionen

Eine Funktion kann als statisch deklariert werden. Wenn eine Funktion statisch ist, hat sie keinen Zugriff auf die Member der Instanz oder self. Dies ist vor allem nützlich um Bibliotheken mit Hilfsfunktionen zu erzeugen:

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

Anweisungen und Kontrollstrukturen

Ausdrücke sind Standard und können Zuweisungen sein, Funktionsaufrufe, Flußkontrollstrukturen, usw. (siehe unten). ``;``als Trennzeichen zwischen Ausdrücken ist optional.

if/else/elif

Einfache Verzweigungen werden mit dem 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 Befehle können auf die selbe 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 anfangs einen anderen Wert zuweisen, basierend auf einem booleschen Ausdruck. 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

You may also wish to check if a value is contained within something. You can use an if statement combined with the in operator to accomplish this:

# 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

Simple loops are created by using while syntax. Loops can be broken using break or continued using continue (i.e. skipping to the next iteration of the loop without executing any further code in the current iteration):

while [expression]:
    statement(s)

for

Um einen Bereich 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 Schlüssel 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 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))

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.

Grundlegende Syntax:

match [expression]:
    [pattern](s):
        [block]
    [pattern](s):
        [block]
    [pattern](s):
        [block]

Crashkurs für jene, die mit switch-Anweisungen vertraut sind:

  1. Ersetze switch mit match.

  2. Entferne case.

  3. Entfernt jedes breaks. Sollte das standardmäßige break ungewünscht sein, so kann durch continue ein Fortfahren erzielt werden.

  4. Ändere default zu einem einzelnen Unterstrich.

Kontrollfluss:

Die Muster werden von oben nach unten abgeglichen. Wenn ein Muster passt, wird der entsprechende Block ausgeführt. Danach setzt die Ausführung unter der match-Anweisung fort. Wenn stattdessen ein Durchrutschen (Fallthrough) gewünscht ist, kann ein continue benutzt werden, um die Ausführung im aktuellen Block anzuhalten und in die Folgenden zu springen.

Es gibt 6 Arten von Mustern:

  • Konstanten-Muster

    Konstanten einfachen Typs, wie Zahlen und Zeichenketten:

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

    Vergleicht mit dem Inhalt einer Variable oder Aufzählung:

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

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

    Es kann als das Äquivalent genutzt werden von default in einer switch-Anweisung in anderen Sprachen.:

    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.")
    
  • Verbindungs-Muster

    Ein Verbindungs-Muster erzeugt eine neue Variable. Wie das Platzhalter-Muster passt es auf alles und gibt diesem Wert einen Namen. Es ist besonders hilfreich beim Feld- 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

    Entspricht einem Array. Jedes einzelne Element des Array-Musters ist selbst ein Muster, sodass Sie es verschachteln können.

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

    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 Feld-Muster, jeder Schlüssel muss ein gleichbleibendes Muster haben.

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

    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.

    Falls kein Wert angegeben wird, wird nur die Existenz des Schlüssels überprüft.

    Ein Wertemuster wird durch ein : vom Schlüsselmuster 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")
    
  • Vielfach-Muster

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

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

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()

Registering named classes

Sie könne Ihrer Klasse einen Name geben, um sie als neuen Typ im Godot-Editor zu registrieren. Dazu verwenden Sie das Schlüsselwort class_name. Optional können Sie ein Komma gefolgt von einem Pfad zu einem Bild hinzufügen, um es als Icon zu verwenden. Ihre Klasse wird dann mit ihrem neuen Icon im Editor erscheinen:

# Item.gd

extends Node
class_name Item, "res://interface/icons/item.png"
../../../_images/class_name_editor_register_example.png

Warnung

Wenn sich das Skript im Verzeichnis res://addons/ befindet, bewirkt class_name nur dann, dass das Node im Dialog Neues Node erstellen angezeigt wird, wenn das Skript Teil eines aktivierten Editor-Plugins ist. Siehe Erweiterungen (Plugins) erstellen für weitere Informationen.

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)

Bemerkung

Die Klassensyntax von Godot ist kompakt: Sie kann nur Mitgliedsvariablen oder Funktionen enthalten. Sie können statische Funktionen verwenden, jedoch keine statischen Mitgliedsvariablen. Auf die gleiche Weise initialisiert die Engine jedes Mal Variablen wenn Sie eine Instanz erstellen, einschließlich Arrays und Dictionaries. Dies dient der Thread-Sicherheit, da Skripte in separaten Threads initialisiert werden können, ohne dass der Benutzer dies weiß.

Vererbung

Eine Klasse (gespeichert als Datei) kann erben von:

  • Einer globalen Klasse.

  • Einer anderen Klassendatei.

  • Eine lokale Klasse innerhalb einer anderen Klassendatei.

Mehrfache Vererbung ist nicht erlaubt.

Vererbung benutzt 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

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 übergeordneten Klasse aufzurufen (d.h. eine extend-ed in Ihrer aktuellen Klasse), stellen Sie dem Funktionsnamen ein . vor:

.base_func(args)

Dies ist besonders nützlich, da Funktionen in erweiterten Klassen andere Funktionen mit demselben Namen in ihren übergeordneten Klassen ersetzen. Wenn Sie sie weiterhin aufrufen möchten, können Sie ihnen einen . voranstellen (wie das Schlüsselwort super in anderen Sprachen):

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

Bemerkung

Standardfunktionen wie _init, und die meisten Benachrichtigungen wie _enter_tree, _exit_tree, _process, _physics_process, etc. werden in allen Elternklassen automatisch aufgerufen. Es besteht keine Notwendigkeit, sie beim Überladen explizit aufzurufen.

Klassen-Konstruktor

Der Klassenkonstruktor, der bei der Klasseninstanziierung aufgerufen wird, heißt _init. Wie bereits erwähnt, werden die Konstruktoren von Elternklassen bei der Vererbung einer Klasse automatisch aufgerufen. Es besteht also normalerweise keine Notwendigkeit, ._init() explizit aufzurufen.

Anders als beim Aufruf einer regulären Funktion, wie im obigen Beispiel mit .some_func, werden Argumente, wenn der Konstruktor aus der geerbten Klasse Argumente entgegennimmt, wie folgt übergeben:

func _init(args).(parent_args):
   pass

Dies wird besser anhand von Beispielen erklärt. 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).(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 übernimmt (e in diesem Fall), dann muss die vererbende Klasse (Idle.gd) ebenfalls _init definieren und passende Parameter an _init von State.gd übergeben.

  2. Idle.gd kann eine andere Anzahl an Argumenten besitzen als die Basisklasse State.gd.

  3. Im obigen Beispiel ist das e im Konstruktor von State.gd das gleiche wie das e in Idle.gd.

  4. Wenn der Idle.gd's _init Konstruktor 0 Argumente annimmt, muss er immer noch irgendeinen Wert an die `State.gd Elternklasse übergeben, selbst wenn sie nichts tut. Das bringt uns zu der Tatsache, dass man auch im Basiskonstruktor Literale übergeben kann, nicht nur Variablen. z.B.:

    # Idle.gd
    
    func _init().(5):
        pass
    

Innere Klassen

Eine Klassendatei kann lokale Klassen beinhalten. Lokale Klassen werden durch das class Schlüsselwort definiert. Sie werden mithilfe der Klassenname.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 resources 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 Instanzieren 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 Exporte verschoben.

Setter/Getter

Es ist oft hilfreich zu wissen, ob sich eine Variable einer Klasse verändert, aus welchen Gründen auch immer. Es ist vielleicht auch wünschenswert sie auf eine gewisse Art vor Zugriffen zu schützen.

Dafür bietet GDScript einen setter/getter Syntax mit dem setget Schlüsselwort. Es wird direkt nach der Definition einer Variable genutzt:

var variable = value setget setterfunc, getterfunc

Wann immer der Wert von variable durch eine externe Quelle modifiziert wird (d.h. nicht aus dem lokalen Gebrauch in der Klasse), wird die setter Funktion (oben setterfunc) aufgerufen. Dies geschieht bevor der Wert geändert wird. Der setter muß entscheiden, was mit dem neuen Wert geschehen soll. Umgekehrt, wenn auf variable zugegriffen wird, muß die getter-Funktion (getterfunc oben) den gewünschten Wert zurückgeben. Unten ist ein Beispiel:

var my_var setget my_var_set, my_var_get


func my_var_set(new_value):
    my_var = new_value


func my_var_get():
    return my_var # Getter must return a value.

Eine der Funktionen setter oder getter kann weggelassen werden :

# Only a setter.
var my_var = 5 setget my_var_set
# Only a getter (note the comma).
var my_var = 5 setget ,my_var_get

Setter und Getter sind nützlich, wenn Variablen in Skripten oder Plugins zum Editor exportiert werden, um Eingaben zu validieren.

Wie gesagt, lokaler Zugriff wird nicht den Setter und Getter auslösen. Hier ist eine Veranschaulichung dafür:

func _init():
    # Does not trigger setter/getter.
    my_integer = 5
    print(my_integer)

    # Does trigger setter/getter.
    self.my_integer = 5
    print(self.my_integer)

Werkzeug-Modus

Standardmäßig werden Skripte nicht innerhalb des Editors ausgeführt und nur die exportierten Eigenschaften können geändert werden. In einigen Fällen ist es erwünscht, dass sie innerhalb des Editors ausgeführt werden (solange sie keinen Spielcode ausführen oder dies manuell vermeiden). Hierfür existiert das Schlüsselwort tool, das an den Anfang der Datei gesetzt 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

Wenn eine Klasse von Reference erbt, werden nicht mehr verwendete Instanzen freigegeben. Es gibt keinen Garbage Collector, nur Referenzzählung. Standardmäßig erweitern alle Klassen Referenz, die keine Vererbung definieren. Wenn dies nicht erwünscht ist, muss eine Klasse Object manuell erben und instance.free() aufrufen. Um Referenzzyklen zu vermeiden, die nicht freigegeben werden können, wird eine Funktion weakref zum Erstellen schwacher Referenzen bereitgestellt. Hier ist ein Beispiel:

extends Node

var my_node_ref

func _ready():
    my_node_ref = weakref(get_node("MyNode"))

func _this_is_called_later():
    var my_node = my_node_ref.get_ref()
    if my_node:
        my_node.do_something()

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 eine Rückruffunktion <https://de.wikipedia.org/wiki/R%C3%BCckruffunktion> _ Mechanismus. Sie übernehmen auch die Rolle der Beobachter, ein gängiges Programmiermuster. Weitere Informationen finden Sie im Observer-Tutorial (in englischer Sprache)<https://gameprogrammingpatterns.com/observer.html> _ im eBook Spielprogrammiermuster.

Sie können diese Signale auf dieselbe Weise mit Methoden verbinden, wie Sie integrierte Signale von Nodes wie Button oder :ref:` class_RigidBody` verbinden.

Im folgenden Beispiel verbinden wir das Signal "health_depleted" von einem "Character" -Node mit einem "Game" -Node. Wenn der Character-Node das Signal aussendet, wird des Game nodes``_on_Character_health_depleted``genannt :

# Game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.connect("health_depleted", self, "_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 ausgeben.

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 Character.gd -Skript definieren wir ein health_changed Signal und senden es mit Object.emit_signal() und von einem Game Node weiter oben im Szenenbaum, wir verbinden ihn mit der Lebensleiste mit der Object.connect() Methode:

# 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.
    emit_signal("health_changed", 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)
...

Bemerkung

Um Signale zu verwenden, muss Ihre Klasse die Objekt-Klasse oder einen beliebigen Typ erweitern, wie zum Beispiel Node, KinematicBody, Control ...

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

# Game.gd

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

    character_node.connect("health_changed", lifebar_node, "_on_Character_health_changed")

Dies ermöglicht es der Lifebar, auf Gesundheitsänderungen zu reagieren, ohne sie mit dem Character-Knoten 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 Rückruffunktionen 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 Rückrufmethode 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 das ausgegebene Signal selbst Ihnen 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 Argument binds array einfügen:

# Game.gd

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

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

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

# BattleLog.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."

Co-Routinen mit yield

GDScript bietet Unterstützung für die integrierte Funktion Coroutinen<https://en.wikipedia.org/wiki/Coroutine> _ über die integrierte Funktion yield. Das Aufrufen von Yield() kehrt sofort von der aktuellen Funktion zurück, wobei der aktuelle eingefrorene Zustand dieselbe Funktion wie der Rückgabewert hat. Wenn Sie für dieses resultierende Objekt resume() aufrufen, wird die Ausführung fortgesetzt und alles zurückgegeben, was die Funktion zurückgibt. Nach der Wiederaufnahme wird das Statusobjekt ungültig. Hier ist ein Beispiel:

func my_func():
    print("Hello")
    yield()
    print("world")


func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print("my dear")
    y.resume()
    # 'y' resumed and is now an invalid state.

Ausgabe:

Hello
my dear
world

Es ist auch möglich Werte zwischen yield() und resume() zu übergeben, zum Beispiel:

func my_func():
    print("Hello")
    print(yield())
    return "cheers!"


func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print(y.resume("world"))
    # 'y' resumed and is now an invalid state.

Ausgabe:

Hello
world
cheers!

Denken Sie daran, den neuen Funktionsstatus zu speichern, wenn Sie mehrere yields verwenden:

func co_func():
    for i in range(1, 5):
        print("Turn %d" % i)
        yield();


func _ready():
    var co = co_func();
    while co is GDScriptFunctionState && co.is_valid():
        co = co.resume();

Co-Routinen & Signale

Die wahre Stärke der Verwendung von yield liegt in der Kombination mit Signalen. yield kann zwei Argumente akzeptieren, ein Objekt und ein Signal. Wenn das Signal empfangen wird, beginnt die Ausführung erneut. Hier sind einige Beispiele:

# Resume execution the next frame.
yield(get_tree(), "idle_frame")

# Resume execution when animation is done playing.
yield(get_node("AnimationPlayer"), "animation_finished")

# Wait 5 seconds, then resume execution.
yield(get_tree().create_timer(5.0), "timeout")

Koroutinen selbst verwenden das Signal completed, wenn sie in einen ungültigen Zustand übergehen, zum Beispiel:

func my_func():
    yield(button_func(), "completed")
    print("All buttons were pressed, hurray!")


func button_func():
    yield($Button0, "pressed")
    yield($Button1, "pressed")

my_func wird erst weiter ausgeführt, sobald beide Buttons gedrückt wurden.

Sie können das Argument des Signals auch abfragen, sobald es von einem Objekt ausgegeben wird:

# Wait for when any node is added to the scene tree.
var node = yield(get_tree(), "node_added")

Wenn mehr als ein Argument angegeben wird, gibt yield ein Array zurück, das diese Argumente enthält:

signal done(input, processed)

func process_input(input):
    print("Processing initialized")
    yield(get_tree(), "idle_frame")
    print("Waiting")
    yield(get_tree(), "idle_frame")
    emit_signal("done", input, "Processed " + input)


func _ready():
    process_input("Test") # Prints: Processing initialized
    var data = yield(self, "done") # Prints: waiting
    print(data[1]) # Prints: Processed Test

Sollten Sie sich unsicher sein, ob eine Funktion nachgibt oder nicht, oder ob sie mehrfach nachgeben kann, können Sie bedingt bis zum completed Signal nachgeben:

func generate():
    var result = rand_range(-1.0, 1.0)

    if result < 0.0:
        yield(get_tree(), "idle_frame")

    return result


func make():
    var result = generate()

    if result is GDScriptFunctionState: # Still working.
        result = yield(result, "completed")

    return result

Dies stellt sicher, dass die Funktion alles zurückgibt, was sie zurückgeben sollte, unabhängig davon, ob Coroutinen intern verwendet wurden. Beachten Sie, dass die Verwendung von während hier redundant wäre, da das Signal abgeschlossen nur ausgegeben wird, wenn die Funktion nicht mehr nachgibt.

onready Schlüsselwort

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 Unterknoten nur abgerufen werden, wenn ein Aufruf von "Node._ready()" erfolgt.

var my_label


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

Dies kann etwas umständlich werden, insbesondere wenn sich Nodes und externe Referenzen häufen. Zu diesem Zweck hat GDScript das Schlüsselwort "onready", das die Initialisierung einer Mitgliedsvariablen verzögert, bis "_ready()" "aufgerufen wird. Es kann den obigen Code durch eine einzelne Zeile ersetzen :

onready var my_label = get_node("MyLabel")

Assert Schlüsselwort

Das behaupten Schlüsselwort kann verwendet werden, um Bedingungen im Debug-Builds zu überprüfen. Diese Behauptungen werden in Nicht-Debug-Builds ignoriert. Dies bedeutet, dass der als Argument übergebene Ausdruck in einem Release-Modus exportierten Projekt nicht ausgewertet wird. Aus diesem Grund dürfen Behauptungen keine Ausdrücke enthalten, die Nebenwirkungen 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 Behauptung Fehler auftritt.