Statische Typisierung in GDScript

In dieser Anleitung geht es um:

  • Wie Typen in GDSkript benutzt werden

  • Diese statische Typen helfen Fehler zu vermeiden

Wo und wie Sie diese neue Sprachfunktion verwenden, liegt ganz bei Ihnen: Sie können sie nur in einigen vertraulichen GDScript-Dateien verwenden, überall verwenden oder Code schreiben, wie Sie es immer getan haben!

Statische Typen können für Variablen, Konstanten, Funktionen, Parameter und Rückgabetypen verwendet werden.

Bemerkung

Typisiertes GDScript ist verfügbar seit Version 3.1.

Ein kurzer Überblick über statische Typisierung

Mit typisiertem GDScript kann Godot noch mehr Fehler erkennen, während Sie den Code schreiben! Sie und Ihre Teamkollegen erhalten während der Arbeit mehr Informationen, da die Argumenttypen beim Aufrufen einer Methode angezeigt werden.

Stellen Sie sich vor, Sie programmieren ein Inventarsystem. Sie codieren einen "Item" -Node und dann ein "Inventar". Um Artikel zum Inventar hinzuzufügen, sollten die Personen, die mit Ihrem Code arbeiten, immer einen Artikel an die Methode "Inventory.add" übergeben. Mit Typen können Sie Folgendes erzwingen:

# In 'Item.gd'.
class_name Item
# In 'Inventory.gd'.
class_name Inventory


func add(reference: Item, amount: int = 1):
    var item = find_item(reference)
    if not item:
        item = _instance_item_from_db(reference)

    item.amount += amount

Ein weiterer wesentlicher Vorteil von typisiertem GDScript ist das neue Warnsystem. Ab Version 3.1 gibt Godot beim Schreiben Warnungen zu Ihrem Code aus: Die Engine identifiziert Abschnitte Ihres Codes, die zur Laufzeit zu Problemen führen können. Sie können jedoch entscheiden, ob Sie den Code unverändert lassen möchten oder nicht. Mehr dazu gleich.

Statische Typen bieten Ihnen auch bessere Optionen für die Code-Vervollständigung. Unten sehen Sie den Unterschied zwischen einer dynamischen und einer statisch typisierten Abschlussoption für eine Klasse namens "PlayerController".

Sie haben wahrscheinlich schon einmal einen Node in einer Variablen gespeichert und einen Punkt eingegeben, der keine Vorschläge für die automatische Vervollständigung anzeigte:

code completion options for dynamic

Dies ist auf dynamischen Code zurückzuführen. Godot kann nicht wissen, welchen Node oder Werttyp Sie an die Funktion übergeben. Wenn Sie den Typ jedoch explizit schreiben, erhalten Sie alle öffentlichen Methoden und Variablen vom Node:

code completion options for typed

Typisiertes GDScript wird in der Zukunft auch die Codeleistung steigern: Just-In-Time-Kompilierung und andere Compiler-Verbesserungen stehen bereits auf der Roadmap!

Insgesamt bietet die typisierte Programmierung eine strukturiertere Erfahrung. Es hilft, Fehler zu vermeiden und verbessert den selbstdokumentierenden Aspekt Ihrer Skripte. Dies ist besonders hilfreich, wenn Sie in einem Team oder an einem langfristigen Projekt arbeiten: Studien haben gezeigt, dass Entwickler die meiste Zeit damit verbringen, den Code anderer Leute oder Skripte zu lesen, die sie in der Vergangenheit geschrieben und vergessen haben. Je klarer und strukturierter der Code ist, desto schneller ist er zu verstehen, desto schneller können Sie vorankommen.

Wie statische Typisierung genutzt wird

Um den Typ einer Variablen oder einer Konstante zu definieren, schreiben Sie einen Doppelpunkt nach dem Namen der Variablen, gefolgt von ihrem Typ. Z.B. var health: int. Dies zwingt den Variablentyp dazu, immer gleich zu bleiben:

var damage: float = 10.5
const MOVE_SPEED: float = 50.0

Godot wird versuchen, Typen abzuleiten, wenn Sie einen Doppelpunkt schreiben, aber Sie lassen den Typ weg:

var life_points := 4
var damage := 10.5
var motion := Vector2()

Aktuell können drei Arten von Typen verwendet werden:

  1. Built-in

  2. Kernklassen und Nodes (Objekt, Node, Area2D, Camera2D usw.)

  3. Your own custom classes. Look at the new class_name feature to register types in the editor.

Bemerkung

Sie müssen keine Typhinweise für Konstanten schreiben, da Godot diese automatisch anhand des zugewiesenen Werts festlegt. Sie können dies dennoch tun, um die Absicht Ihres Codes klarer zu machen.

Eigene Variablentypen

Sie können jede Klasse, einschließlich Ihrer benutzerdefinierten Klassen, als Typen verwenden. Es gibt zwei Möglichkeiten sie in Skripten zu verwenden. Die erste Methode besteht darin, das Skript, das Sie als Typ in eine Konstante verwenden möchten, vorzuladen:

const Rifle = preload("res://player/weapons/Rifle.gd")
var my_rifle: Rifle

Die zweite Methode besteht darin, beim Erstellen das Schlüsselwort class_name zu verwenden. Für das obige Beispiel würde Ihr Rifle.gd folgendermaßen aussehen:

class_name Rifle
extends Node2D

Wenn Sie class_name verwenden, registriert Godot den Rifle-Typ global im Editor und Sie können ihn überall verwenden, ohne ihn in eine Konstante vorladen zu müssen:

var my_rifle: Rifle

Variablenumwandlung

Typumwandlung ist ein Schlüsselkonzept in typisierten Sprachen. Casting ist die Konvertierung eines Werts von einem Typ in einen anderen.

Stellen Sie sich einen Feind in Ihrem Spiel vor, der Area2D erweitert. Sie möchten, dass es mit dem Player kollidiert, einem KinematicBody2D mit einem Skript namens PlayerController. Sie verwenden das Signal on_body_entered, um die Kollision zu erkennen. Mit eingegebenem Code wird der Körper, den Sie erkennen, ein generischer PhysicsBody2D sein und nicht Ihr PlayerController beim _on_body_entered Rückruf.

Sie können überprüfen, ob dieses PhysicsBody2D Ihr Player mit dem Casting-Schlüsselwort as ist, und den Doppelpunkt : erneut verwenden, um die Variable zur Verwendung dieses Typs zu zwingen. Dies zwingt die Variable, sich an den Typ PlayerController zu halten:

func _on_body_entered(body: PhysicsBody2D) -> void:
    var player := body as PlayerController
    if not player:
        return

    player.damage()

Da wir mit einem benutzerdefinierten Typ zu tun haben, falls body den PlayerController nicht erweitert, wird die Variable player auf null gesetzt. Wir können dies verwenden um zu überprüfen, ob der Körper der Spieler ist oder nicht. Dank des cast erhalten wir auch eine komplette automatische Vervollständigung der Spielervariablen.

Bemerkung

Wenn Sie versuchen mit einem integrierten Typ zu casten und dies fehlschlägt, gibt Godot einen Fehler aus.

Sichere Zeilen

Sie können auch casting verwenden, um sichere Zeilen zu gewährleisten. Sichere Zeilen sind ein neues Tool in Godot 3.1, mit dem Sie feststellen können, wann mehrdeutige Codezeilen typsicher sind. Da Sie typisierten und dynamischen Code mischen und abgleichen können, verfügt Godot manchmal nicht über genügend Informationen um zu wissen, ob eine Anweisung zur Laufzeit einen Fehler auslöst oder nicht.

Dies geschieht, wenn Sie einen untergeordneten Node erhalten. Nehmen wir zum Beispiel einen Timer: Mit dynamischem Code können Sie den Node mit $Timer erhalten. GDScript unterstützt Duck-Typing, so das selbst wenn Ihr Timer vom Typ Timer ist, so ist er auch ein Node und ein Object, er erweitert zwei Klassen. Mit dynamischem GDScript kann Ihnen auch der Node-typ egal sein, solange er über die Methoden verfügt, die Sie aufrufen müssen.

Sie können Casting verwenden um Godot den zu erwartenden Typ mitzuteilen, wenn Sie einen Node erhalten: ($Timer als Timer), ($Player als KinematicBody2D) usw. Godot stellt die Funktionalität des Typs sicher, und wenn ja wird die Zeilennummer links im Skripteditor grün.

Unsafe vs Safe Line

Unsichere Zeile (Zeile 7) im Vergleich zu sicheren Zeilen (Zeile 6 und 8)

Bemerkung

Sie können sichere Zeilen deaktivieren oder ihre Farbe in den Editoreinstellungen ändern.

Definieren Sie den Rückgabetyp einer Funktion mit dem Pfeil ->

Um den Rückgabetyp einer Funktion zu definieren, schreiben Sie nach ihrer Deklaration einen Bindestrich und eine rechtwinklige Klammer ->, gefolgt vom Rückgabetyp:

func _process(delta: float) -> void:
    pass

Der Typ void bedeutet, dass die Funktion nichts zurückgibt. Sie können wie bei Variablen einen beliebigen Typ verwenden:

func hit(damage: float) -> bool:
    health_points -= damage
    return health_points <= 0

Sie können auch Ihre eigenen Nodes als Rückgabetypen verwenden:

# Inventory.gd

# Adds an item to the inventory and returns it.
func add(reference: Item, amount: int) -> Item:
    var item: Item = find_item(reference)
    if not item:
        item = ItemDatabase.get_instance(reference)

    item.amount += amount
    return item

Typisiert oder dynamisch: Halten Sie sich an einen Stil

Typed GDScript and dynamic GDScript can coexist in the same project. But it's recommended to stick to either style for consistency in your codebase, and for your peers. It's easier for everyone to work together if you follow the same guidelines, and faster to read and understand other people's code.

Typisierter Code erfordert etwas mehr Schreibaufwand, aber Sie erhalten die oben beschriebenen Vorteile. Hier ist ein Beispiel für dasselbe leere Skript in einem dynamischen Stil:

extends Node


func _ready():
    pass


func _process(delta):
    pass

Und mit statischer Typisierung:

extends Node


func _ready() -> void:
    pass


func _process(delta: float) -> void:
    pass

Wie Sie sehen, können Sie auch Typen mit den virtuellen Methoden der Engine verwenden. Signalrückrufe können wie alle Methoden auch Typen verwenden. Hier ist ein body_entered Signal im dynamischen Stil:

func _on_Area2D_body_entered(body):
    pass

Und der gleiche Aufruf mit Typhinweisen:

func _on_area_entered(area: CollisionObject2D) -> void:
    pass

Sie können dies jederzeit ersetzen, z.B. CollisionObject2D mit Ihrem eigenen Typ, um Parameter automatisch umzuwandeln:

func _on_area_entered(bullet: Bullet) -> void:
    if not bullet:
        return

    take_damage(bullet.damage)

Die Variable bullet könnte hier jedes CollisionObject2D enthalten, aber wir stellen sicher, dass es sich um unser Bullet handelt, einen für unser Projekt erstellten Node. Wenn es sich um etwas anderes handelt, z.B. ein Area2D oder einen Node, der Bullet nicht erweitert, ist die Variable bullet null.

Warnsystem

Bemerkung

Die Dokumentation zum GDScript-Warnsystem wurde verschoben nach GDScript Warnungs System.

Fälle in denen man keine Typen spezifizieren kann

Lassen Sie uns zum Abschluss dieser Einführung einige Fälle behandeln, in denen Sie keine Typhinweise verwenden können. Alle folgenden Beispiele lösen Fehler aus.

Man kann Aufzählungen nicht als Typen verwenden:

enum MoveDirection {UP, DOWN, LEFT, RIGHT}
var current_direction: MoveDirection

Sie können den Typ einzelner Mitglieder in einem Array nicht angeben. Dies erzeugt einen Fehler:

var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy]

Sie können die Zuweisung von Typen in einer for-Schleife nicht erzwingen, da jedes Element, das diese for-Schleife durchläuft, bereits einen anderen Typ hat. Sie können also nicht schreiben:

var names = ["John", "Marta", "Samantha", "Jimmy"]
for name: String in names:
    pass

Zwei Skripte können nicht zyklisch voneinander abhängen:

# Player.gd

extends Area2D
class_name Player


var rifle: Rifle
# Rifle.gd

extends Area2D
class_name Rifle


var player: Player

Zusammenfassung

Typisiertes GDScript ist ein leistungsstarkes Tool. Ab Version 3.1 von Godot verfügbar, können Sie strukturierteren Code schreiben, häufige Fehler vermeiden und skalierbare Systeme erstellen. In Zukunft werden statische Typen dank anstehender Compiler-Optimierungen auch einen schönen Leistungsschub bringen.