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 getipptem 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 getippte 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``, `` Knoten``, `` Area2D``, `` Camera2D`` usw.)
  3. Deine eigenen, benutzerdefinierten Klassen. Schau doch die neue Funktion an: ref: class_name <doc_scripting_continued_class_name>, um Typen im Editor zu registrieren.

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 Variablen-Typen

Du kannst jede Klasse, einschließlich deiner benutzerdefinierten Klassen, als Typen verwenden. Es gibt zwei Möglichkeiten, sie in Skripten zu verwenden. Die erste Methode besteht darin, das Skript, das du 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:

extends Node2D
class_name Rifle

Wenn du `` class_name`` verwendest, registriert Godot den Rifle-Typ global im Editor und du kannst 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.

This happens when you get a child node. Let's take a timer for example: with dynamic code, you can get the node with $Timer. GDScript supports duck-typing, so even if your timer is of type Timer, it is also a Node and an Object, two classes it extends. With dynamic GDScript, you also don't care about the node's type as long as it has the methods you need to call.

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

Typisiertes GDScript und dynamisches GDScript können im selben Projekt koexistieren. Ich empfehle jedoch sich aus Gründen der Konsistenz in Ihrer Codebasis und für Ihre Kollegen an beide Stile zu halten. Es ist für alle einfacher zusammenzuarbeiten, wenn Sie dieselben Richtlinien befolgen und den Code anderer schneller lesen und verstehen können.

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)

The bullet variable could hold any CollisionObject2D here, but we make sure it is our Bullet, a node we created for our project. If it's anything else, like an Area2D, or any node that doesn't extend Bullet, the bullet variable will be 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.