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.

Référence GDScript

GDScript est un langage de programmation de haut niveau, orienté objet, impératif et typé progressivement conçu pour Godot. Il utilise une syntaxe basée sur l'indentation similaire à des langages comme Python. Son objectif est d'être optimisé et étroitement intégré à Godot, permettant une grande flexibilité pour la création et l'intégration de contenu.

GDScript est entièrement indépendant de Python et n'est pas basé sur lui.

Historique

Note

La documentation sur l'histoire de GDScript a été déplacée dans Questions fréquentes.

Exemple de GDScript

Certaines personnes apprennent plus facilement en regardant la syntaxe. Voici donc un exemple de ce à quoi ressemble GDScript.

# Everything after "#" is a comment.
# A file is a class!

# (optional) icon to show in the editor dialogs:
@icon("res://path/to/optional/icon.svg")

# (optional) class definition:
class_name MyClass

# Inheritance:
extends BaseClass


# Member variables.
var a = 5
var s = "Hello"
var arr = [1, 2, 3]
var dict = {"key": "value", 2: 3}
var other_dict = {key = "value", other_key = 2}
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)


# Functions.
func some_function(param1, param2, param3):
    const local_const = 5

    if param1 < local_const:
        print(param1)
    elif param2 > 5:
        print(param2)
    else:
        print("Fail!")

    for i in range(20):
        print(i)

    while param2 != 0:
        param2 -= 1

    match param3:
        3:
            print("param3 is 3!")
        _:
            print("param3 is not 3!")

    var local_var = param1 + 3
    return local_var


# Functions override functions with the same name on the base/super class.
# If you still want to call them, use "super":
func something(p1, p2):
    super(p1, p2)


# It's also possible to call another function in the super class:
func other_something(p1, p2):
    super.something(p1, p2)


# Inner class
class Something:
    var a = 10


# Constructor
func _init():
    print("Constructed!")
    var lv = Something.new()
    print(lv.a)

Si vous avez déjà de l’expérience avec des langages à typage statique tels que C, C++ ou C# mais que vous n'avez jamais utilisé un langage à typage dynamique, il vous est conseillé de lire ce tutoriel : GDScript : Une introduction aux langages dynamiques.

Identifiants

Toute chaîne de caractère limitée aux caractères alphabétiques (a à z et A à Z), aux chiffres (0 à 9) et _ est un identifiant potentiel. Les identifiants ne doivent également pas commencer par un chiffre. Les identifiants sont sensibles à la casse. (toto est différent de TOTO).

Les identifiants peuvent également contenir la plupart des caractères Unicode faisant partie de UAX#31. Cela vous permet d'utiliser des noms d'identifiants écrits dans des langues autres que l'anglais. Les caractères Unicode considérés comme « pouvant prêter à confusion » avec les caractères ASCII et les émojis ne sont pas autorisés dans les identifiants.

Mots-clés

Ce qui suit est une liste de mots-clés supportés par le langage. Étant donné que les mots-clés sont des mots (jetons) réservés, ils ne peuvent êtres utilisés comme identifiants. Les opérateurs (comme in, not, and ou or) et les noms des types intégrés énumérés dans les sections suivantes sont également réservés.

Les mots-clés sont définis dans le GDScript tokenizer si vous souhaitez regarder sous le capot.

Mot-clé

Description

if

Voir if/else/elif.

elif

Voir if/else/elif.

else

Voir if/else/elif.

for

Voir for.

while

Voir while.

match

Voir match.

when

Used by pattern guards in match statements.

break

Quitte l'exécution de la boucle for ou while courante.

continue

Passe immédiatement à l'itération suivante de la boucle for ou while.

pass

Utilisé lorsqu'une instruction est requise syntaxiquement mais où l’exécution de code est indésirable, comme par exemple, dans une fonction vide.

return

Retourne une valeur à partir d'une fonction.

class

Définit une classe interne. Voir Inner classes.

class_name

Définit le script comme une classe accessible globalement avec le nom spécifié. Voir Registering named classes.

extends

Définit quelle classe étendre avec la classe courante.

is

Teste si une variable est du type d'une classe donnée, ou si elle est d'un type intégré donnée.

in

Teste si une valeur se trouve dans une chaîne, un tableau, une plage, un dictionnaire ou un nœud. Lorsqu'il est utilisé avec for, il les parcourt au lieu de les tester.

as

Convertit la valeur vers un type donné, si cela est possible.

self

Refers to current class instance. See self.

super

Resolves the scope of the parent method. See Inheritance.

signal

Defines a signal. See Signals.

func

Defines a function. See Functions.

static

Définit une fonction statique ou une variable membre statique.

const

Defines a constant. See Constants.

enum

Defines an enum. See Enums.

var

Defines a variable. See Variables.

breakpoint

Aide pour les points d'arrêt du débogueur. Contrairement aux points d'arrêt créés en cliquant dans la gouttière, breakpoint est stocké dans le script lui-même. Cela le rend persistant sur différentes machines lors de l'utilisation du contrôle de version.

preload

Précharge une classe ou une variable. Voir Classes as resources.

await

Attend la fin d'un signal ou d'une coroutine. Voir Awaiting signals or coroutines.

yield

Utilisé auparavant pour les coroutines. Conservé comme mot-clé pour le temps de la transition.

assert

Valide une condition, journalise les erreurs en cas d'échec. Est Ignoré dans les compilations autre que celles de débogages. Voir Assert keyword.

void

Utilisé pour indiquer qu'une fonction ne renvoie aucune valeur.

PI

Constante PI.

TAU

Constante TAU.

INF

Constante infinie. Utilisée pour les comparaisons et comme résultat de calculs.

NAN

Constante NAN (pas un nombre, Not A Number en anglais). Utilisée comme résultat quand les calculs sont impossibles.

Opérateurs

Voici la liste des opérateurs pris en charge et leur priorité. Tous les opérateurs binaires sont associatifs à gauche, y compris l'opérateur **. Cela signifie que 2 ** 2 ** 3 est égal à (2 ** 2) ** 3. Utilisez des parenthèses pour spécifier explicitement la priorité dont vous avez besoin, par exemple 2 ** (2 ** 3). L'opérateur ternaire if/else est associatif à droite.

Opérateur

Description

( )

Regroupement (priorité la plus élevée)

Les parenthèses ne sont pas vraiment un opérateur, mais vous permettent de spécifier explicitement la priorité d'une opération.

x[index]

Abonnement

x.attribut

Référence d'attribut

foo()

Appel de fonction

await x

Awaiting signals or coroutines

x is Node
x is not Node

Test de type

Voir aussi la fonction is_instance_of().

x ** y

Puissance

Multiplie x par lui-même y fois, de manière similaire à l'appel de la fonction pow().

~x

Opération bit-à-bit de négation

+x
-x

Identité / Négation

x * y
x / y
x % y

Multiplication / Division / Reste

L'opérateur % est également utilisé pour le formatage des chaînes.

Remarque : ces opérateurs ont le même comportement qu'en C++, ce qui peut être inattendu pour les utilisateurs venant de Python, JavaScript, etc. Voir une note détaillée après le tableau.

x + y
x - y

Ajout (ou Concaténation) / Soustraction

x << y
x >> y

Décalage de bits

x & y

Opération ET bit à bit (AND)

x ^ y

Opération OU exclusif bit-à-bit (XOR)

x | y

Opération OU bit-à-bit (OR)

x == y
x != y
x < y
x > y
x <= y
x >= y

Comparaison

Voir la note détaillée après le tableau.

x in y
x not in y

Vérification d'inclusion

in est également utilisé avec le mot-clé for en tant que partie de la syntaxe.

not x
!x

Booléen NON (NOT) et son autre notation non recommandée

x and y
x && y

Booléen ET (AND) et son autre notation non recommandée

x or y
x || y

Booléen OU (OR) et son autre notation non recommandée

true_expr if cond else false_expr

if / else ternaire

x as Node

Conversion de type<casting_>

x = y
x += y
x -= y
x *= y
x /= y
x **= y
x %= y
x &= y
x |= y
x ^= y
x <<= y
x >>= y

Affectation (priorité la plus basse)

Vous ne pouvez pas utiliser un opérateur d’affectation à l’intérieur d’une expression.

Note

Le comportement de certains opérateurs peut différer de ce que vous attendez :

  1. Si les deux opérandes de l'opérateur / sont de type int, alors la division entière est effectuée au lieu de la division réelle. Par exemple, 5 / 2 == 2, et non pas 2,5. Si cela n'est pas souhaité, utilisez au moins un float (x / 2.0), Faites une conversion de type (float(x) / y), ou multipliez par 1.0 (x * 1.0 / y).

  2. L'opérateur % n'est disponible que pour les entiers, pour les flottants, utilisez la fonction fmod().

  3. Pour les valeurs négatives, l'opérateur % et fmod() utilisent la troncature au lieu d'arrondir vers moins l'infini. Cela signifie que le reste a un signe. Si vous avez besoin du reste au sens mathématique du terme, utilisez plutôt les fonctions posmod() et fposmod().

  4. Les opérateurs == et != permettent parfois de comparer des valeurs de types différents (par exemple, 1 == 1.0 est vrai), mais dans d'autres cas, ils peuvent provoquer une erreur à l'exécution. Si vous n'êtes pas sûr des types des opérandes, vous pouvez utiliser en toute sécurité la fonction is_same() (mais notez qu'elle est plus stricte concernant les types et les références). Pour comparer des nombres à virgule flottante, utilisez plutôt les fonctions is_equal_approx() et is_zero_approx().

Littéraux

Exemple(s)

Description

null

Valeur nulle

false, true

Valeurs booléennes

45

Entier en base 10 (décimal)

0x8f51

Entier base 16 (hexadécimal)

0b101010

Entier base 2 (binaire)

3.14, 58.1e-10

Nombre à virgule flottante (réel)

"Bonjour", 'Salut'

Chaines de caractères régulières

"""Bonjour""", '''Salut'''

Chaînes régulières à triples guillemets

r"Bonjour", r'Salut'

Chaînes de caractères brutes

r"""Bonjour""", r'''Salut'''

Chaînes de caractères brutes à triple guillemets

&"name"

StringName

^"Node/Label"

NodePath

Il existe également deux constructions qui ressemblent à des littéraux, mais qui n'en sont pas réellement :

Exemple

Description

$NodePath

Abréviation de get_node("NodePath")

%UniqueNode

Abréviation de get_node("%UniqueNode")

Les nombres entiers et les nombres flottants peuvent être séparés par des _ pour les rendre plus lisibles. Les manières suivantes d'écrire les nombres sont toutes valables :

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.

Les littéraux de chaînes régulières peuvent contenir les séquences d’échappement suivantes :

Séquence d'échappement

S’étend à

\n

Nouvelle ligne (line feed)

\t

Caractère de tabulation horizontale

\r

Retour chariot

\a

Alerte (bip/cloche)

\b

Effacer

\f

Saut de page (Formfeed)

\v

Caractère de tabulation verticale

\"

Guillemet

\'

Apostrophe

\\

Antislash

\uXXXX

Entrée Unicode UTF-16 XXXX (hexadécimal, insensible à la casse)

\UXXXXXX

Entrée Unicode UTF-32 XXXXXX (hexadécimal, insensible à la casse)

Il existe deux manières de représenter un caractère d'échappement Unicode au delà de 0xFFFF :

De plus, l'utilisation de \ suivi d'un caractère de nouvelle ligne à l'intérieur d'une chaîne vous permettra de la continuer sur la ligne suivante, sans insérer de caractère de nouvelle ligne dans la chaîne elle-même.

Une chaîne entourée de guillemets d'un type (par exemple ") peut contenir des guillemets d'un autre type (par exemple ') sans échappement. Les chaînes entre guillemets triples vous permettent d'éviter d'échapper jusqu'à deux guillemets consécutifs du même type (sauf s'ils sont adjacents aux bords de la chaîne).

Les chaînes de caractères brutes encodent toujours la chaîne telle qu'elle apparaît dans le code source. Ceci est particulièrement utile pour les expressions régulières. Une chaîne brute ne traite pas les séquences d'échappement, mais il reconnaît \\ et \" (\') et les remplace par eux-mêmes. Ainsi, une chaîne peut avoir un guillemet qui correspond à celui d'ouverture, mais seulement s'il est précédé d'un antislash.

print("\tchar=\"\\t\"")  # Prints `    char="\t"`.
print(r"\tchar=\"\\t\"") # Prints `\tchar=\"\\t\"`.

Note

Certaines chaînes ne peuvent pas être représentées à l'aide de chaînes brutes : vous ne pouvez pas avoir un nombre impair d'antislash à la fin d'une chaîne ou avoir un guillemet d'ouverture sans échappement à l'intérieur de la chaîne. Cependant, en pratique, cela n'a pas d'importance puisque vous pouvez utiliser un type de guillemet différent ou utiliser la concaténation avec une chaîne de caractères régulière.

GDScript prend également en charge le formattage des chaînes de caractères.

Annotations

Les annotations sont des jetons spéciaux dans GDScript qui agissent comme des modificateurs d'un script ou de son code et peuvent affecter la façon dont le script est traité par le moteur ou l'éditeur Godot.

Chaque annotation commence par le caractère @ et est suivi par un nom. Une description détaillée et un exemple pour chaque annotation peuvent être trouvés dans la référence de classe GDScript <class_@GDScript>.

Par exemple, vous pouvez l'utiliser pour exporter une valeur vers l'éditeur :

@export_range(1, 100, 1, "or_greater")
var ranged_var: int = 50

Pour plus d'informations sur l'exportation de propriétés, consultez les exportations GDScript.

Toute expression constante compatible avec le type d'argument requis par l'annotation peut être transmise comme valeur d'argument d'annotation :

const MAX_SPEED = 120.0

@export_range(0.0, 0.5 * MAX_SPEED)
var initial_speed: float = 0.25 * MAX_SPEED

Les annotations peuvent être écrites une par ligne ou bien toutes sur la même ligne. Elles affectent l'instruction suivante qui n'est pas une annotation. Les annotations peuvent avoir des arguments passés entre parenthèses et séparés par des virgules.

Ces deux-là sont équivalentes :

@annotation_a
@annotation_b
var variable

@annotation_a @annotation_b var variable

L'annotation @onready

Lors de l'utilisation de nœuds, il est très courant de vouloir garder des références à des parties de la scène dans une variable. Comme les scènes ne peuvent être configurées que lors de l'entrée dans l'arbre des scènes actives, les sous-nœuds ne peuvent être obtenus que lorsqu'un appel à Node._ready() est fait.

var my_label


func _ready():
    my_label = get_node("MyLabel")

Cela peut devenir un peu fastidieux, surtout lorsque les nœuds et les références externes s'accumulent. Pour cela, GDScript dispose de l'annotation @onready, qui diffère l'initialisation d'une variable membre jusqu'à ce que _ready() soit appelé. Il peut remplacer le code ci-dessus par une seule ligne :

@onready var my_label = get_node("MyLabel")

Avertissement

L'application de @onready et de toute annotation @export à la même variable ne fonctionne pas comme prévu. L'annotation @onready entraînera la définition de la valeur par défaut après l'entrée en vigueur de @export et la remplacera :

@export var a = "init_value_a"
@onready @export var b = "init_value_b"

func _init():
    prints(a, b) # init_value_a <null>

func _notification(what):
    if what == NOTIFICATION_SCENE_INSTANTIATED:
        prints(a, b) # exported_value_a exported_value_b

func _ready():
    prints(a, b) # exported_value_a init_value_b

Par conséquent, l'avertissement ONREADY_WITH_EXPORT est généré et traité comme une erreur par défaut. Nous vous déconseillons de le désactiver ou de l'ignorer.

Commentaires

Tout ce qui se trouve entre un # et la fin de la ligne est ignoré et considéré comme un commentaire.

# This is a comment.

Astuce

Dans l'éditeur de script Godot, des mots-clés spéciaux sont mis en évidence dans les commentaires pour attirer l'attention de l'utilisateur sur des commentaires spécifiques :

  • Critique (apparaît en rouge) : ALERT, ATTENTION, CAUTION, CRITICAL, DANGER, SECURITY

  • Avertissement (apparaît en jaune) : BUG, DEPRECATED, FIXME, HACK, TASK, TBD, TODO, WARNING

  • Information (apparaît en vert) : INFO, NOTE, NOTICE, TEST, TESTING

Ces mots-clés sont sensibles à la casse, ils doivent donc être écrits en majuscules pour être reconnus :

# In the example below, "TODO" will appear in yellow by default.
# The `:` symbol after the keyword is not required, but it's often used.

# TODO: Add more items for the player to choose from.

La liste des mots-clés mis en surbrillance et leurs couleurs peuvent être modifiés dans la section Éditeur de texte > Thème > Marqueurs de commentaires des paramètres de l'éditeur.

Use two hash symbols (##) instead of one (#) to add a documentation comment, which will appear in the script documentation and in the inspector description of an exported variable. Documentation comments must be placed directly above a documentable item (such as a member variable), or at the top of a file. Dedicated formatting options are also available. See GDSCript Commentaires de documentation for details.

## This comment will appear in the script documentation.
var value

## This comment will appear in the inspector tooltip, and in the documentation.
@export var exported_value

Régions de code

Les régions de code sont des commentaires spéciaux que l'éditeur de scripts considère comme des régions pliables. Cela signifie qu'après avoir écrit des commentaires de région de code, vous pouvez réduire et développer la région en cliquant sur la flèche qui apparaît à gauche du commentaire. Cette flèche apparaît dans un carré violet pour se distinguer du pliage de code standard.

La syntaxe est la suivante :

# Important: There must be *no* space between the `#` and `region` or `endregion`.

# Region without a description:
#region
...
#endregion

# Region with a description:
#region Some description that is displayed even when collapsed
...
#endregion

Astuce

Pour créer rapidement une région de code, sélectionnez plusieurs lignes dans l'éditeur de script, faites un clic droit sur la sélection puis choisissez Créer une région de code. La description de la région sera automatiquement sélectionnée pour être modifiée.

Il est possible d'imbriquer des régions de code dans d'autres régions de code.

Voici un exemple concret d’utilisation des régions de code :

# This comment is outside the code region. It will be visible when collapsed.
#region Terrain generation
# This comment is inside the code region. It won't be visible when collapsed.
func generate_lakes():
    pass

func generate_hills():
    pass
#endregion

#region Terrain population
func place_vegetation():
    pass

func place_roads():
    pass
#endregion

Cela peut être utile pour organiser de gros morceaux de code en sections plus faciles à comprendre. Cependant, n'oubliez pas que les éditeurs externes ne prennent généralement pas en charge cette fonctionnalité. Assurez-vous donc que votre code est facile à suivre, même lorsque vous ne vous appuyez pas sur des zones de code pliables.

Note

Individual functions and indented sections (such as if and for) can always be collapsed in the script editor. This means you should avoid using a code region to contain a single function or indented section, as it won't bring much of a benefit. Code regions work best when they're used to group multiple elements together.

Continuation de la ligne

Une ligne de code dans GDScript peut être continuée sur la ligne suivante en utilisant un antislash (barre oblique inverse) (\). Ajoutez-en une à la fin d'une ligne et le code de la ligne suivante agira comme s'il s'agissait d'une continuation de la ligne précéndente. Voici un exemple :

var a = 1 + \
2

Une ligne peut être continuée plusieurs fois comme ceci :

var a = 1 + \
4 + \
10 + \
4

Types intégrés

Les types intégrés sont alloués sur la pile. Ils sont transmis comme valeurs. Cela signifie qu'une copie est créée à chaque affectation ou lors de leur transmission comme arguments aux fonctions. Les exceptions sont Object, Array, Dictionary et les tableaux compacts (tels que PackedByteArray), qui sont passés par référence afin d'être partagés. Tous les tableaux, Dictionary et certains objets (Node, Resource) ont une méthode duplicate() qui vous permet d'en faire une copie.

Types intégrés basiques

Une variable dans GDScript peut être affectée à plusieurs types intégrés.

null

null est une donnée vide qui ne contient aucune information et à laquelle aucune autre valeur ne peut être affectée.

Only types that inherit from Object can have a null value (Object is therefore called a "nullable" type). Variant types must have a valid value at all times, and therefore cannot have a null value.

bool

Abréviation de "booléen", il ne peut contenir que true ou false.

int

De l'anglais "integer", il contient des nombres entiers (positifs et négatifs). Ceux-ci sont stockés sous forme de valeurs 64-bits, équivalent à int64_t en C++.

float

Contient un nombre réel (avec décimales), en tant que valeur à virgule flottante. Celui-ci est stocké comme valeur 64 bits, équivalent à double en C++. Note : Actuellement, les structures de données comme Vector2, Vector3 et PackedFloat32Array stockent des valeurs 32 bits à virgules flottantes de précision unique (float).

String

Une séquence de caractères au format Unicode.

StringName

Une chaîne immuable qui n'autorise qu'une seule instance de chaque nom. Elles sont plus lentes à créer et peuvent entraîner l'attente de verrous lors du multi tâche. En échange, elles sont très rapides à comparer, ce qui en fait de bons candidats pour les clés de dictionnaire.

NodePath

Un chemin pré-analysé vers un nœud ou une propriété de nœud. Il peut être facilement assigné à et depuis une chaîne. Ils sont utiles pour interagir avec l'arbre afin d'obtenir un nœud ou d'affecter des propriétés comme avec Tweens.

Types intégrés vectoriels

Vector2

Type de vecteur 2D contenant les propriétés x et y. Peut aussi être accédé de la même façon qu'un tableau.

Vector2i

Identique à Vector2 mais les composants sont des entiers. Utile pour représenter des éléments dans une grille 2D.

Rect2

Type de rectangle 2D contenant deux propriétés de type vecteur : position et size. Contient également une propriété end qui correspond à position + size.

Vector3

Type de vecteur 3D contenant les propriétés x, y et z. Il peut également être accédé de la même façon qu'un tableau.

Vector3i

Identique à Vector3 mais les composants sont des entiers. Peut être utilisé pour indexer des éléments dans une grille 3D.

Transform2D

Matrice 3x2 utilisée pour les transformations 2D.

Plane

Plan 3D normalisé contenant un vecteur normal et une distance scalaire d.

Quaternion

Un quaternion est un type de données utilisé pour représenter une rotation 3D. Cette représentation est utile pour l'interpolation de rotations.

AABB

La boîte englobante (ou boîte 3D) alignée par axe contient 2 propriétés de type vecteur : position et size. Contient également une propriété end qui correspond à position + size.

Basis

Matrice 3x3 utilisée pour les rotations 3D et les mises à l'échelle. Elle contient 3 propriétés de type vecteur (x, y et z) et peut aussi être accédé comme un tableau de vecteurs 3D.

Transform3D

Transformation 3D contenant une propriété de type Basis basis et une propriété vecteur 3D (Vector3) origin.

Types intégrés dans le moteur

Color

Le type de données Color contient les propriétés r, g, b, et a. Il peut aussi être accédé par h, s, et v pour la teinte(hue)/saturation/valeur.

RID

Identifiant de ressource (RID). Les serveurs utilisent des RIDs génériques pour référencer des données opaques.

Object

Classe de base pour tout ce qui n'est pas un type intégré.

Types de conteneurs intégrés

Array

Séquence générique de types d'objets arbitraires, y compris d'autres tableaux ou dictionnaires (voir ci-dessous). Le tableau peut être redimensionné dynamiquement. Les tableaux sont indexés à partir de l'indice 0. Les indices négatifs comptent à partir de la fin.

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].

Tableaux typés

Godot 4.0 a ajouté la prise en charge des tableaux typés. Lors des opérations d'écriture, Godot vérifie que les valeurs des éléments correspondent au type spécifié, de sorte que le tableau ne peut pas contenir de valeurs non valides. L'analyseur statique GDScript prend en compte les tableaux typés, cependant les méthodes de tableau comme front() et back() renvoie toujours des Variant.

Les tableaux typés ont la syntaxe Array[Type], où Type peut être n'importe quel type Variant, classe native ou utilisateur, ou énumération. Les types de tableaux imbriqués (comme Array[Array[int]]) ne sont pas pris en charge.

var a: Array[int]
var b: Array[Node]
var c: Array[MyClass]
var d: Array[MyEnum]
var e: Array[Variant]

Array et Array[Variant] sont la même chose.

Note

Les tableaux sont transmis par référence, de sorte que le type d'élément du tableau est également un attribut de la structure en mémoire référencée par une variable lors de l'exécution. Le type statique d'une variable restreint les structures auxquelles elle peut faire référence. Par conséquent, vous ne pouvez pas attribuer un tableau avec un type d'élément différent, même si le type est un sous-type du type requis.

Si vous souhaitez convertir un tableau typé, vous pouvez créer un nouveau tableau et utiliser la méthode Array.assign() :

var a: Array[Node2D] = [Node2D.new()]

# (OK) You can add the value to the array because `Node2D` extends `Node`.
var b: Array[Node] = [a[0]]

# (Error) You cannot assign an `Array[Node2D]` to an `Array[Node]` variable.
b = a

# (OK) But you can use the `assign()` method instead. Unlike the `=` operator,
# the `assign()` method copies the contents of the array, not the reference.
b.assign(a)

Une seule exception a été faite pour le type Array (Array[Variant]), pour des raisons de commodité pour l'utilisateur et de compatibilité avec l'ancien code. Cependant, les opérations sur des tableaux non typés sont considérées comme dangereuses.

Tableaux compacts

PackedArrays are generally faster to iterate on and modify compared to a typed Array of the same type (e.g. PackedInt64Array versus Array[int]) and consume less memory. In the worst case, they are expected to be as fast as an untyped Array. Conversely, non-Packed Arrays (typed or not) have extra convenience methods such as Array.map that PackedArrays lack. Consult the class reference for details on the methods available. Typed Arrays are generally faster to iterate on and modify than untyped Arrays.

While all Arrays can cause memory fragmentation when they become large enough, if memory usage and performance (iteration and modification speed) is a concern and the type of data you're storing is compatible with one of the Packed Array types, then using those may yield improvements. However, if you do not have such concerns (e.g. the size of your array does not reach the tens of thousands of elements) it is likely more helpful to use regular or typed Arrays, as they provide convenience methods that can make your code easier to write and maintain (and potentially faster if your data requires such operations a lot). If the data you will store is of a known type (including your own defined classes), prefer to use a typed Array as it may yield better performance in iteration and modification compared to an untyped Array.

Dictionary

Conteneur associatif qui contient des valeurs référencées par des clés uniques.

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

La syntaxe de tableau de style Lua est également prise en charge. Le style Lua utilise = au lieu de : et n'utilise pas de guillemets pour marquer les clés de chaîne (ce qui réduit légèrement la quantité à écrire). Cependant, les clés écrites sous cette forme ne peuvent pas commencer par un chiffre (comme tout identifiant GDScript) et doivent être des chaînes de caractères.

var d = {
    test22 = "value",
    some_key = 2,
    other_key = [2, 3, 4],
    more_key = "Hello"
}

Pour ajouter une clé à un dictionnaire existant, accédez-y comme une clé existante et affectez lui une valeur :

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

Note

La syntaxe des crochets peut être utilisée pour accéder aux propriétés de n'importe quel Object, et pas seulement aux Dictionnaires. N'oubliez pas qu'elle provoquera une erreur de script lorsque vous tenterez d'indexer une propriété inexistante. Pour éviter cela, utilisez les méthodes Object.get() et Object.set() à la place.

Signal

Un signal est un message qui peut être émis par un objet à destination de ceux qui souhaitent l'écouter. Le type Signal peut être utilisé pour faire passer l'émetteur.

Les signaux sont mieux utilisés en les obtenant à partir d'objets réels, par exemple $Button.button_up.

Callable

Contient un objet et une fonction, ce qui est pratique pour transmettre des fonctions en tant que valeurs (par exemple lors de la connexion à des signaux).

Obtenir une méthode en tant que membre renvoie un callable. var x = $Sprite2D.rotate définira la valeur de x sur un callable avec $Sprite2D comme objet et rotate comme méthode.

Vous pouvez l'appeler en utilisant la méthode call : x.call(PI).

Variables

Les variables peuvent exister en tant que membres de la classe ou locales aux fonctions. Elles sont créées avec le mot-clé var et peuvent, éventuellement, se voir attribuer une valeur à l'initialisation.

var a # Data type is 'null' by default.
var b = 5
var c = 3.8
var d = b + c # Variables are always initialized in direct order (see below).

Les variables peuvent optionnellement avoir une spécification de type. Lorsqu'un type est spécifié, la variable sera obligée d'avoir toujours le même type, et essayer d'assigner une valeur incompatible entraînera une erreur.

Les types sont spécifiés dans la variable par un : après le nom de la variable, suivi par le type.

var my_vector2: Vector2
var my_node: Node = Sprite2D.new()

Si la variable est initialisée dans la déclaration, le type peut être déduit, il est donc possible de ne pas mettre le nom du type :

var my_vector2 := Vector2() # 'my_vector2' is of type 'Vector2'.
var my_node := Sprite2D.new() # 'my_node' is of type 'Sprite2D'.

L'inférence de type n'est possible que si la valeur affectée a un type défini, sinon une erreur sera générée.

Les types valides sont :

  • Types intégrés (Array, Vector2, int, String, etc.).

  • Engine classes (Node, Resource, RefCounted, etc.).

  • Les noms des constantes s'ils contiennent un script ressource (MyScript si vous avez déclaré const MyScript = preload("res://my_script.gd")).

  • D'autres classes dans le même script, respectant la portée (InnerClass.NestedClass si vous avez déclaré class NestedClass à l'intérieur de class InnerClass dans la même portée).

  • Les classes de script déclarées avec le mot-clé class_name.

  • Chargements automatiques enregistrés comme des singletons.

Note

Bien que Variant soit une spécification de type valide, ce n'est pas un vrai type. Cela signifie simplement qu'il n'y a pas de type défini et cela équivaut à ne pas avoir de type statique du tout. Par conséquent, l'inférence n'est pas autorisée par défaut pour Variant, car il s'agit probablement d'une erreur.

Vous pouvez désactiver cette vérification, ou la transformer simplement en avertissement, en la modifiant dans les paramètres du projet. Voir Système d’avertissement de GDScript pour plus de détails.

Ordre d'initialisation

Les variables membres sont initialisées dans l'ordre suivant :

  1. Selon le type statique de la variable, la variable est soit null (variables et objets non typés) ou a une valeur par défaut du type (0 pour int, false pour bool, etc.).

  2. Les valeurs spécifiées sont attribuées dans l'ordre des variables dans le script, de haut en bas.

    • (Uniquement pour les classes dérivées de Node) Si l'annotation @onready est appliquée à une variable, son initialisation est reportée à l'étape 5.

  3. Si elle est implémentée, la méthode _init() est appelée.

  4. Lors de l'instanciation de scènes et de ressources, les valeurs exportées sont attribuées.

  5. (Uniquement pour les classes dérivées de Node) Les variables @onready sont initialisées.

  6. (Uniquement pour les classes dérivées de Node) Si elle est implémentée, la méthode _ready() est appelée.

Avertissement

Vous pouvez spécifier une expression complexe pour initialiser une variable, y compris des appels de fonction. Assurez-vous que les variables sont initialisées dans le bon ordre, sinon vos valeurs risquent d'être écrasées. Par exemple :

var a: int = proxy("a", 1)
var b: int = proxy("b", 2)
var _data: Dictionary = {}

func proxy(key: String, value: int):
    _data[key] = value
    print(_data)
    return value

func _init() -> void:
    print(_data)

Affichera :

{ "a": 1 }
{ "a": 1, "b": 2 }
{  }

Pour le corriger, déplacez la définition de la variable _data au-dessus de la définition a ou supprimez l'affectation de dictionnaire vide (= {}).

Variables statiques

Une variable membre de la classe peut être déclarée statique :

static var a

Les variables statiques appartiennent à la classe, pas aux instances. Cela signifie que les variables statiques partagent des valeurs entre plusieurs instances, contrairement aux variables membres classiques.

Depuis l'intérieur d'une classe, vous pouvez accéder aux variables statiques de n'importe quelle fonction, statique ou non. Depuis l'extérieur de la classe, vous pouvez accéder aux variables statiques en utilisant la classe ou une instance (la seconde n'est pas recommandée car elle est moins lisible).

Note

Les annotations @export et @onready ne peuvent pas être appliquées à une variable statique. Les variables locales ne peuvent pas être statiques.

L'exemple suivant définit une classe Person avec une variable statique nommée max_id. Nous incrémentons le max_id dans la fonction _init(). Cela permet de suivre facilement le nombre d'instances Person dans notre jeu.

# person.gd
class_name Person

static var max_id = 0

var id
var name

func _init(p_name):
    max_id += 1
    id = max_id
    name = p_name

Dans ce code, nous créons deux instances de notre classe Person et vérifions que la classe et chaque instance ont la même valeur max_id, car la variable est statique et accessible à chaque instance.

# test.gd
extends Node

func _ready():
    var person1 = Person.new("John Doe")
    var person2 = Person.new("Jane Doe")

    print(person1.id) # 1
    print(person2.id) # 2

    print(Person.max_id)  # 2
    print(person1.max_id) # 2
    print(person2.max_id) # 2

Les variables statiques peuvent avoir des indices de type, des setters et des getters :

static var balance: int = 0

static var debt: int:
    get:
        return -balance
    set(value):
        balance = -value

Une variable statique de la classe parente est également accessible via une classe enfant :

class A:
    static var x = 1

class B extends A:
    pass

func _ready():
    prints(A.x, B.x) # 1 1
    A.x = 2
    prints(A.x, B.x) # 2 2
    B.x = 3
    prints(A.x, B.x) # 3 3

annotation``@static_unload``

Les classes GDScript étant des ressources, la présence de variables statiques dans un script empêche son déchargement même s'il n'y a plus d'instances de cette classe et aucune autre référence restante. Cela peut être important si les variables statiques stockent de grandes quantités de données ou contiennent des références à d'autres ressources du projet, telles que des scènes. Vous devez nettoyer ces données manuellement ou utiliser l'annotation @static_unload si les variables statiques ne stockent pas de données importantes et peuvent être réinitialisées.

Avertissement

Actuellement, en raison d'un bug, les scripts ne sont jamais libérés, même si l'annotation @static_unload est utilisée.

Notez que @static_unload s'applique à l'ensemble du script (y compris les classes internes) et doit être placé en haut du script, avant class_name et extends :

@static_unload
class_name MyNode
extends Node

Voir aussi Static functions et Static constructor.

Conversion de type

Les valeurs affectées à des variables typées doivent avoir un type compatible. S'il est nécessaire de contraindre une valeur à être d'un certain type, surtout pour les types d'objet, vous pouvez utiliser l'opérateur de conversion as.

La conversion de types d'objets résulte en le même objet si la valeur est du même type ou d'un type enfant du type de conversion.

var my_node2D: Node2D
my_node2D = $Sprite2D as Node2D # Works since Sprite2D is a subtype of Node2D.

Si la valeur n'est pas un type enfant, l'opération de conversion se résultera en une valeur null.

var my_node2D: Node2D
my_node2D = $Button as Node2D # Results in 'null' since a Button is not a subtype of Node2D.

Pour les types intégrés, ils seront convertis de force si possible, sinon le moteur générera une erreur.

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.

Le casting est également utile pour avoir de meilleures variables de "type sécurisé" lors de l’interaction avec l’arbre de la scène :

# Will infer the variable to be of type Sprite2D.
var my_sprite := $Character as Sprite2D

# Will fail if $AnimPlayer is not an AnimationPlayer, even if it has the method 'play()'.
($AnimPlayer as AnimationPlayer).play("walk")

Constantes

Les constantes sont des valeurs que vous ne pouvez pas changer lorsque le jeu est s’exécute. Leur valeur doit être connue au moment de la compilation. L'utilisation du mot-clé const permet de donner un nom à une valeur constante. Si vous essayez d'attribuer une valeur à une constante après qu'elle ait été déclarée, vous obtiendrez une erreur.

Nous recommandons d'utiliser des constantes chaque fois qu'une valeur n'est pas censée changer.

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

Même si le type des constantes est implicitement spécifié par la valeur assignée, il est également possible d'ajouter une spécification explicite du type :

const A: int = 5
const B: Vector2 = Vector2()

Assigner une valeur à un type incompatible va générer une erreur.

Vous pouvez également créer des constantes à l'intérieur d'une fonction, ce qui est utile pour nommer des valeurs magiques locales.

Énumérations

Les énumérations sont en fait une forme abrégée pour déclarer des constantes, et sont pratiques si vous voulez assigner des entiers consécutifs à certaines constantes.

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

Si vous passez un nom à l'énumération, toutes les clés seront placées dans une constante Dictionary de ce nom. Cela signifie que toutes les méthodes constantes d'un dictionnaire peuvent également être utilisées avec une énumération nommée.

Important

Les clés d'une énumération nommée ne sont pas enregistrées comme constantes globales. Elles doivent être accessibles en préfixant le nom de l'énumération (Name.KEY).

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.

func _ready():
    # Access values with Name.KEY, prints '5'
    print(State.STATE_JUMP)
    # Use dictionary methods:
    # prints '["STATE_IDLE", "STATE_JUMP", "STATE_SHOOT"]'
    print(State.keys())
    # prints '{ "STATE_IDLE": 0, "STATE_JUMP": 5, "STATE_SHOOT": 6 }'
    print(State)
    # prints '[0, 5, 6]'
    print(State.values())

If not assigning a value to a key of an enum it will be assigned the previous value plus one, or 0 if it is the first entry in the enum. Multiple keys with the same value are allowed.

Fonctions

Functions always belong to a class. The scope priority for variable look-up is: local → class member → global. The self variable is always available and is provided as an option for accessing class members (see self), but is not always required (and should not be sent as the function's first argument, unlike Python).

func my_function(a, b):
    print(a)
    print(b)
    return a + b  # Return is optional; without it 'null' is returned.

Une fonction peut return à tout moment. La valeur de retour par défaut est null.

Si une fonction ne contient qu'une seule ligne de code, elle peut être écrite sur une seule ligne :

func square(a): return a * a

func hello_world(): print("Hello World")

func empty_function(): pass

Les fonctions peuvent également avoir une spécification de type pour les arguments et pour les valeurs retournées. Les types peuvent être ajoutés aux arguments de la même manière que pour les variables :

func my_function(a: int, b: String):
    pass

Si l'argument d'une fonction a une valeur par défaut, il est possible d'inférer le type :

func my_function(int_arg := 42, String_arg := "string"):
    pass

Le type de retour de la fonction peut être spécifié après la liste d'arguments en utilisant le jeton de flèche (->) :

func my_int_function() -> int:
    return 0

Les fonctions qui ont un type de retour doivent retourner une valeur appropriée. Paramétrer le type d'une fonction à void signifie qu'elle ne retournera rien. Les fonctions vides peuvent retourner à l'avance en utilisant le mot-clé return, mais elles ne peuvent pas retourner de valeurs.

func void_function() -> void:
    return # Can't return a value.

Note

Les fonctions non-vides doivent toujours retourner une valeur, donc si votre code a des instructions de branchement (comme une construction if/else), tous les chemins possibles doivent avoir un retour. Par exemple, si vous avez un return à l'intérieur d'un bloc if mais pas après, l'éditeur affichera une erreur car si le bloc n'est pas exécuté, la fonction n'aura pas de valeur valide à retourner.

Référencer des fonctions

Les fonctions sont des valeurs de première classe en termes d'objet Callable. Le référencement d'une fonction par son nom sans l'appeler génère automatiquement l'objet Callable approprié. Cela peut être utilisé pour passer des fonctions en tant qu'arguments.

func map(arr: Array, function: Callable) -> Array:
    var result = []
    for item in arr:
        result.push_back(function.call(item))
    return result

func add1(value: int) -> int:
    return value + 1;

func _ready() -> void:
    var my_array = [1, 2, 3]
    var plus_one = map(my_array, add1)
    print(plus_one) # Prints `[2, 3, 4]`.

Note

Les callables doivent être appelés avec la méthode call(). Vous ne pouvez pas utiliser directement l'opérateur (). Ce comportement est implémenté pour éviter les problèmes de performances lors des appels directs de fonction.

Fonctions Lambda

Les fonctions Lambda vous permettent de déclarer des fonctions qui n'appartiennent pas à une classe. À la place, un objet Callable est créé et assigné directement à une variable. Cela peut être utile pour créer des objets Callable à transmettre sans polluer la portée de la classe.

var lambda = func (x):
    print(x)

Pour appeler la fonction lambda créée, vous pouvez utiliser la méthode call() :

lambda.call(42) # Prints `42`.

Les fonctions Lambda peuvent être nommées à des fins de débogage (le nom est affiché dans le débogueur) :

var lambda = func my_lambda(x):
    print(x)

Vous pouvez spécifier des indications de type pour les fonctions lambda de la même manière que pour les fonctions classiques :

var lambda := func (x: int) -> void:
    print(x)

Notez que si vous souhaitez renvoyer une valeur à partir d'une fonction lambda, un return explicite est requis (vous ne pouvez pas omettre return) :

var lambda = func (x): return x ** 2
print(lambda.call(2)) # Prints `4`.

Les fonctions Lambda prennent en compte l'environnement local , on parle de capture:

var x = 42
var lambda = func ():
    print(x) # Prints `42`.
lambda.call()

Avertissement

La valeur des variables locales est capturée une unique fois, lorsque le lambda est créé. Elles ne seront donc pas mises à jour dans le lambda si elles sont réaffectées dans la fonction externe :

var x = 42
var lambda = func (): print(x)
lambda.call() # Prints `42`.
x = "Hello"
lambda.call() # Prints `42`.

De plus, une fonction lambda ne peut pas modifier une variable locale externe. Après avoir quitté la fonction lambda, la variable restera inchangée, car la capture de la lambda la masque implicitement :

var x = 42
var lambda = func ():
    print(x) # Prints `42`.
    x = "Hello" # Produces the `CONFUSABLE_CAPTURE_REASSIGNMENT` warning.
    print(x) # Prints `Hello`.
lambda.call()
print(x) # Prints `42`.

Cependant, si vous utilisez des types de données transmis par référence (tableaux, dictionnaires et objets), les modifications de contenu sont partagées jusqu'à ce que vous réaffectiez la variable :

var a = []
var lambda = func ():
    a.append(1)
    print(a) # Prints `[1]`.
    a = [2] # Produces the `CONFUSABLE_CAPTURE_REASSIGNMENT` warning.
    print(a) # Prints `[2]`.
lambda.call()
print(a) # Prints `[1]`.

Fonctions statiques

Une fonction peut être déclarée statique. Lorsqu'une fonction est statique, elle n'a pas accès aux variables membres de l'instance ou à self. Une fonction statique a accès aux variables statiques. Les fonctions statiques sont également utiles pour créer des bibliothèques de fonctions d'assistance (Helpers) :

static func sum2(a, b):
    return a + b

Les fonctions Lambda ne peuvent pas être déclarées statiques.

Voir aussi Static variables et Static constructor.

Instructions et flux de contrôle

Les instructions sont standard et peuvent être des affectations, des appels de fonctions, des structure de contrôle, etc (voir ci-dessous). ; utilisé comme séparateur d'instructions est entièrement facultatif.

Expressions

Les expressions sont des séquences d'opérateurs et de leurs opérandes de manière ordonnée. Une expression en elle-même peut également être une déclaration, bien que seuls les appels soient raisonnables pour être utilisés comme déclarations, car les autres expressions n'ont pas d'effets secondaires.

Les expressions renvoient des valeurs qui peuvent être affectées à des cibles valides. Les opérandes d'un opérateur peuvent être une autre expression. Une affectation n'est pas une expression et ne renvoie donc aucune valeur.

Voici quelques exemples d'expressions :

2 + 2 # Binary operation.
-5 # Unary operation.
"okay" if x > 4 else "not okay" # Ternary operation.
x # Identifier representing variable or constant.
x.a # Attribute access.
x[4] # Subscript access.
x > 2 or x < 5 # Comparisons and logic operators.
x == y + 2 # Equality test.
do_something() # Function call.
[1, 2, 3] # Array definition.
{A = 1, B = 2} # Dictionary definition.
preload("res://icon.png") # Preload builtin function.
self # Reference to current instance.

Les identifiants, les attributs et les indices sont des cibles d'affectation valides. Les autres expressions ne peuvent pas se trouver à gauche d'une affectation.

self

self can be used to refer to the current instance and is often equivalent to directly referring to symbols available in the current script. However, self also allows you to access properties, methods, and other names that are defined dynamically (i.e. are expected to exist in subtypes of the current class, or are provided using _set() and/or _get()).

extends Node

func _ready():
    # Compile time error, as `my_var` is not defined in the current class or its ancestors.
    print(my_var)
    # Checked at runtime, thus may work for dynamic properties or descendant classes.
    print(self.my_var)

    # Compile time error, as `my_func()` is not defined in the current class or its ancestors.
    my_func()
    # Checked at runtime, thus may work for descendant classes.
    self.my_func()

Avertissement

Beware that accessing members of child classes in the base class is often considered a bad practice, because this blurs the area of responsibility of any given piece of code, making the overall relationship between parts of your game harder to reason about. Besides that, one can simply forget that the parent class had some expectations about it's descendants.

if/else/elif (si / sinon / sinon-si)

Les conditions simples sont créées en utilisant la syntaxe if/else/elif. Les parenthèses autour des conditions sont autorisées, mais pas obligatoires. Étant donné la nature de l'indentation par tabulations, elif peut être utilisé à la place de else/if pour maintenir un niveau d'indentation raisonnable.

if (expression):
    statement(s)
elif (expression):
    statement(s)
else:
    statement(s)

Les instructions courtes peuvent être écrites sur la même ligne que la condition :

if 1 + 1 == 2: return 2 + 2
else:
    var x = 3 + 3
    return x

Parfois, il peut être nécessaire d'affecter une valeur initiale différente, basée sur une expression booléenne. Dans ce cas, les conditions ternaires peuvent être utiles :

var x = (value) if (expression) else (value)
y += 3 if y < 10 else -1

Les expressions conditionnelles ternaires peuvent être imbriquée pour gérer plus de deux cas. Lorsque ces expressions sont imbriquées, il est recommandé de les indenter sur plusieurs lignes pour préserver leur lisibilité :

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

Vous pouvez également souhaiter vérifier si une valeur est contenue dans quelque chose. Vous pouvez utiliser une instruction if combinée à l'opérateur in pour y parvenir :

# 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

Les boucles simples sont créées en utilisant la syntaxe while. Les boucles peuvent être interrompues à l'aide de break ou continuées à l'aide de continue (qui passe à l'itération suivante de la boucle sans exécuter de code supplémentaire dans l'itération en cours) :

while (expression):
    statement(s)

for

Pour itérer à travers une plage, telle qu'un tableau ou une table, une boucle for est utilisée. Lors de l'itération dans un tableau, l'élément courant du tableau est stocké dans la variable de la boucle. Lors de l'itération sur un dictionnaire, la key est stockée dans la variable de la boucle.

for x in [5, 7, 11]:
    statement # Loop iterates 3 times with 'x' as 5, then 7 and finally 11.

var names = ["John", "Marta", "Samantha", "Jimmy"]
for name: String in names: # Typed loop variable.
    print(name) # Prints name's content.

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 i in range(8, 2, -2):
    statement # Similar to [8, 6, 4] 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)).

Si vous souhaitez attribuer des valeurs à un tableau pendant son itération, il est préférable d'utiliser for i in array.size().

for i in array.size():
    array[i] = "Hello World"

La variable de la boucle est locale à la boucle for et changer sa valeur ne modifiera pas la valeur du tableau. Les objets passés par référence (tels que les nœuds) peuvent toujours être manipulés en appelant des méthodes sur la variable de la boucle.

for string in string_array:
    string = "Hello World" # This has no effect

for node in node_array:
    node.add_to_group("Cool_Group") # This has an effect

match

L'instruction match est utilisée pour réaliser un branchement de l’exécution d'un programme. Elle est semblable à l'instruction switch présente en beaucoup d'autres langages mais elle procure cependant quelques fonctionnalités supplémentaires.

Avertissement

match est plus strict en termes de type que l'opérateur ==. Par exemple, 1 ne correspondra pas à 1.0. La seule exception est la correspondance String vs StringName : par exemple, la chaîne "hello" est considérée comme égale à la chaîne &"hello".

Syntaxe de base

match <test value>:
    <pattern(s)>:
        <block>
    <pattern(s)> when <pattern guard>:
        <block>
    <...>

Cours intensif pour les personnes familiarisées avec les instructions switch

  1. Remplacez switch par match.

  2. Supprimer case.

  3. Supprimez tous les break.

  4. Remplacer default par un unique underscore (_).

Contrôle du flux d’exécution

Les motifs sont comparés de haut en bas. Si un motif correspond, le premier bloc correspondant sera exécuté. Ensuite, l'exécution se poursuit en dessous de l'instruction match.

Note

Le mot clé continue dans match pris en charge dans 3.x a été supprimé dans Godot 4.0.

Les types de motifs suivants sont disponibles :

  • Motif littéral

    Correspond à un litéral:

    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")
    
  • Motif d'expression

    Vérifie la correspondance avec une expression constante, un identifiant ou un accès à un attribut (A.B) :

    match typeof(x):
        TYPE_FLOAT:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Motif générique (joker/wildcard)

    Ce motif correspond à toutes les possibilités. il est écrit sous la forme d'un simple underscore (_).

    Il peut être utilisée comme l’équivalent de default de l'expression switch dans d'autres langages :

    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.")
    
  • Motif de liaison

    Un motif de liaison introduit une nouvelle variable. Comme le caractère joker (wildcard), il correspond à toutes les possibilités - et donne aussi un nom à cette valeur. Elle est particulièrement utile avec les tableaux et les dictionnaires :

    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)
    
  • Motif de tableau

    Il correspond à un tableau. Chaque élément du motif de tableau est un motif lui-même, de sorte que vous pouvez les imbriquer.

    La longueur du tableau est d'abord testée, elle doit être de la même taille que l'expression, sinon cette dernière ne correspondra pas.

    Tableau ouvert : Un tableau peut être plus grand que l'expression en faisant de .. la dernière sous-expression.

    Chaque sous-expression doit être séparée par des virgules.

    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")
    
  • Motif de dictionnaire

    Fonctionne de la même manière que le motif du tableau. Chaque clé doit être une expression constante.

    La taille du dictionnaire est d'abord testée, elle doit être de la même taille que l'expression, sinon l'expression ne correspondra pas.

    Dictionnaire ouvert : Un dictionnaire peut être plus grand que l'expression en faisant de .. le dernier sous-modèle.

    Chaque sous-expression doit être séparée par des virgules.

    Si vous ne spécifiez pas de valeur, seule l'existence de la clé est vérifiée.

    Le motif de valeur est séparée du motif de clé par un : .

    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")
    
  • Motifs multiples

    Vous pouvez également spécifier plusieurs motifs séparées par une virgule. Ces motifs ne peuvent être des motifs de liaisons.

    match x:
        1, 2, 3:
            print("It's 1 - 3")
        "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")
    

Expression de garde

A pattern guard is an optional condition that follows the pattern list and allows you to make additional checks before choosing a match branch. Unlike a pattern, a pattern guard can be an arbitrary expression.

Only one branch can be executed per match. Once a branch is chosen, the rest are not checked. If you want to use the same pattern for multiple branches or to prevent choosing a branch with too general pattern, you can specify a pattern guard after the list of patterns with the when keyword:

match point:
    [0, 0]:
        print("Origin")
    [_, 0]:
        print("Point on X-axis")
    [0, _]:
        print("Point on Y-axis")
    [var x, var y] when y == x:
        print("Point on line y = x")
    [var x, var y] when y == -x:
        print("Point on line y = -x")
    [var x, var y]:
        print("Point (%s, %s)" % [x, y])
  • If there is no matching pattern for the current branch, the pattern guard is not evaluated and the patterns of the next branch are checked.

  • If a matching pattern is found, the pattern guard is evaluated.

    • Si c'est vrai, alors le corps de la branche est exécuté et match se termine.

    • Si c'est faux, alors les motifs de la branche suivante sont vérifiés.

Classes

Par défaut, tous les fichiers scripts sont des classes sans nom. Dans ce cas, vous pouvez uniquement les référencer en utilisant le chemin du fichier, en utilisant un chemin relatif ou un chemin absolu. Par exemple, si vous nommez un fichier de script character.gd :

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

Enregistrement de classes nommées

Vous pouvez donner un nom à votre classe pour l'enregistrer comme un nouveau type dans l'éditeur de Godot. Pour cela, vous utilisez le mot-clé class_name. Vous pouvez éventuellement utiliser l'annotation @icon avec un chemin vers une image, pour l'utiliser comme icône. Votre classe apparaîtra alors avec sa nouvelle icône dans l'éditeur :

# item.gd

@icon("res://interface/icons/item.png")
class_name Item
extends Node
../../../_images/class_name_editor_register_example.png

Astuce

Les images SVG utilisées comme icônes personnalisées de nœuds doivent avoir les options Éditeur > Mettre à l'échelle avec l'échelle de l'éditeur et Éditeur > Convertir les icônes avec le thème de l'éditeur activées dans l'importateur. Cela permet aux icônes de suivre l'échelle de l'éditeur et les paramètres de thème si les icônes sont conçues avec la même palette de couleurs que les icônes de Godot.

Voici un exemple de fichier de classe :

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

Si vous souhaitez également utiliser extends, vous pouvez conserver les deux sur la même ligne :

class_name MyNode extends Node

Note

Godot initialise les variables non statiques à chaque fois que vous créez une instance, y compris les tableaux et les dictionnaires. Cela est dans l'esprit de la sécurité des threads, car les scripts peuvent être initialisés dans des threads séparés sans que l'utilisateur ne le sache.

Avertissement

The Godot editor will hide these custom classes with names that begin with the prefix "Editor" in the 'Create New Node' or 'Create New Scene' dialog windows. The classes are available for instantiation at runtime via their class names, but are automatically hidden by the editor windows along with the built-in editor nodes used by the Godot editor.

Héritage

Une classe (stockée sous forme de fichier) peut hériter de :

  • Une classe globale.

  • Un autre fichier de classe.

  • Une classe interne à l'intérieur d'un autre fichier de classe.

L'héritage multiple n'est pas autorisé.

L'héritage utilise le mot-clé 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

Note

Si l'héritage n'est pas explicitement défini, la classe héritera par défaut de RefCounted.

Pour vérifier si une instance donnée hérite d'une classe donnée, le mot-clé is peut être utilisé :

# Cache the enemy class.
const Enemy = preload("enemy.gd")

# [...]

# Use 'is' to check inheritance.
if entity is Enemy:
    entity.apply_damage()

Pour appeler une fonction dans une classe parente (c'est-à-dire une classe étendue (extend) par votre classe actuelle), utilisez le mot-clé super :

super(args)

Ceci est particulièrement utile car les fonctions des classes étendues remplacent les fonctions portant le même nom dans leurs classes parentes. Si vous souhaitez toujours les appeler, vous pouvez utiliser super:

func some_func(x):
    super(x) # Calls the same function on the super class.

Si vous devez appeler une fonction différente de la classe parente, vous pouvez spécifier le nom de la fonction avec l'opérateur d'attribut :

func overriding():
    return 0 # This overrides the method in the base class.

func dont_override():
    return super.overriding() # This calls the method as defined in the base class.

Avertissement

L'une des mauvaises pratiques les plus courantes consiste à essayer de remplacer les méthodes non virtuelles de moteur telles que get_class(), queue_free(), etc. Ceci n'est pas pris en charge pour des raisons techniques.

Dans Godot 3, vous pouvez masquer les méthodes du moteur dans GDScript, et cela fonctionnera si vous appelez cette méthode dans GDScript. Cependant, le moteur n'exécutera pas votre code si la méthode est appelée à l'intérieur du moteur lors d'un événement.

Dans Godot 4, le masquage ne fonctionne pas toujours, car GDScript optimise les appels de méthodes natives. Par conséquent, nous avons ajouté l'avertissement NATIVE_METHOD_OVERRIDE, qui est traité comme une erreur par défaut. Nous vous déconseillons fortement de désactiver ou d'ignorer l'avertissement.

Notez que cela ne s'applique pas aux méthodes virtuelles telles que _ready(), _process() et autres (marquées avec le qualificateur virtual dans la documentation et dont les noms commencent par un trait de soulignement). Ces méthodes sont spécifiquement destinées à personnaliser le comportement du moteur et peuvent être remplacées dans GDScript. Les signaux et les notifications peuvent également être utiles à ces fins.

Constructeur de classe

Le constructeur de classe, appelé lors de l'instanciation de la classe, est nommé _init. Si vous souhaitez appeler le constructeur de la classe de base, vous pouvez également utiliser la syntaxe super. Notez que chaque classe possède un constructeur implicite qui est toujours appelé (définissant les valeurs par défaut des variables de classe). super est utilisé pour appeler le constructeur explicite :

func _init(arg):
   super("some_default", arg) # Call the custom base constructor.

Ceci est mieux expliqué par des exemples. Disons que nous avons ce scénario :

# 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):
    super(e)
    # Do something with 'e'.
    message = m

Il y a plusieurs choses à garder à l'esprit ici :

  1. Si la classe parent (state.gd) définit un constructeur _init qui prend des arguments (e dans ce cas), alors la classe fille (idle.gd) doit définir aussi _init et doit passer les paramètres appropriés à _init définis dans state.gd.

  2. idle.gd peut avoir un nombre différent d'arguments que la classe parent state.gd.

  3. Dans l'exemple ci-dessus, le e qui est passé au constructeur state.gd, est le même e passé à idle.gd.

  4. Si le constructeur _init de idle.gd prend 0 arguments, il doit tout de même transmettre une valeur à la classe parent state.gd, même si elle ne fait rien. Ce qui nous amène au fait que vous pouvez aussi passer des littéraux dans le constructeur de base, pas seulement des variables. Par exemple :

    # idle.gd
    
    func _init():
        super(5)
    

Constructeur statique

Un constructeur statique est une fonction statique _static_init qui est appelée automatiquement lorsque la classe est chargée, une fois les variables statiques initialisées :

static var my_static_var = 1

static func _static_init():
    my_static_var = 2

Un constructeur statique ne peut pas prendre d’arguments et ne doit renvoyer aucune valeur.

Classes internes

Un fichier de classe peut contenir des classes internes. Les classes internes sont définies à l'aide du mot-clé class. Ils sont instanciés à l'aide de la fonction ClassName.new().

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

Les classes comme ressources

Les classes stockées en tant que fichiers sont traitées comme des ressources GDScript. Elles doivent être chargées à partir du disque pour y accéder à partir d'autres classes. Cela se fait soit à l'aide des fonctions load ou preload (voir ci-dessous). L'instanciation d'une ressource de classe chargée se fait en appelant la fonction new sur l'objet de la classe :

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

Exports

Note

La documentation à propos des exports a été déplacé vers GDScript Propriétés exportées.

Propriétés (setters et getters)

Parfois, vous souhaitez qu'une variable membre d'une classe fasse plus que simplement contenir des données et effectue une validation ou un calcul à chaque fois que sa valeur change. Il peut également être souhaitable d'encapsuler son accès d'une manière ou d'une autre.

Pour cela, GDScript fournit une syntaxe spéciale pour définir des propriétés à l'aide des mots-clés set et get après une déclaration de variable. Vous pouvez ensuite définir un bloc de code qui sera exécuté lorsque la variable sera consultée ou affectée.

Exemple :

var milliseconds: int = 0
var seconds: int:
    get:
        return milliseconds / 1000
    set(value):
        milliseconds = value * 1000

Note

Contrairement à setget dans les versions précédentes de Godot, les méthodes et et get sont toujours appelées (sauf comme indiqué ci-dessous), même lorsqu'elles sont utilisées à l'intérieur de la même classe (avec ou sans préfixe avec self .). Cela rend le comportement cohérent. Si vous avez besoin d'un accès direct à la valeur, utilisez une autre variable pour l'accès direct et faites en sorte que le code de la propriété utilise ce nom.

Syntaxe alternative

Il existe également une autre notation pour utiliser les fonctions existantes de classe si vous souhaitez séparer le code de la déclaration de la variable ou si vous devez réutiliser le code sur plusieurs propriétés (mais vous ne pouvez pas distinguer pour quelle propriété le setter/getter est appelé) :

var my_prop:
    get = get_my_prop, set = set_my_prop

Cela peut également être fait sur la même ligne :

var my_prop: get = get_my_prop, set = set_my_prop

Le setter et le getter doivent utiliser la même notation, le mélange de styles pour la même variable n'est pas autorisé.

Note

Vous ne pouvez pas spécifier d'indications de type pour les fonctions setter et getter en ligne. Cela est fait exprès pour réduire le code sans intérêt. Si la variable est typée, alors l'argument du setter est automatiquement du même type et la valeur de retour du getter doit lui correspondre. Les fonctions setter/getter séparées peuvent avoir des indications de type et le type doit correspondre au type de la variable ou être un type plus large.

Quand les setter/getter ne sont pas appelés

Lorsqu'une variable est initialisée, la valeur initiale sera écrite directement dans la variable. Y compris si l'annotation @onready est appliquée à la variable.

L'utilisation du nom de la variable pour la modifier dans son propre setter ou pour l'obtenir dans son propre getter accédera directement à la variable sous-jacente, de sorte que cela ne générera pas de récursivité infinie et vous évitera de déclarer explicitement une autre variable :

signal changed(new_value)
var warns_when_changed = "some value":
    get:
        return warns_when_changed
    set(value):
        changed.emit(value)
        warns_when_changed = value

Ceci s'applique également à la syntaxe alternative :

var my_prop: set = set_my_prop

func set_my_prop(value):
    my_prop = value # No infinite recursion.

Avertissement

L'exception ne se propage pas aux autres fonctions appelées dans le setter/getter. Par exemple, le code suivant provoquera une récursivité infinie :

var my_prop:
    set(value):
        set_my_prop(value)

func set_my_prop(value):
    my_prop = value # Infinite recursion, since `set_my_prop()` is not the setter.

Mode tool(outil)

Par défaut, les scripts ne s'exécutent pas dans l'éditeur et seules les propriétés exportées peuvent être modifiées. Dans certains cas, il est souhaitable qu'ils s'exécutent dans l'éditeur (tant qu'ils n'exécutent pas le code du jeu ou évitent de le faire manuellement). Pour cela, l'annotation @tool existe et doit être placée en haut du fichier :

@tool
extends Button

func _ready():
    print("Hello")

Voir Exécuter le code dans l'éditeur pour plus d'informations.

Avertissement

Faites attention en supprimant des nœuds avec queue_free() ou free() dans un script outil (en particulier le propriétaire du script). Puisque les scripts outils s'exécutent dans l'éditeur, une erreur pourrait le faire crasher.

Gestion de la mémoire

Godot implémente le comptage de références pour libérer certaines instances qui ne sont plus utilisées, au lieu d'un ramasse-miettes (garbage collector), ou nécessitant une gestion purement manuelle. Toute instance de la classe RefCounted (ou toute classe qui en hérite, comme Resource) sera libérée automatiquement lorsqu'elle n'est plus utilisée. Pour une instance de toute classe qui n'est pas une RefCounted (comme Node ou le type de base Object), elle restera en mémoire jusqu'à ce qu'elle soit supprimée avec free() (ou queue_free() pour les Nodes).

Note

Si un Node est supprimé via free() ou queue_free(), tous ses enfants seront également supprimés de manière récursive.

Pour éviter les références cycliques qui ne peuvent pas être libérés, une fonction WeakRef est fournie pour créer des références faibles, qui permettent d'accéder à l'objet sans empêcher un RefCounted de se libérer. Voici un exemple :

extends Node

var my_file_ref

func _ready():
    var f = FileAccess.open("user://example_file.json", FileAccess.READ)
    my_file_ref = weakref(f)
    # the FileAccess class inherits RefCounted, so it will be freed when not in use

    # the WeakRef will not prevent f from being freed when other_node is finished
    other_node.use_file(f)

func _this_is_called_later():
    var my_file = my_file_ref.get_ref()
    if my_file:
        my_file.close()

Alternativement, quand vous n'utilisez pas de références, le is_instance_valid(instance) peut être utilisé pour vérifier si un objet a été libéré.

Signaux

Les signaux sont un outil permettant d’émettre des messages à partir d’un objet auquel d’autres objets peuvent réagir. Pour créer des signaux personnalisés pour une classe, utilisez le mot-clé signal.

extends Node


# A signal named health_depleted.
signal health_depleted

Note

Les signaux sont comme des fonctions de rappel. Ils remplissent également le rôle d'Observateurs, un modèle de conception courant en programmation. Pour plus d'informations, lisez le tutoriel sur les Observateurs (en anglais), de l'e-book Game Programming Patterns.

Vous pouvez connecter ces signaux à des méthodes de la même manière que vous connectez les signaux intégrés de nœuds tels que Button ou RigidBody3D.

Dans l'exemple ci-dessous, on connecte le signal health_depleted du nœud Character au nœud Game. Quand le nœud Character émettra ce signal, la fonction de rappel _on_character_health_depleted du nœud Game sera appelée :

# game.gd

func _ready():
    var character_node = get_node('Character')
    character_node.health_depleted.connect(_on_character_health_depleted)


func _on_character_health_depleted():
    get_tree().reload_current_scene()

Vous pouvez émettre autant d'arguments que vous souhaitez avec un signal.

Voici un exemple où cela est utile. Si nous voulons ajouter une barre de vie qui s'anime quand les points de vie changent, mais nous souhaitons séparer l'interface utilisateur du joueur dans notre arbre de scène.

Dans notre script character.gd, on va définir un signal health_changed et l'émettre avec Signal.emit(), et depuis le nœud Game plus haut dans l'arborescence on le connecte au nœud Lifebar avec la méthode Signal.connect():

# 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.
    health_changed.emit(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)
...

Dans le nœud Game, on récupère les nœuds Character et Lifebar puis on connecte le personnage, qui émet le signal, au récepteur c'est-à-dire Lifebar dans notre cas.

# game.gd

func _ready():
    var character_node = get_node('Character')
    var lifebar_node = get_node('UserInterface/Lifebar')

    character_node.health_changed.connect(lifebar_node._on_Character_health_changed)

Cela permet alors à la Lifebar de réagir aux changements de points de vie sans avoir à la coupler au nœud Character.

Il est possible d'ajouter des arguments optionnels entre parenthèses après la définition du signal :

# Defining a signal that forwards two arguments.
signal health_changed(old_value, new_value)

Ces arguments seront affichés dans le dock Nœud et Godot les utilisera pour générer les fonctions de rappel automatiquement pour vous. Dans tous les cas, vous pouvez émettre autant d'arguments que vous souhaitez avec vos signaux ; c'est à vous d'émettre les bonnes valeurs.

../../../_images/gdscript_basics_signals_node_tab_1.png

GDScript peut lier un tableau de valeurs à des connexions entre un signal et une méthode. Lorsque le signal est émis, la méthode de rappel reçoit les valeurs liées. Ces arguments liés sont uniques pour chaque connexion et les valeurs resteront les mêmes.

Vous pouvez utiliser ces arguments pour ajouter des informations supplémentaires à la connexion si le signal émit ne vous donne pas accès à toutes les informations dont vous avez besoin.

En nous appuyant sur l'exemple ci-dessus, disons que nous souhaitons afficher un journal des dégâts subis par chaque personnage à l'écran, comme Player1 took 22 damage.. Le signal health_changed ne nous donne pas le nom du personnage qui a subi des dégâts. Ainsi, lorsque nous connectons le signal à la console du jeu, nous pouvons ajouter le nom du personnage dans l'argument du tableau de liaison :

# game.gd

func _ready():
    var character_node = get_node('Character')
    var battle_log_node = get_node('UserInterface/BattleLog')

    character_node.health_changed.connect(battle_log_node._on_Character_health_changed.bind(character_node.name))

Notre nœud BattleLog reçoit chaque élément du tableau de liaisons comme argument supplémentaire :

# battle_log.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."

En attente de signaux ou de coroutines

Le mot-clé await peut être utilisé pour créer des coroutines qui attendent qu'un signal soit émis avant de continuer l'exécution. L'utilisation du mot-clé await avec un signal ou un appel de fonction qui est également une coroutine rendra immédiatement le contrôle à l'appelant. Lorsque le signal est émis (ou que la coroutine appelée se termine), elle reprendra l'exécution à partir du point où elle s'est arrêtée.

Par exemple, pour arrêter l'exécution jusqu'à ce que l'utilisateur appuie sur un bouton, vous pouvez faire quelque chose comme ceci :

func wait_confirmation():
    print("Prompting user")
    await $Button.button_up # Waits for the button_up signal from Button node.
    print("User confirmed")
    return true

Dans ce cas, wait_confirmation devient une coroutine, ce qui signifie que l'appelant doit également l'attendre :

func request_confirmation():
    print("Will ask the user")
    var confirmed = await wait_confirmation()
    if confirmed:
        print("User confirmed")
    else:
        print("User cancelled")

Notez que demander la valeur de retour d'une coroutine sans await déclenchera une erreur :

func wrong():
    var confirmed = wait_confirmation() # Will give an error.

Cependant, si vous ne dépendez pas du résultat, vous pouvez simplement l'appeler de manière asynchrone, ce qui n'arrêtera pas l'exécution et ne fera pas de la fonction actuelle une coroutine :

func okay():
    wait_confirmation()
    print("This will be printed immediately, before the user press the button.")

Si vous utilisez await avec une expression qui n'est ni un signal ni une coroutine, la valeur sera renvoyée immédiatement et la fonction ne rendra pas le contrôle à l'appelant :

func no_wait():
    var x = await get_five()
    print("This doesn't make this function a coroutine.")

func get_five():
    return 5

Cela signifie également que le renvoi d'un signal à partir d'une fonction qui n'est pas une coroutine, obligera l'appelant à attendre ce signal :

func get_signal():
    return $Button.button_up

func wait_button():
    await get_signal()
    print("Button was pressed")

Note

Contrairement à yield dans les versions précédentes de Godot, vous ne pouvez pas obtenir l'objet correspondant à l'état de la fonction. Cela est fait pour garantir la sécurité des types. Avec cette sécurité en place, une fonction ne peut pas dire qu'elle renvoie un int alors qu'elle renvoie en fait un objet d'état correspondant à l'état de la fonction pendant l'exécution.

Mot-clé d'assertion

Le mot-clé assert sert à vérifier une condition dans un build de débogage. Cette instruction est ignorée dans un build sans débogage, c'est-à-dire que l'expression passée en argument de assert ne sera pas évaluée dans un projet exporté en mode publication. De ce fait, assert ne doit pas contenir des expressions ayant des effets secondaires. Sinon, le comportement du script variera selon s'il est exécuté dans un build de débogage ou de publication.

# Check that 'i' is 0. If 'i' is not 0, an assertion error will occur.
assert(i == 0)

Lors de l'exécution d'un projet depuis l'éditeur, celui-ci sera mis en pause en cas d'erreur d'assertion.

Vous pouvez éventuellement passer un message d’erreur personnalisé à afficher si l’assertion échoue :

assert(enemy_power < 256, "Enemy is too powerful!")