GDScript basics

Introduction

GDScript is a high-level, dynamically typed programming language used to create content. It uses a syntax similar to Python (blocks are indent-based and many keywords are similar). Its goal is to be optimized for and tightly integrated with Godot Engine, allowing great flexibility for content creation and integration.

Histoire

In the early days, the engine used the Lua scripting language. Lua is fast, but creating bindings to an object oriented system (by using fallbacks) was complex and slow and took an enormous amount of code. After some experiments with Python, it also proved difficult to embed.

Le dernier langage de script tiers qui était utilisé pour créer des jeux était Squirrel, mais celui-ci fut aussi abandonné. Dès lors, il devint évident qu’un langage de script personnalisé pourrait faire usage de l’architecture particulière de Godot de façon plus optimale :

  • Godot intègre les scripts dans les nœuds. La plupart des langages ne sont pas conçus avec cela à l’esprit.
  • Godot utilise plusieurs types de données intégrés pour les opérations mathématiques en 2D et 3D. Les scripts de language ne fournissent pas cela. Et lier ceux-ci et inefficaces.
  • Godot utilise très largement les threads pour récupérer et initialiser les données à distance ou du disque. Les interpréteurs de script pour les langages courants ne sont pas adaptés pour ça.
  • Godot possède déjà un modèle de gestion de la mémoire pour les ressources. La grande majorité des langages de script viennent avec leur propre modèle, ce qui résulte en un effort redondant et des bugs.
  • Lier du code est toujours très compliqué et cela résulte en des défaillances diverses, des bugs inattendus et généralement une maintenabilité difficile.

Le résultat de ces considérations est GDScript. Le langage et l’interpréteur pour GDScript sont, au final, plus compacts que le code de liaison pour Lua et Squirrel, tout en ayant des fonctionnalités équivalentes. Au fil du temps, avoir un langage intégré s’est avéré être un énorme avantage.

Exemple de GDScript

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

# A file is a class!

# Inheritance

extends BaseClass

# (optional) class definition with a custom icon

class_name MyClass, "res://path/to/optional/icon.svg"

# Member variables

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

# Function

func some_function(param1, param2):
    var local_var = 5

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

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

    while param2 != 0:
        param2 -= 1

    var local_var2 = param1 + 3
    return local_var2

# Functions override functions with the same name on the base/parent class.
# If you still want to call them, use '.' (like 'super' in other languages).

func something(p1, p2):
    .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.

Langage

Ce qui suit est un aperçu de GDScript Les détails tels que les méthodes disponibles pour les tableaux ou autres objets peuvent être consultés dans les liens vers les descriptions des classes.

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

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 (symboles) réservés, il ne peuvent êtres utilisés comme identifiants. Les opérateurs (comme  » in « ,  » not « ,  » 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.
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 ou 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.
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.
as Convertir la valeur vers un type donné, si possible.
self Réfère à l’instance courante de la classe.
tool Exécute le script dans l’éditeur.
signal Définit un signal.
func Définit une fonction.
static Définit une fonction statique. Les variables membres statiques ne sont pas autorisés.
const Définit une constante.
enum Définit une énumération.
var Définit une variable.
onready Initialise une variable une fois que le nœud auquel le script est attaché, ainsi que ses enfants, font partie de l’arborescence de la scène.
export Sauvegarde une variable ainsi que la ressource qui lui est attachée et la rend visible et modifiable via l’éditeur.
setget Définit les accesseurs (setter) et mutateurs (getter) pour une variable.
point d’arrêt Assistant de l’éditeur de points d’arrêt du débogueur.
preload Précharge une classe ou une variable. Voir Classes as resources.
yield Support de coroutine. Voir Coroutines with yield.
assert Affirmer une condition, journalise les erreurs en cas d’échec. Ignorer dans les compilations autre que de débogages. Voir Assert keyword.
remote Annotation de réseautage RPC. Voir high-level multiplayer docs.
master Annotation de réseautage RPC. Voir high-level multiplayer docs.
puppet Annotation de réseautage RPC. Voir high-level multiplayer docs.
remotesync Annotation de réseautage RPC. Voir high-level multiplayer docs.
mastersync Annotation de réseautage RPC. Voir high-level multiplayer docs.
puppetsync Annotation de réseautage RPC. Voir high-level multiplayer docs.
PI Constante PI.
TAU Constante TAU.
INF Constante de l’infinité. Elle est utilisée pour les comparaisons.
NAN La constante NAN (n’est pas un nombre). Elle est utilisée pour les comparaisons.

Opérateurs

Ce qui est suit est la liste des opérateurs supportés et leur priorité.

Opérateur Description
x[index] Subscription (highest priority)
x.attribut Attribute reference
is Instance type checker
~ Opération bit-à-bit de négation
-x Negative / Unary negation
* / %

Multiplication / Division / Reste

Ces opérateurs ont le même comportement qu’en C++. La division d’entiers est tronquée plutôt que de retourner un nombre fractionnaire, et l’opérateur % n’est disponible que pour les ints (« fmod » pour les floats)

+ Addition / Concatenation of arrays
- Soustraction
<< >> Bit shifting
& Conjonction logique bit-à-bit
^ Opération « Ou » exclusif bit-à-bit
| Disjonction logique bit-à-bit
< > == != >= <= Comparaisons
in Content test
! not NOT booléen
and && AND booléenooléen
or || OR booléen
if x else if / else ternaire
= += -= *= /= %= &= |= Assignment (lowest priority)

Littéraux

Littéraux Type
45 Entier en base 10 (décimal)
0x8F51 Base 16 (hexadecimal) integer
0b101010 Base 2 (binary) integer
3.14, 58.1e-10 Floating-point number (real)
"Bonjour", "Salut" Chaînes de caractères
"""Bonjour""" Chaîne de caractères multiligne
@"Node/Label" class_NodePath or StringName
$NodePath Sténographie pour get_node("NodePath")

Commentaire

Tout ce qui est écrit du # jusqu’à la fin de la ligne est ignoré et est considéré comme un commentaire.

# This is a comment.

Types intégrés

Built-in types are stack-allocated. They are passed as values. This means a copy is created on each assignment or when passing them as arguments to functions. The only exceptions are Arrays and Dictionaries, which are passed by reference so they are shared. (Pooled arrays such as PoolByteArray are still passed as values.)

Types intégrés basiques

Une variable en GDScript peut être affectée à divers 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.

bool

Short for « boolean », it can only contain true or false.

int

Short for « integer », it stores whole numbers (positive and negative). It is stored as a 64-bit value, equivalent to « int64_t » in C++.

float

Stores real numbers, including decimals, using floating-point values. It is stored as a 64-bit value, equivalent to « double » in C++. Note: Currently, data structures such as Vector2, Vector3, and PoolRealArray store 32-bit single-precision « float » values.

String

A sequence of characters in Unicode format. Strings can contain standard C escape sequences. GDScript also supports Chaînes de format GDScript.

Types intégrés vectoriels

Vector2

Type de vecteur 2D contenant les attributs x and y. Peut aussi être accédé comme pour un tableau.

Rect2

2D Rectangle type containing two vectors fields: position and size. Also contains an end field which is position + size.

Vector3

Type de vecteur 3D contenant les attributs x, y et z. Il peut également être accédé comme un tableau.

Transform2D

3×2 matrix used for 2D transforms.

Plane

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

Quat

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

Axis-aligned bounding box (or 3D box) contains 2 vectors fields: position and size. Also contains an end field which is position + size.

Basis

Matrice 3x3 utilisée pour les rotations 3D et les mises à l’échelle. Elle contient 3 attributs vecteurs (x, y et z) et peut aussi être accédé comme un tableae de vecteurs 3D.

Transform

Transformation 3D contenant un attribut base vectorielle basis et un attribut vecteur 3D (Vector3) origin.

Types intégrés dans le moteur

Color

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

NodePath

Chemin vers un nœud utilisé principalement dans le système de scène. Il peut facilement être assigné vers ou à partir d’une chaîne de caractère.

RID

Identificateur de ressource (RID). Les serveurs utilise 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

Generic sequence of arbitrary object types, including other arrays or dictionaries (see below). The array can resize dynamically. Arrays are indexed starting from index 0. Negative indices count from the end.

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

GDScript arrays are allocated linearly in memory for speed. Large arrays (more than tens of thousands of elements) may however cause memory fragmentation. If this is a concern, special types of arrays are available. These only accept a single data type. They avoid memory fragmentation and use less memory, but are atomic and tend to run slower than generic arrays. They are therefore only recommended to use for large data sets:

  • PoolByteArray : Un tableau d’octets (entiers de 0 à 255).
  • PoolIntArray : Un tableau d’entiers.
  • PoolRealArray : Un tableau de nombres flottants.
  • PoolStringArray : Un tableau de chaînes de caractères.
  • PoolVector2Array : Un tableau d’objets Vector2.
  • PoolVector3Array : Un tableau d’objets Vector3.
  • PoolColorArray : Un tableau d’objets Color.

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

Les tableaux en style Lua sont également supportés. Le style Lua utilise = au lieu de : et n’utilise pas de guillemets pour marquer les clés de chaîne de caractères (ce qui réduit légèrement l’écriture). Notez cependant que comme tout identificateur GDScript, les clés écrites dans ce formulaire ne peuvent pas commencer par un chiffre.

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

Pour ajouter une clé a 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.

Données

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

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 = Sprite.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 := Sprite.new() # 'my_node' is of type 'Sprite'

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 :

  • Built-in types (Array, Vector2, int, String, etc.).
  • Engine classes (Node, Resource, Reference, etc.).
  • Les noms des constantes s’ils contiennent un script ressource (MyScript si vous avez déclaré const MyScript = preload("res://my_script.gd")).
  • Other classes in the same script, respecting scope (InnerClass.NestedClass if you declared class NestedClass inside the class InnerClass in the same scope).
  • Les classes de script déclarées avec le mot-clé class_name.

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 = $Sprite as Node2D # Works since Sprite 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

Casting is also useful to have better type-safe variables when interacting with the scene tree:

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

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

Constantes

Les constantes sont similaires aux variables, mais doivent être constantes ou des expressions constantes et doivent être assignées à l’initialisation.

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.

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.

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

Si vous donnez un nom à l” « enum », cela mettra toutes les valeurs à l’intérieur d’un « const dictionary » de même nom.

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

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.

Fonctions

Les fonctions appartiennent toujours à une classe. La priorité de portée pour la recherche de la variable est : locale → membre de classe → globale. La variable self est toujours disponible et est fournie comme option pour accéder aux membres de la classe, mais n’est pas toujours nécessaire (et ne devrait pas être envoyée comme premier argument de la fonction, contrairement à Python).

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

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

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 retourné une valeur appropriée. Paramétrer le type d’une fonction à void signifie qu’elle ne retournera rien. Les fonctions voids peuvent retourner à l’avance en utilisant le mot-clé return, mais elles ne peuvent pas retourner de valeurs.

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 ``retour”” à l’intérieur d’un bloc ``si”” 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.

Referencing functions

Contrary to Python, functions are not first-class objects in GDScript. This means they cannot be stored in variables, passed as an argument to another function or be returned from other functions. This is for performance reasons.

To reference a function by name at run-time, (e.g. to store it in a variable, or pass it to another function as an argument) one must use the call or funcref helpers:

# Call a function by name in one step.
my_node.call("my_function", args)

# Store a function reference.
var my_func = funcref(my_node, "my_function")
# Call stored function reference.
my_func.call_func(args)

Fonctions statiques

A function can be declared static. When a function is static, it has no access to the instance member variables or self. This is mainly useful to make libraries of helper functions:

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

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.

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

De simples conditions 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 onglets, elif peut être utilisé à la place de else/if pour maintenir le niveau d’indentation.

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

Sometimes, you might want to assign a different initial value based on a boolean expression. In this case, ternary-if expressions come in handy:

var x = [value] if [expression] else [value]
y += 3 if y < 10 else -1

while

Les boucles simples sont créées en utilisant la syntaxe while. Elles peuvent être interrompues en utilisant break ou poursuivies en utilisant continue :

while [expression]:
    statement(s)

for

Pour itérer sur une plage de valeurs, comme par exemple un tableau (Array), une boucle for est utilisée. En itérant sur un tableau, l’élément courant est stocké dans la variable de boucle. En itérant sur un dictionnaire, c’est l”index qui est stocké dans la variable de boucle.

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

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

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.

Basic syntax:

match [expression]:
    [pattern](s):
        [block]
    [pattern](s):
        [block]
    [pattern](s):
        [block]

Cours rapide pour ceux qui sont familiers avec les instructions switch :

  1. Replace switch with match.
  2. Remove case.
  3. Enlevez tous les break. Si vous ne voulez pas break par défaut, vous pouvez utiliser continue pour poursuivre l’exécution de match.
  4. Remplacer default par un unique underscore (_).

Contrôle du flux d’exécution :

Les expressions sont comparées du haut vers le bas. Si une expression correspond au modèle, le bloc de code correspondant sera exécuté. Après cela, l’exécution continuera après le bloc d’instructions match, ignorant les comparaisons suivantes. Si vous voulez tout de même poursuivre l’exécution de match et passer à la comparaison suivante, vous pouvez utiliser continue.

Il y a 6 types d’expressions :

  • Constant pattern

    Constant primitives, like numbers and strings:

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

    Matches the contents of a variable/enum:

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Wildcard pattern

    Cette expression correspond à toute possibilité. Elle est écrite sous la forme d’un simple underscore (_).

    It can be used as the equivalent of the default in a switch statement in other languages:

    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.")
    
  • Binding pattern

    A binding pattern introduces a new variable. Like the wildcard pattern, it matches everything - and also gives that value a name. It’s especially useful in array and dictionary patterns:

    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)
    
  • Array pattern

    Matches an array. Every single element of the array pattern is a pattern itself, so you can nest them.

    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.

    Open-ended array: An array can be bigger than the pattern by making the last subpattern ...

    Every subpattern has to be comma-separated.

    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")
    
  • Dictionary pattern

    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.

    Open-ended dictionary: A dictionary can be bigger than the pattern by making the last subpattern ...

    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.

    A value pattern is separated from the key pattern with a :.

    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")
    
  • Multiple patterns

    You can also specify multiple patterns separated by a comma. These patterns aren’t allowed to have any bindings in them.

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

Classes

By default, all script files are unnamed classes. In this case, you can only reference them using the file’s path, using either a relative or an absolute path. For example, if you name a script file 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()

Instead, you can give your class a name to register it as a new type in Godot’s editor. For that, you use the class_name keyword. You can add an optional comma followed by a path to an image, to use it as an icon. Your class will then appear with its new icon in the editor:

# Item.gd

extends Node

class_name Item, "res://interface/icons/item.png"
../../../_images/class_name_editor_register_example.png

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)

Note

La syntaxe de classe de Godot est compacte: elle ne peut contenir que des membres variables ou des fonctions. Vous pouvez utiliser des fonctions statiques, mais pas des membres variables statiques. De la même manière, le moteur initialise les variables chaque fois que vous créez une instance, ce qui inclut les tableaux et les dictionnaires. Ceci 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 n’en soit informé.

Héritage

A class (stored as a file) can inherit from:

  • A global class.
  • Another class file.
  • Une classe interne à l’intérieur d’un autre fichier de classe.

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

Inheritance uses the extends keyword:

# 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

To check if a given instance inherits from a given class, the is keyword can be used:

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

# [...]

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

To call a function in a parent class (i.e. one extend-ed in your current class), prepend . to the function name:

.base_func(args)

This is especially useful because functions in extending classes replace functions with the same name in their parent classes. If you still want to call them, you can prefix them with . (like the super keyword in other languages):

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

Note

Default functions like _init, and most notifications such as _enter_tree, _exit_tree, _process, _physics_process, etc. are called in all parent classes automatically. There is no need to call them explicitly when overloading them.

Constructeur de classe

The class constructor, called on class instantiation, is named _init. As mentioned earlier, the constructors of parent classes are called automatically when inheriting a class. So, there is usually no need to call ._init() explicitly.

Unlike the call of a regular function, like in the above example with .some_func, if the constructor from the inherited class takes arguments, they are passed like this:

func _init(args).(parent_args):
   pass

This is better explained through examples. Consider this scenario:

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

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

  1. If the inherited class (State.gd) defines a _init constructor that takes arguments (e in this case), then the inheriting class (Idle.gd) must define _init as well and pass appropriate parameters to _init from State.gd.

  2. Idle.gd can have a different number of arguments than the parent class State.gd.

  3. In the example above, e passed to the State.gd constructor is the same e passed in to Idle.gd.

  4. If Idle.gd’s _init constructor takes 0 arguments, it still needs to pass some value to the State.gd parent class, even if it does nothing. This brings us to the fact that you can pass literals in the base constructor as well, not just variables. eg.:

    # Idle.gd
    
    func _init().(5):
        pass
    

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 ressources. 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 classe :

# Load the class resource when calling load().
var my_class = 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()

Exportations

Note

Documentation about exports has been moved to GDScript exports.

Setters/getters

Il est souvent utile de savoir quand une variable membre d’une classe change pour une raison quelconque. Il peut également être souhaitable d’encapsuler son accès d’une manière ou d’une autre.

Pour cela, GDScript fournit une syntaxe setter/getter en utilisant le mot-clé setget. Il est utilisé directement après une définition de variable :

var variable = value setget setterfunc, getterfunc

Whenever the value of variable is modified by an external source (i.e. not from local usage in the class), the setter function (setterfunc above) will be called. This happens before the value is changed. The setter must decide what to do with the new value. Vice versa, when variable is accessed, the getter function (getterfunc above) must return the desired value. Below is an example:

var my_var setget my_var_set, my_var_get

func my_var_set(new_value):
    my_var = new_value

func my_var_get():
    return my_var # Getter must return a value.

Either of the setter or getter functions can be omitted:

# Only a setter.
var my_var = 5 setget my_var_set
# Only a getter (note the comma).
var my_var = 5 setget ,my_var_get

Setters and getters are useful when exporting variables to the editor in tool scripts or plugins, for validating input.

L’accès local ne déclenchera pas le setter et le getter. En voici une illustration :

func _init():
    # Does not trigger setter/getter.
    my_integer = 5
    print(my_integer)

    # Does trigger setter/getter.
    self.my_integer = 5
    print(self.my_integer)

Mode outil

By default, scripts don’t run inside the editor and only the exported properties can be changed. In some cases, it is desired that they do run inside the editor (as long as they don’t execute game code or manually avoid doing so). For this, the tool keyword exists and must be placed at the top of the file:

tool
extends Button

func _ready():
    print("Hello")

Avertissement

Be cautious when freeing nodes with queue_free() or free() in a tool script (especially the script’s owner itself). As tool scripts run their code in the editor, misusing them may lead to crashing the editor.

Gestion de la mémoire

Si une classe hérite de class_Reference, les instances seront libérées lorsqu’elles ne seront plus utilisées. Il n’y a pas de Garbage Collector, uniquement un comptage des références. Par défaut, toutes les classes qui ne définissent un héritage étendu Reference. Si cela n’est pas désiré, alors une classe doit hériter class_Object manuellement et doit appeler instance.free(). Pour éviter les cycles de référence qui ne peuvent pas être libérés, une fonction weakref est fournie pour créer des références faibles.

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

Signals are a tool to emit messages from an object that other objects can react to. To create custom signals for a class, use the signal keyword.

extends Node

# A signal named health_depleted
signal health_depleted

Note

Signals are a Callback mechanism. They also fill the role of Observers, a common programming pattern. For more information, read the Observer tutorial in the Game Programming Patterns ebook.

You can connect these signals to methods the same way you connect built-in signals of nodes like class_Button or class_RigidBody.

In the example below, we connect the health_depleted signal from a Character node to a Game node. When the Character node emits the signal, the game node’s _on_Character_health_depleted is called:

# Game.gd

func _ready():
   var character_node = get_node('Character')
   character_node.connect("health_depleted", self, "_on_Character_health_depleted")

func _on_Character_health_depleted():
   get_tree().reload_current_scene()

You can emit as many arguments as you want along with a signal.

Here is an example where this is useful. Let’s say we want a life bar on screen to react to health changes with an animation, but we want to keep the user interface separate from the player in our scene tree.

In our Character.gd script, we define a health_changed signal and emit it with Object.emit_signal(), and from a Game node higher up our scene tree, we connect it to the Lifebar using the Object.connect() method:

# 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
    emit_signal("health_changed", 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)
...

Note

To use signals, your class has to extend the Object class or any type extending it like Node, KinematicBody, Control

In the Game node, we get both the Character and Lifebar nodes, then connect the character, that emits the signal, to the receiver, the Lifebar node in this case.

# Game.gd

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

   character_node.connect("health_changed", lifebar_node, "_on_Character_health_changed")

This allows the Lifebar to react to health changes without coupling it to the Character node.

You can write optional argument names in parentheses after the signal’s definition:

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

These arguments show up in the editor’s node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of arguments when you emit signals; it’s up to you to emit the correct values.

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

GDScript can bind an array of values to connections between a signal and a method. When the signal is emitted, the callback method receives the bound values. These bound arguments are unique to each connection, and the values will stay the same.

You can use this array of values to add extra constant information to the connection if the emitted signal itself doesn’t give you access to all the data that you need.

Building on the example above, let’s say we want to display a log of the damage taken by each character on the screen, like Player1 took 22 damage.. The health_changed signal doesn’t give us the name of the character that took damage. So when we connect the signal to the in-game console, we can add the character’s name in the binds array argument:

# Game.gd

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

   character_node.connect("health_changed", battle_log_node, "_on_Character_health_changed", [character_node.name])

Our BattleLog node receives each element in the binds array as an extra argument:

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

Coroutines avec yield

GDScript offers support for coroutines via the yield built-in function. Calling yield() will immediately return from the current function, with the current frozen state of the same function as the return value. Calling resume() on this resulting object will continue execution and return whatever the function returns. Once resumed, the state object becomes invalid. Here is an example:

func my_func():
   print("Hello")
   yield()
   print("world")

func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print("my dear")
    y.resume()
    # 'y' resumed and is now an invalid state.

Will print:

Hello
my dear
world

It is also possible to pass values between yield() and resume(), for example:

func my_func():
   print("Hello")
   print(yield())
   return "cheers!"

func _ready():
    var y = my_func()
    # Function state saved in 'y'.
    print(y.resume("world"))
    # 'y' resumed and is now an invalid state.

Will print:

Hello
world
cheers!

Coroutines et signaux

The real strength of using yield is when combined with signals. yield can accept two arguments, an object and a signal. When the signal is received, execution will recommence. Here are some examples:

# Resume execution the next frame.
yield(get_tree(), "idle_frame")

# Resume execution when animation is done playing.
yield(get_node("AnimationPlayer"), "finished")

# Wait 5 seconds, then resume execution.
yield(get_tree().create_timer(5.0), "timeout")

Coroutines themselves use the completed signal when they transition into an invalid state, for example:

func my_func():
    yield(button_func(), "completed")
    print("All buttons were pressed, hurray!")

func button_func():
    yield($Button0, "pressed")
    yield($Button1, "pressed")

my_func ne poursuivra l’exécution que lorsque les deux boutons auront été pressés.

Mot-clé onready

When using nodes, it’s common to desire to keep references to parts of the scene in a variable. As scenes are only warranted to be configured when entering the active scene tree, the sub-nodes can only be obtained when a call to Node._ready() is made.

var my_label

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

This can get a little cumbersome, especially when nodes and external references pile up. For this, GDScript has the onready keyword, that defers initialization of a member variable until _ready() is called. It can replace the above code with a single line:

onready var my_label = get_node("MyLabel")

Mot-clé d’assertion

The assert keyword can be used to check conditions in debug builds. These assertions are ignored in non-debug builds. This means that the expression passed as argument won’t be evaluated in a project exported in release mode. Due to this, assertions must not contain expressions that have side effects. Otherwise, the behavior of the script would vary depending on whether the project is run in a debug build.

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

When running a project from the editor, the project will be paused if an assertion error occurs.