Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Guide de style GDScript

Ce guide de style liste les standards pour écrire du GDScript élégant. Le but est d'encourager l'écriture de code propre et lisible ainsi que de promouvoir l'uniformité au travers des projets, les discussions et les tutoriels. Nous espérons que cela encouragera le développement d'outil d'auto-formatage.

Puisque GDScript ressemble à Python, ce guide est inspiré par le guide de style de programmation de Python PEP 8 .

Les guides de style ne sont pas conçus comme des livres de règles rigides. Parfois, il se peut que vous ne puissiez pas appliquer certaines des règles ci-dessous. Lorsque cela se produit, utilisez votre jugement et demandez à vos collègues développeurs de vous donner leur avis.

En général, garder votre code consistant dans vos projets et au sein de votre équipe est plus important que de suivre ce guide à la lettre.

Note

L'éditeur de script intégré dans Godot utilise beaucoup de ces standards par défaut. Laissez-le vous aider.

Voici un exemple de classe complet basé sur ces lignes directrices :

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!")

Formatage

Encodage et caractères spéciaux

  • Utilisez le saut de ligne (LF) pour terminer les lignes, et non CRLF ou CR. (éditeur par défaut)

  • Utilisez un caractère de saut de ligne à la fin de chaque fichier. (éditeur par défaut)

  • Utilisez l'encodage UTF-8 sans indicateur d'ordre d'octet. (éditeur par défaut)

  • Utilisez Tabs au lieu d'espaces pour l'indentation. (éditeur par défaut)

Indentation

Chaque niveau d'indentation doit être supérieur d'une unité au bloc qui le contient.

Bon :

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

Mauvais :

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

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

Utiliser 2 niveaux d'indentations pour distinguer les suites de lignes des blocs de code réguliers.

Bon :

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

Mauvais :

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

Les exceptions à cette règle sont les tableaux, les dictionnaires et les énumérations. Utilisez un seul niveau d'indentation pour distinguer les lignes de continuation :

Bon :

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

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

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

Mauvais :

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

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

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

Virgule de fin

Utilisez une virgule de fin de ligne sur la dernière ligne des tableaux, dictionnaires et énumérations. Ceci a pour résultat un refactoring plus facile et de meilleures différences dans le contrôle de version car la dernière ligne n'a pas besoin d'être modifiée lors de l'ajout de nouveaux éléments.

Bon :

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

Mauvais :

enum Tiles {
    TILE_BRICK,
    TILE_FLOOR,
    TILE_SPIKE,
    TILE_TELEPORT
}

Les virgules de fin de ligne sont inutiles dans les listes en une ligne, alors ne les ajoutez pas dans ce cas.

Bon :

enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}

Mauvais :

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

Lignes vides

Encadrer les fonctions et les définitions de classes par deux ligne vide :

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)

Utiliser une ligne vide à l'intérieur des fonctions pour séparer les sections logiques.

Note

Nous utilisons une seule ligne entre les classes et les définitions de fonctions dans la référence de la classe et dans les courts extraits de code de cette documentation.

Longueur de la ligne

Gardez chaque ligne de code en dessous de 100 caractères.

Si vous le pouvez, essayez de ne pas dépasser 80 caractères par ligne. Cela permet de lire le code sur de petits écrans et avec deux scripts ouverts côte à côte dans un éditeur de texte externe. Par exemple, lorsqu'il s'agit d'une révision différentielle.

Une Instruction par ligne

Ne combinez jamais plusieurs instructions sur une même ligne. Non, les programmeurs C, pas avec une seule ligne d'instruction conditionnelle.

Bon :

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

if flag:
    print("flagged")

Mauvais :

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

if flag: print("flagged")

La seule exception à cette règle est l'opérateur ternaire :

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

Formatez les instructions sur plusieurs lignes pour la lisibilité

Quand vous avez des instructions if particulièrement longues ou des expressions ternaires imbriquées, les séparer en plusieurs lignes améliore la lisibilité. Comme les lignes suivantes font parties de la même expression, 2 niveaux d'indentation devraient être utilisés au lieu d'un.

GDScript permet d'écrire des instructions sur plusieurs lignes à l'aide de parenthèses ou d'antislashs. Les parenthèses sont privilégiées dans ce guide de style car elles permettent une refactorisation plus simple. Avec les antislashs, vous devez vous assurer que la dernière ligne ne contient pas un antislash à la fin. Avec les parenthèses, vous n'avez pas à vous soucier d'un antislash à la fin de la dernière ligne.

Quand vous séparez une expression conditionnelle en plusieurs lignes, les mots-clés and/or devraient être placé en début de ligne, et non à la fin de la ligne précédente.

Bon :

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

Mauvais :

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

Évitez les parenthèses inutiles

Évitez les parenthèses dans les expressions et les déclarations conditionnelles. À moins qu'elles ne soient nécessaires pour l'ordre des opérations ou qu'elles s'étendent sur plusieurs lignes, elles ne font que réduire la lisibilité.

Bon :

if is_colliding():
    queue_free()

Mauvais :

if (is_colliding()):
    queue_free()

Opérateurs booléens

Préférez les versions en anglais des opérateurs booléens, car elles sont les plus accessibles :

  • Utilisez and au lieu de &&.

  • Utilisez or au lieu de ||.

  • Use not instead of !.

Vous pouvez également utiliser des parenthèses autour des opérateurs booléens pour éviter toute ambiguïté. Cela peut améliorer la lisibilité des longues expressions.

Bon :

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

Mauvais :

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

Espace de commentaires

Regular comments (#) and documentation comments (##) should start with a space, but not code that you comment out. Additionally, code region comments (#region/#endregion) must follow that precise syntax, so they should not start with a space.

Using a space for regular and documentation comments helps differentiate text comments from disabled code.

Bon :

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

Mauvais :

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

Note

In the script editor, to toggle the selected code commented, press Ctrl + K. This feature adds a single # sign at the start of the selected lines.

Espaces

Utiliser toujours un seul espace autour des opérateurs et après les virgules. Éviter les espaces supplémentaires dans les références de dictionnaires et les appels de fonctions.

Bon :

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

Mauvais :

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

Ne pas utiliser d'espaces pour aligner verticalement les expressions :

x        = 100
y        = 100
velocity = 500

Guillemets

Utilisez des guillemets doubles dans la mesure du possible, sauf si les guillemets simples peuvent réduire le nombre de caractères à échapper dans une chaîne de caractères donnée. Voir l'exemple ci-dessous :

# 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\"")

Nombres

N'omettez pas le zéro de tête ou de queue dans les nombres à virgule flottante. Sinon, cela les rend moins lisibles et plus difficiles à distinguer des nombres entiers d'un seul coup d'œil.

Bon :

var float_number = 0.234
var other_float_number = 13.0

Mauvais :

var float_number = .234
var other_float_number = 13.

Utilisez des minuscules pour les lettres des nombres hexadécimaux, car leur hauteur réduite rend le nombre plus facile à lire.

Bon :

var hex_number = 0xfb8c0b

Mauvais :

var hex_number = 0xFB8C0B

Profitez des underscores de GDScript dans les littéraux pour rendre les grands nombres plus lisibles.

Bon :

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

Mauvais :

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

Conventions de nommage

Ces conventions de nommage suivent le style du moteur Godot. Si vous ne les respectez pas, votre code se détachera des conventions de nommage existantes, ce qui entraînera un code incohérent.

Nom de fichier

Utilisez snake_case pour les noms de fichiers. Pour les classes nommées, convertissez le nom de la classe de PascalCase en 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

Cela correspond à la façon dont les fichiers C++ sont nommés dans le code source de Godot. Cela permet également d'éviter les problèmes de sensibilité à la casse qui peuvent survenir lors de l'exportation d'un projet de Windows vers d'autres plateformes.

Classes et Nœuds

Utiliser le PascalCase pour les noms des classes et des nœuds :

extends CharacterBody3D

Utiliser aussi le PascalCase lorsque vous chargez une classe dans une constante ou une variable :

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

Fonctions et Variables

Utiliser le snake_case pour nommer les fonctions et variables :

var particle_effect
func load_level():

Préfixer un seul trait de soulignement (_) aux méthodes virtuelles que l'usager doit redéfinir, les fonctions privées, et les variables privées :

var _counter = 0
func _recalculate_path():

Signaux

Utiliser le passé pour nommer les signaux :

signal door_opened
signal score_changed

Constantes et énumérations

Écrivez les constantes avec CONSTANT_CASE, c'est-à-dire en majuscules avec un trait de soulignement (_) pour séparer les mots :

const MAX_SPEED = 200

Utilisez PascalCase pour les énumération(enum) names et CONSTANT_CASE pour leurs membres, car ce sont des constantes :

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

Ordre du code

Cette première section porte sur l'ordre du code. Pour le formatage, voir Formatage. Pour les conventions de nommage, voir Conventions de nommage.

Nous suggérons d'organiser le code GDScript de cette manière :

01. @tool
02. class_name
03. extends
04. # docstring

05. signals
06. enums
07. constants
08. @export variables
09. public variables
10. private variables
11. @onready variables

12. optional built-in virtual _init method
13. optional built-in virtual _enter_tree() method
14. built-in virtual _ready method
15. remaining built-in virtual methods
16. public methods
17. private methods
18. subclasses

Nous avons optimisé l'ordre pour rendre le code plus simple à lire de haut en bas, afin d'aider les développeurs à comprendre comment le code fonctionne la première fois qu'ils le lisent, et pour éviter les erreurs liées à l'ordre de déclaration des variables.

Cet ordre du code suit quatre règles générales :

  1. Les propriétés et signaux viennent en premier, suivis des méthodes.

  2. Publique vient avant privé.

  3. Les rappels(callbacks) virtuels précèdent l'interface de la classe.

  4. Les fonctions de construction et d'initialisation de l'objet, _init et _ready, viennent avant les fonctions qui modifient l'objet à l'exécution.

Déclaration de classe

If the code is meant to run in the editor, place the @tool annotation on the first line of the script.

Follow with the class_name if necessary. You can turn a GDScript file into a global type in your project using this feature. For more information, see GDScript reference.

Then, add the extends keyword if the class extends a built-in type.

Following that, you should have the class's optional documentation comments. You can use that to explain the role of your class to your teammates, how it works, and how other developers should use it, for example.

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.

Signaux et propriétés

Écrivez les déclarations de signaux, suivies des propriétés, c'est-à-dire les variables membres, après la docstring.

Les énumérations doivent venir après les signaux, car vous pouvez les utiliser comme indices d’exportation pour les autres propriétés.

Ensuite, écrivez les constantes, les variables exportées puis les variables publiques, privées et onready, dans cet ordre.

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

Note

Le compilateur GDScript évalue les variables marquées onready juste avant le rappel _ready. Vous pouvez utiliser cela pour mettre en cache les dépendances des nœuds, c'est-à-dire pour obtenir des nœuds enfants dans la scène sur laquelle votre classe s'appuie. C'est ce que montre l'exemple ci-dessus.

Variables membres

Ne déclarez pas les variables membres si elles ne sont utilisées que localement dans une méthode, car cela rend le code plus difficile à suivre. Déclarez-les plutôt comme des variables locales dans le corps de la méthode.

Variables locales

Déclarez les variables locales le plus près possible de leur première utilisation. Il est ainsi plus facile de suivre le code, sans avoir à trop le faire défiler pour retrouver l'endroit où la variable a été déclarée.

Méthodes et fonctions statiques

Après les propriétés de la classe viennent les méthodes.

Commencez avec la méthode de rappel _init (), que le moteur appellera lors de la création de l'objet en mémoire. Continuez avec la méthode de rappel _ready (), que Godot appelle quand il ajoute un nœud à l'arbre des scènes.

Ces fonctions doivent apparaître en premier car elles indiquent comment l'objet est initialisé.

D'autres méthodes de rappel virtuelles intégrées, comme _unhandled_input() et _physics_process, doivent venir ensuite. Celles-ci contrôlent la boucle principale de l'objet et les interactions avec le moteur de jeu.

Le reste de l'interface de la classe, les méthodes publiques et privées, viennent ensuite dans cet ordre.

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

Typage statique

Depuis Godot 3.1, GDScript supporte le typage statique optionnel.

Types déclarés

Pour déclarer le type d'une variable, utilisez <variable>: <type> :

var health: int = 0

Pour déclarer le type de retour d'une fonction, utilisez -> <type> :

func heal(amount: int) -> void:

Types inférés

In most cases you can let the compiler infer the type, using :=. Prefer := when the type is written on the same line as the assignment, otherwise prefer writing the type explicitly.

Bon :

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.

Include the type hint when the type is ambiguous, and omit the type hint when it's redundant.

Mauvais :

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 some cases, the type must be stated explicitly, otherwise the behavior will not be as expected because the compiler will only be able to use the function's return type. For example, get_node() cannot infer a type unless the scene or file of the node is loaded in memory. In this case, you should set the type explicitly.

Bon :

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

Alternativement, vous pouvez utiliser le mot-clé as pour transtyper le type de retour, et ce type sera utilisé pour inférer le type de la variable.

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

Cette option est aussi considérée plus type-safe que la première.

Mauvais :

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