Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

GDScript Style Guide

Dieser Style Guide listet Konventionen auf, um elegantes GDScript zu schreiben. Er soll zu sauberem, lesbarem Code ermutigen und Konsistenz in Projekten, Diskussionen und Tutorials fördern. Dies wird hoffentlich auch zur Entwicklung automatisierter Formatierungs-Tools beitragen.

Da GDScript viele Gemeinsamkeiten mit Python aufweist, sind die Empfehlungen an Pythons`PEP 8 <https://www.python.org/dev/peps/pep-0008/>`__ Style Guide angelehnt.

Style Guides sind nicht als dogmatisch einzuhaltende Regelwerke zu verstehen. Einige der folgenden Richtlinien sind möglicherweise nicht stets anwendbar. In diesem Fall sollte nach bestem Wissen und Gewissen gehandelt und ggf. Erfahrungswerte bei anderen Entwicklern eingeholt werden.

Im Allgemeinen ist es wichtiger, Ihren Code in Ihren Projekten und in Ihrem Team konsistent zu halten, als dieser Anleitung blind zu folgen.

Bemerkung

Godot's integrierter Skripteditor verwendet standardmäßig einige dieser Konventionen.

Hier ist ein vollständiges Klassenbeispiel basierend auf diesen Richtlinien:

class_name StateMachine
extends Node
## Hierarchical State machine for the player.
##
## Initializes states and delegates engine callbacks ([method Node._physics_process],
## [method Node._unhandled_input]) to the state.

signal state_changed(previous, new)

@export var initial_state: Node
var is_active = true:
    set = set_is_active

@onready var _state = initial_state:
    set = set_state
@onready var _state_name = _state.name


func _init():
    add_to_group("state_machine")


func _enter_tree():
    print("this happens before the ready method!")


func _ready():
    state_changed.connect(_on_state_changed)
    _state.enter()


func _unhandled_input(event):
    _state.unhandled_input(event)


func _physics_process(delta):
    _state.physics_process(delta)


func transition_to(target_state_path, msg={}):
    if not has_node(target_state_path):
        return

    var target_state = get_node(target_state_path)
    assert(target_state.is_composite == false)

    _state.exit()
    self._state = target_state
    _state.enter(msg)
    Events.player_state_changed.emit(_state.name)


func set_is_active(value):
    is_active = value
    set_physics_process(value)
    set_process_unhandled_input(value)
    set_block_signals(not value)


func set_state(value):
    _state = value
    _state_name = _state.name


func _on_state_changed(previous, new):
    print("state changed")
    state_changed.emit()


class State:
    var foo = 0

    func _init():
        print("Hello!")

Formatierung

Encoding und Sonderzeichen

  • Verwenden Sie Line Feed (LF) für Zeilenumbrüche, nicht CRLF oder CR. (Default im Editor)

  • Verwenden Sie ein Line Feed am Ende jeder Datei. (Default im Editor)

  • Verwenden Sie die UTF-8-Encoding ohne ein byte order mark. (Default im Editor)

  • Verwenden Sie Tabs anstelle von Leerzeichen für Einrückungen. (Default im Editor)

Einrückung

Jede Einrückungsebene sollte um eins größer sein als die des umgebenden Blocks.

Gut:

for i in range(10):
    print("hello")

Schlecht:

for i in range(10):
  print("hello")

for i in range(10):
        print("hello")

Verwenden Sie 2 Einrückungsebenen, um Fortsetzungszeilen von normalen Codeblöcken zu unterscheiden.

Gut:

effect.interpolate_property(sprite, "transform/scale",
        sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
        Tween.TRANS_QUAD, Tween.EASE_OUT)

Schlecht:

effect.interpolate_property(sprite, "transform/scale",
    sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
    Tween.TRANS_QUAD, Tween.EASE_OUT)

Ausnahmen von dieser Regel sind Arrays, Dictionarys und Enums. Verwenden Sie eine einzelne Einrückungsstufe, um fortgesetzte Zeilen zu unterscheiden:

Gut:

var party = [
    "Godot",
    "Godette",
    "Steve",
]

var character_dict = {
    "Name": "Bob",
    "Age": 27,
    "Job": "Mechanic",
}

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT,
}

Schlecht:

var party = [
        "Godot",
        "Godette",
        "Steve",
]

var character_dict = {
        "Name": "Bob",
        "Age": 27,
        "Job": "Mechanic",
}

enum Tiles {
        TILE_BRICK,
        TILE_FLOOR,
        TILE_SPIKE,
        TILE_TELEPORT,
}

Nachgestelltes Komma

Verwenden Sie in Arrays, Dictionarys und Enums in der letzten Zeile ein nachfolgendes Komma. Dies erleichtert das Refactoring und verbessert Diffs in der Versionsverwaltung, da die letzte Zeile beim Hinzufügen neuer Elemente nicht geändert werden muss.

Gut:

var array = [
    1,
    2,
    3,
]

Schlecht:

var array = [
    1,
    2,
    3
]

Nachgestellte Kommas sind in einzeiligen Listen nicht erforderlich. Fügen Sie sie in diesem Fall also nicht hinzu.

Gut:

var array = [1, 2, 3]

Schlecht:

var array = [1, 2, 3,]

Leerzeilen

Umgeben Sie Funktionen und Klassendefinitionen mit zwei Leerzeilen:

func heal(amount):
    health += amount
    health = min(health, max_health)
    health_changed.emit(health)


func take_damage(amount, effect=null):
    health -= amount
    health = max(0, health)
    health_changed.emit(health)

Verwenden Sie innerhalb von Funktionen eine Leerzeile, um logische Abschnitte zu trennen.

Bemerkung

Wir verwenden eine einzelne Zeile zwischen Klassen- und Funktionsdefinitionen in der Klassenreferenz und in kurzen Codeschnipseln in dieser Dokumentation.

Zeilenlänge

Halten Sie einzelne Codezeilen unter 100 Zeichen.

Wenn Sie können, versuchen Sie, Zeilen unter 80 Zeichen zu halten. Dies hilft beim Lesen des Codes auf kleinen Displays und mit zwei nebeneinander geöffneten Skripten in einem externen Texteditor. Zum Beispiel bei der Betrachtung einer Datei in einer Differenzansicht.

Eine Anweisung pro Zeile

Vermeiden Sie es, mehrere Anweisungen in einer einzigen Zeile zu kombinieren, einschließlich bedingter Anweisungen, um die GDScript-Lesbarkeitsrichtlinien einzuhalten.

Gut:

if position.x > width:
    position.x = 0

if flag:
    print("flagged")

Schlecht:

if position.x > width: position.x = 0

if flag: print("flagged")

Die einzige Ausnahme dieser Regel ist der tenäre Operator:

next_state = "idle" if is_on_floor() else "fall"

Formatieren Sie mehrzeilige Anweisungen zur besseren Lesbarkeit

Wenn Sie besonders lange if-Anweisungen oder verschachtelte ternäre Ausdrücke haben, verbessert es die Lesbarkeit, wenn Sie diese über mehrere Zeilen umbrechen. Da Fortsetzungszeilen immer noch Teil desselben Ausdrucks sind, sollten 2 Einrückungsebenen statt einer verwendet werden.

GDScript erlaubt den Umbruch von Anweisungen über mehrere Zeilen mit Hilfe von Klammern oder Backslashes. Klammern werden in diesem Styleguide bevorzugt, da sie eine einfachere Umstrukturierung ermöglichen. Bei Backslashes müssen Sie sicherstellen, dass die letzte Zeile niemals einen Backslash am Ende enthält. Mit Klammern müssen Sie sich keine Sorgen machen, dass die letzte Zeile am Ende einen Backslash hat.

Wenn ein bedingter Ausdruck über mehrere Zeilen umgebrochen wird, sollten die Schlüsselwörter and/or am Anfang der Zeilenfortsetzung stehen, nicht am Ende der vorherigen Zeile.

Gut:

var angle_degrees = 135
var quadrant = (
        "northeast" if angle_degrees <= 90
        else "southeast" if angle_degrees <= 180
        else "southwest" if angle_degrees <= 270
        else "northwest"
)

var position = Vector2(250, 350)
if (
        position.x > 200 and position.x < 400
        and position.y > 300 and position.y < 400
):
    pass

Schlecht:

var angle_degrees = 135
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"

var position = Vector2(250, 350)
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
    pass

Vermeiden Sie unnötige Klammern

Vermeiden Sie Klammern in Ausdrücken und bedingten Anweisungen. Sofern sie nicht für die Reihenfolge der Operationen oder den Umbruch über mehrere Zeilen erforderlich sind, verringern sie nur die Lesbarkeit.

Gut:

if is_colliding():
    queue_free()

Schlecht:

if (is_colliding()):
    queue_free()

Boolesche Operatoren

Benutze wenn möglich die englische Version der Booleschen Operatoren, da diese verständlicher sind:

  • Verwenden Sie and anstelle von &&.

  • Verwenden Sie or anstelle von ||.

  • Verwenden Sie not anstelle von !.

Sie können auch boolesche Operatoren in Klammern setzen, um Mehrdeutigkeiten zu vermeiden. Dies kann lange Ausdrücke leichter lesbar machen.

Gut:

if (foo and bar) or not baz:
    print("condition is true")

Schlecht:

if foo && bar || !baz:
    print("condition is true")

Leerzeichen bei Kommentaren

Reguläre Kommentare (#) und Dokumentationskommentare (##) sollten mit einem Leerzeichen beginnen, aber nicht der Code, den Sie auskommentieren. Außerdem müssen Code-Region-Kommentare (#region/#endregion) genau dieser Syntax folgen, sie sollten also nicht mit einem Leerzeichen beginnen.

Die Verwendung eines Leerzeichens für reguläre und Dokumentationskommentare hilft, Textkommentare von deaktiviertem Code zu unterscheiden.

Gut:

# This is a comment.
#print("This is disabled code")

Schlecht:

#This is a comment.
# print("This is disabled code")

Bemerkung

Im Skript-Editor können Sie die Kommentierung des ausgewählten Codes ein- und ausschalten, indem Sie Strg + K drücken. Diese Funktion fügt ein einzelnes #-Zeichen vor jedem Code in den ausgewählten Zeilen ein oder entfernt es.

Leerzeichen

Verwenden Sie immer ein Leerzeichen um Operatoren und nach Kommas. Vermeiden Sie auch zusätzliche Leerzeichen in Dictonary-Referenzen und Funktionsaufrufen. Eine Ausnahme bilden einzeilige Dictionary-Deklarationen, bei denen nach der öffnenden Klammer und vor der schließenden Klammer ein Leerzeichen eingefügt werden sollte. Dies erleichtert die visuelle Unterscheidung des Wörterbuchs von einem Array, da die [] Zeichen in den meisten Schriftarten ähnlich wie {} aussehen.

Gut:

position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
my_dictionary = { key = "value" }
print("foo")

Schlecht:

position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
my_dictionary = {key = "value"}
print ("foo")

Verwenden Sie keine Leerzeichen, um Ausdrücke vertikal auszurichten:

x        = 100
y        = 100
velocity = 500

Anführungszeichen

Verwenden Sie doppelte Anführungszeichen, es sei denn, einfache Anführungszeichen machen es möglich, Escaping von Zeichen in einem gegebenen String zu vermeiden. Siehe die Beispiele unten:

# Normal string.
print("hello world")

# Use double quotes as usual to avoid escapes.
print("hello 'world'")

# Use single quotes as an exception to the rule to avoid escapes.
print('hello "world"')

# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")

Zahlen

Lassen Sie die führende oder nachgestellte Null in Float-Zahlen nicht weg. Andernfalls sind sie schwerer lesbar und auf einen Blick schwerer von Integer-Zahlen zu unterscheiden.

Gut:

var float_number = 0.234
var other_float_number = 13.0

Schlecht:

var float_number = .234
var other_float_number = 13.

Verwenden Sie Kleinschreibung für Buchstaben in hexadezimalen Zahlen, da ihre geringere Höhe die Lesbarkeit der Zahl erleichtert.

Gut:

var hex_number = 0xfb8c0b

Schlecht:

var hex_number = 0xFB8C0B

Nutzen Sie in GDScript Unterstriche in Literalen, damit große Zahlen lesbarer werden.

Gut:

var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12345

Schlecht:

var large_number = 1234567890
var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345

Namenskonventionen

These naming conventions follow the Godot Engine style. Breaking these will make your code clash with the built-in naming conventions, leading to inconsistent code. As a summary table:

Typ

Convention

Beispiel

Dateinamen

snake_case

yaml_parser.gd

Class names

PascalCase

class_name YAMLParser

Node names

PascalCase

Camera3D, Player

Funktionen

snake_case

func load_level():

Variablen

snake_case

var particle_effect

Signale

snake_case

signal door_opened

Konstanten

CONSTANT_CASE

const MAX_SPEED = 200

Enum names

PascalCase

enum Element

Enum members

CONSTANT_CASE

{EARTH, WATER, AIR, FIRE}

Dateinamen

Verwenden Sie snake_case für Dateinamen. Für benannte Klassen konvertieren Sie den PascalCase-Klassennamen in snake_case:

# This file should be saved as `weapon.gd`.
class_name Weapon
extends Node
# This file should be saved as `yaml_parser.gd`.
class_name YAMLParser
extends Object

Dies ist konsistent mit der Benennung von C++-Dateien im Godot Quellcode. Dadurch werden auch Probleme mit der Groß- und Kleinschreibung vermieden, die beim Exportieren eines Projekts von Windows auf andere Plattformen auftreten können.

Klassen und Nodes

Verwenden Sie PascalCase für Klassen- und Node-Namen:

extends CharacterBody3D

Verwenden Sie auch PascalCase, wenn Sie eine Klasse in eine Konstante oder Variable laden:

const Weapon = preload("res://weapon.gd")

Funktionen und Variablen

Verwenden Sie snake_case, um Funktionen und Variablen zu benennen:

var particle_effect
func load_level():

Stellen Sie einen einzelnen Unterstrich (_) vor virtueller Methoden, Funktionen, die der Benutzer überschreiben muss, privaten Funktionen und privaten Variablen:

var _counter = 0
func _recalculate_path():

Signale

Verwenden Sie die Vergangenheitsform, um Signale zu benennen:

signal door_opened
signal score_changed

Konstanten und Enums

Schreiben Sie Konstanten mit CONSTANT_CASE, d.h. in Großbuchstaben mit einem Unterstrich (_) zur Trennung der Wörter:

const MAX_SPEED = 200

Verwenden Sie PascalCase für Enum-Namen und CONSTANT_CASE für deren Member, da sie Konstanten sind:

enum Element {
    EARTH,
    WATER,
    AIR,
    FIRE,
}

Write enums with each item on its own line. This allows adding documentation comments above each item more easily, and also makes for cleaner diffs in version control when items are added or removed.

Gut:

enum Element {
    EARTH,
    WATER,
    AIR,
    FIRE,
}

Schlecht:

enum Element { EARTH, WATER, AIR, FIRE }

Code-Reihenfolge

This section focuses on code order. For formatting, see Formatierung. For naming conventions, see Namenskonventionen.

Wir empfehlen, GDScript-Code auf folgende Art zu organisieren:

01. @tool, @icon, @static_unload
02. class_name
03. extends
04. ## doc comment

05. signals
06. enums
07. constants
08. static variables
09. @export variables
10. remaining regular variables
11. @onready variables

12. _static_init()
13. remaining static methods
14. overridden built-in virtual methods:
    1. _init()
    2. _enter_tree()
    3. _ready()
    4. _process()
    5. _physics_process()
    6. remaining virtual methods
15. overridden custom methods
16. remaining methods
17. subclasses

And put the class methods and variables in the following order depending on their access modifiers:

1. public
2. private

Wir haben die Reihenfolge optimiert, um das Lesen des Codes von oben nach unten zu vereinfachen, Entwicklern das erste Lesen des Codes zu erleichtern und Fehler im Zusammenhang mit der Reihenfolge der Variablendeklarationen zu vermeiden.

Diese Code-Reihenfolge folgt vier Faustregeln:

  1. Propertys und Signale stehen an erster Stelle, gefolgt von Methoden.

  2. Public kommt vor Private.

  3. Virtuelle Callbacks kommen vor dem Interface der Klasse.

  4. Die Konstruktions- und Initialisierungsfunktionen des Objekts, _init und _ready, stehen vor Funktionen, die das Objekt zur Laufzeit ändern.

Klassendeklaration

Wenn der Code im Editor laufen soll, setzen Sie die Annotation @tool in die erste Zeile des Skripts.

Follow with the optional @icon then the class_name if necessary. You can turn a GDScript file into a global type in your project using class_name. For more information, see GDScript-Referenz.

Dann fügen Sie das Schlüsselwort extends hinzu, wenn die Klasse einen Built-in-Typ erweitert.

Danach sollten Sie die optionalen Dokumentationskommentare für die Klasse einfügen. Damit können Sie zum Beispiel Ihren Teamkollegen die Rolle Ihrer Klasse erklären, wie sie funktioniert und wie andere Entwickler sie verwenden sollten.

class_name MyNode
extends Node
## A brief description of the class's role and functionality.
##
## The description of the script, what it can do,
## and any further detail.

Signale und Propertys

Schreiben Sie Signaldeklarationen, gefolgt von Propertys, das heißt, Membervariablen nach dem Docstring.

Enums sollten nach Signalen kommen, da Sie sie als Exporthinweise für andere Propertys verwenden können.

Schreiben Sie dann Konstanten, exportierte Variablen, öffentliche, private und onready-Variablen in dieser Reihenfolge.

signal player_spawned(position)

enum Jobs {
    KNIGHT,
    WIZARD,
    ROGUE,
    HEALER,
    SHAMAN,
}

const MAX_LIVES = 3

@export var job: Jobs = Jobs.KNIGHT
@export var max_health = 50
@export var attack = 5

var health = max_health:
    set(new_health):
        health = new_health

var _speed = 300.0

@onready var sword = get_node("Sword")
@onready var gun = get_node("Gun")

Bemerkung

GDScript wertet @onready-Variablen direkt vor dem _ready-Callback aus. Sie können dies verwenden, um Knotenabhängigkeiten zwischenzuspeichern, das heißt, um untergeordnete Knoten in der Szene abzurufen, auf die sich Ihre Klasse stützt. Dies zeigt das obige Beispiel.

Membervariablen

Deklarieren Sie keine Membervariablen, wenn sie nur lokal in einer Methode verwendet werden, da dies das Lesen des Codes erschwert. Deklarieren Sie sie stattdessen als lokale Variablen in der Methode.

Lokale Variablen

Deklarieren Sie lokale Variablen so nah wie möglich am Ort ihrer ersten Verwendung. Dies erleichtert das Lesen des Codes, ohne zu viel scrollen zu müssen, um herauszufinden, wo die Variable deklariert wurde.

Methoden und statische Funktionen

Nach den Klassen-Propertys kommen die Methoden.

Beginnen Sie mit der Callback-Methode _init(), die von der Engine beim Erstellen des Objekts im Speicher aufgerufen wird. Folgen Sie dem _ready()-Callback den Godot aufruft, wenn er dem Szenenbaum einen Node hinzufügt.

Diese Funktionen sollten an erster Stelle stehen, da sie zeigen, wie das Objekt initialisiert wird.

Andere Built-in-virtuelle Callbacks wie _unhandled_input() und _physics_process sollten als nächstes folgen. Diese steuern die Hauptschleife des Objekts und die Interaktionen mit der Spiel-Engine.

Der Rest des Interfaces der Klasse, öffentliche und private Methoden, folgt danach in dieser Reihenfolge.

func _init():
    add_to_group("state_machine")


func _ready():
    state_changed.connect(_on_state_changed)
    _state.enter()


func _unhandled_input(event):
    _state.unhandled_input(event)


func transition_to(target_state_path, msg={}):
    if not has_node(target_state_path):
        return

    var target_state = get_node(target_state_path)
    assert(target_state.is_composite == false)

    _state.exit()
    self._state = target_state
    _state.enter(msg)
    Events.player_state_changed.emit(_state.name)


func _on_state_changed(previous, new):
    print("state changed")
    state_changed.emit()

Statische Typisierung

GDScript supports optional static typing.

Deklarierte Typen

Verwenden Sie <Variable>: <Typ> um den Typ einer Variablen zu deklarieren:

var health: int = 0

Um den Rückgabetyp einer Funktion zu deklarieren, verwenden Sie -> <type>:

func heal(amount: int) -> void:

Typinferenz

In den meisten Fällen können Sie den Typ vom Compiler inferieren lassen, indem Sie := verwenden. Bevorzugen Sie :=, wenn der Typ in der gleichen Zeile wie die Zuweisung steht, andernfalls schreiben Sie den Typ lieber explizit.

Gut:

var health: int = 0 # The type can be int or float, and thus should be stated explicitly.
var direction := Vector3(1, 2, 3) # The type is clearly inferred as Vector3.

Fügen Sie den Type-Hint ein, wenn der Typ mehrdeutig ist, und lassen Sie den Type-Hint weg, wenn er überflüssig ist.

Schlecht:

var health := 0 # Typed as int, but it could be that float was intended.
var direction: Vector3 = Vector3(1, 2, 3) # The type hint has redundant information.

# What type is this? It's not immediately clear to the reader, so it's bad.
var value := complex_function()

In einigen Fällen muss der Typ explizit angegeben werden, da sonst das Verhalten nicht wie erwartet ist, da der Compiler nur den Rückgabetyp der Funktion verwenden kann. Zum Beispiel kann get_node() keinen Typ ableiten, es sei denn, die Szene oder Datei des Nodes ist im Speicher geladen. In diesem Fall sollten Sie den Typ explizit festlegen.

Gut:

@onready var health_bar: ProgressBar = get_node("UI/LifeBar")

Alternativ können Sie das Schlüsselwort as verwenden, um den Rückgabetyp zu casten, und dieser Typ wird verwendet, um den Typ der Variable abzuleiten.

@onready var health_bar := get_node("UI/LifeBar") as ProgressBar
# health_bar will be typed as ProgressBar

Diese Option wird auch als mehr type-safe angesehen als die erste.

Schlecht:

# The compiler can't infer the exact type and will use Node
# instead of ProgressBar.
@onready var health_bar := get_node("UI/LifeBar")