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 |
continue |
Springt sofort zur nächsten Iteration der |
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 |
|
Indexierung (höchste Priorität) |
|
Referenzierung eines Attributs |
|
Funktionsaufruf |
|
Prüfung des Instanztyps |
|
Bitweise Negation |
|
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 |
|
When used with the |
|
Boolesches NICHT |
|
Boolesches UND |
|
Boolesches ODER |
|
Bedingte (ternäre) if/else Anweisung |
|
Typumwandlung |
|
Zuweisung (niedrigste Priorität) |
Literale¶
Literal |
Typ |
|
Dezimale Ganzzahl (Basis 10) |
|
Basis 16 (Hexadezimal) Ganzzahl |
|
Basis 2 (Binär) Ganzzahl |
|
Fließkommazahl (reell) |
|
Zeichenketten |
|
Mehrzeilige Zeichenkette |
|
NodePath oder StringName |
|
Kurzform für |
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.
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 Array
s 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 |
|
Zeilenumbruch (Line Feed) |
|
Horizontaler Tabulator |
|
Wagenrücklauf (Carriage Return) |
|
Alarm (Piepton/Klingel) |
|
Rücktaste |
|
neue Seite (Page Break) |
|
Vertikaler Tabulator |
|
Doppelte Anführungszeichen |
|
Einfaches Anführungszeichen |
|
Backslash (Rückstrich oder umgekehrter Schrägstrich) |
|
Unicode Codepoint |
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:
PoolByteArray: Ein Array aus Bytes (Integer von 0 bis 255).
PoolIntArray: Ein Array aus ganzen Zahlen.
PoolRealArray: Ein Array aus Fließkommazahlen.
PoolStringArray: Ein Array aus Zeichenketten.
PoolVector2Array: Ein Array aus Vector2 Objekten.
PoolVector3Array: Ein Array aus Vector3 Objekten.
PoolColorArray: Ein Array aus Color Objekten.
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 manconst MyScript = preload("res://my_script.gd")
deklariert hat).Andere Klassen im gleichen Skript, unter Berücksichtigung des Geltungsbereichs (
InnerClass.NestedClass
, wenn manclass NestedClass
innerhalb derclass 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:
Ersetze
switch
mitmatch
.Entferne
case
.Entfernt jedes
break
s. Sollte das standardmäßigebreak
ungewünscht sein, so kann durchcontinue
ein Fortfahren erzielt werden.Ä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 einerswitch
-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"
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:
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
vonState.gd
übergeben.Idle.gd
kann eine andere Anzahl an Argumenten besitzen als die BasisklasseState.gd
.Im obigen Beispiel ist das
e
im Konstruktor vonState.gd
das gleiche wie dase
inIdle.gd
.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.
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 yield
s 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.
Kommentare¶
Alles ab einem
#
bis zum Ende der Zeile wird ignoriert und als Kommentar aufgefasst.# This is a comment.