GDScript basics

Introduction

GDScript est un langage de haut niveau à typage dynamique utilisé pour créer du contenu. Il utilise une syntaxe similaire au langage Python (les blocs sont identifiés par l’indentation et une grande partie des mots clés sont similaires). Son but est d’être optimisé et étroitement intégré avec le moteur Godot, cela permettant une grande flexibilité pour la création et l’intégration de contenu.

Histoire

Au début, le moteur utilisait le langage de scripts Lua . Lua est rapide, mais créer des bindings (ou liaisons) vers un système orienté objet (en utilisant des fallbacks) était complexe, lent et demandait une énorme quantité de code. Après quelques expérimentations avec Python, il s’avéra aussi difficile a intégrer.

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] Opérateur d’indice, Priorité la plus haute
x.attribut Référence d’attribut
is Vérificateur de type d’instance
~ Opération bit-à-bit de négation
-x Négatif / Négation Unaire
* / %

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 / Concaténation de Tableaux
- Soustraction
<< >> Décalage de bits
& Conjonction logique bit-à-bit
^ Opération « Ou » exclusif bit-à-bit
| Disjonction logique bit-à-bit
< > == != >= <= Comparaisons
in Test de contenu
! not NOT booléen
and && AND booléenooléen
or || OR booléen
if x else if / else ternaire
= += -= *= /= %= &= |= Affectation, Priorité la plus basse

Littéraux

Littéraux Type
45 Entier en base 10 (décimal)
0x8F51 Entier en base 16 (hexadécimal)
3.14, 58.1e-10 Nombre à virgule flottante (réel)
"Bonjour", "Salut" Chaînes de caractères
"""Bonjour""" Chaîne de caractères multiligne
@"Node/Label" Chemin du nœud (NodePath) ou nom (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.

Les commentaires multilignes peuvent être créés en utilisant «  » » (trois guillemets de suite) au début et à la fin d’un bloc de texte. Noter que cela crée une chaîne de caractères, mais qu’elle ne sera pas séparée quand le script sera compilé.

""" Everything on these
lines is considered
a comment. """

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 quand elles sont passées comme arguments aux fonctions. Les seules exceptions sont les tableaux et les dictionnaires, qui sont passés par référence de sorte qu’ils sont partagés. (Cependant, pas les PoolArray``s comme ``PoolByteArray, ceux-ci sont passés par valeurs aussi, prenez donc ceci en considération lorsque vous décidez lesquels utiliser !)

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

Le type de donnée booléen ne peut contenir que true ou false.

int

Le type de donnée entier ne peut contenir que des nombres entiers, négatifs et positifs.

float

Utilisé pour contenir une valeur a virgule flottante (nombres réels).

String

Une séquence de caractère au format Unicode. Les chaînes de caractère peuvent contenir les séquences d’échappement de standard C. GDScript prend en charge les chaînes de format aussi connue comme fonctionnalité printf.

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

Type de rectangle 2D contenant deux attributs vecteurs : position et size (taille). Contiens alternativement un attribut end (fin) équivalant à position+size.

Vector3

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

Transform2D

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

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

Une boîte englobante alignée sur les axes (ou boîte 3D) deux attributs vecteurs : position et size (taille). Contiens alternativement un attribut end (fin) équivalant à 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

Séquence générique de types d’objets arbitraires. Sont inclus d’autres tableaux ou dictionnaires (voir ci-dessous). Le tableau peut être redimensionné dynamiquement. Les tableaux sont indexés en commençant par l’index 0. Depuis Godot 2.1, les indices peuvent être négatifs comme en Python, pour compter à 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].

Les tableaux GDScript sont alloués en mémoire de façon linéaire pour les performances. Les tableaux de très grosse taille (plus d’une dizaine de milliers d’éléments) peuvent cependant provoquer une fragmentation de la mémoire. S’il s’agit d’un problème à considérer, des types de tableaux spéciaux sont disponibles. Ceux-ci n’acceptent qu’un seul type de données. Ils permettent d’éviter une fragmentation de la mémoire, utilisent moins de mémoire, mais sont atomiques et ont tendance à être moins performants que les tableaux génériques. Leur usage n’est donc recommandé que pour de très larges ensembles de données :

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 :

  • Types intégrés (Array, Vector2, int, String, etc.)
  • Classes du moteur (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")).
  • Les autres classes dans le même script, en 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.

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

La conversion de type est également utile pour avoir de meilleures variables de types sûrs en interagissant avec des arbres :

# 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’énumération, cela mettra également toutes les valeurs à l’intérieur d’un dictionnaire constant 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.

Référencement des fonctions

Contrairement au Python, les fonctions ne sont pas des objets de première classe dans GDScript. Cela signifie qu’elles ne peuvent pas être stockées dans des variables, passées en argument à une autre fonction ou être renvoyées par d’autres fonctions. Ceci pour des raisons de performance.

Pour référencer une fonction par son nom au moment de l’exécution (par exemple pour la stocker dans une variable, ou la passer à une autre fonction en argument), il faut utiliser les aides call ou funcref:

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

Rappelez-vous que les fonctions par défaut telles que _init, et la plupart des notifications telles que _enter_tree, _exit_tree, _process, _physics_process, etc. sont appelées automatiquement dans toutes les classes de base. Il est donc uniquement nécessaire d’appeler la fonction explicitement lorsqu’elle est surchargée d’une manière ou d’une autre.

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. Ceci est principalement utile pour faire des bibliothèques de fonctions d’aide :

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

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

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

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.

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.

Syntaxe de base :

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

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

  1. Remplace switch par match
  2. Enlever 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 :

  • Les constantes

    Les constantes primitives, telles que les nombres et les chaînes de caractères

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

    Correspond au contenu d’une variable/énumération

    match typeof(x):
        TYPE_REAL:
            print("float")
        TYPE_STRING:
            print("text")
        TYPE_ARRAY:
            print("array")
    
  • Le caractère joker (wildcard)

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

    Elle 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 tbh.")
    
  • Expression de liaison

    Une expression de liaison introduit une nouvelle variable. Comme le caractère joker (wildcard), elle 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)
    
  • Expression de tableaux

    Elle correspond à un tableau. Chaque élément du modèle de tableau est un modèle 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")
    
  • Expression 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-motif

    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.

    Une expression de valeur est séparée de l’expression 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")
    
Expressions multiples :

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

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

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

A la place, vous pouvez donner un nom à votre classe pour l’enregistrer en tant que nouveau type dans l’éditeur de Godot. Pour cela, utilisez le mot clé “class_name”. Vous pouvez éventuellement ajouter une virgule suivie d’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

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

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

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 de base (c’est-à-dire une qui a été étendue par votre classe courante), préfixez . au nom de la fonction :

.basefunc(args)

Ceci est particulièrement utile parce que les fonctions d’extension de classes remplacent les fonctions portant le même nom dans leurs classes de base. Donc, si vous voulez quand même les appeler, vous pouvez utiliser . équivalent au mot-clé super dans d’autres langages :

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

Constructeur de classe

Le constructeur de classe, appelé instanciation de classe, est nommé _init. Comme mentionné précédemment, les constructeurs des classes parentes sont appelés automatiquement lors de l’héritage d’une classe. Il n’est donc généralement pas nécessaire d’appeler ._init() explicitement.

Contrairement à l’appel d’une fonction régulière comme dans l’exemple ci-dessus avec .some_func, si le constructeur de la classe héritée prend des arguments, ils sont passés comme ceci :

func _init(args).(parent_args):
   pass

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

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

  1. si la classe héritée (State.gd) définit un constructeur _init qui prend des arguments (e dans ce cas), alors la classe héritant (Idle.gd) a à définir aussi _init et à passer les paramètres appropriés à _init depuis State.gd
  2. Idle.gd peut avoir un nombre différent d’arguments que la classe de base State.gd
  3. dans l’exemple ci-dessus, e 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 de base 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().(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

Les membres de classe peuvent être exportés. Cela signifie que leur valeur est sauvegardée avec la ressource (par exemple la scène) à laquelle ils sont attachés. Ils seront également disponibles pour l’édition dans l’éditeur de propriétés. L’exportation se fait en utilisant le mot clé export :

extends Button

export var number = 5 # Value will be saved and visible in the property editor.

Une variable exportée doit être initialisée à une expression constante ou avoir une indication d’exportation sous la forme d’un argument au mot-clé d’exportation (voir ci-dessous).

L’un des avantages fondamentaux de l’exportation des variables membres est de les rendre visibles et modifiables dans l’éditeur. De cette façon, les artistes et les game designers peuvent modifier les valeurs qui influenceront plus tard le fonctionnement du programme. Pour cela, une syntaxe spéciale d’exportation est fournie.

# If the exported value assigns a constant or constant expression,
# the type will be inferred and used in the editor.

export var number = 5

# Export can take a basic data type as an argument, which will be
# used in the editor.

export(int) var number

# Export can also take a resource type to use as a hint.

export(Texture) var character_face
export(PackedScene) var scene_file

# Integers and strings hint enumerated values.

# Editor will enumerate as 0, 1 and 2.
export(int, "Warrior", "Magician", "Thief") var character_class
# Editor will enumerate with string names.
export(String, "Rebecca", "Mary", "Leah") var character_name

# Named Enum Values

# Editor will enumerate as THING_1, THING_2, ANOTHER_THING.
enum NamedEnum {THING_1, THING_2, ANOTHER_THING = -1}
export (NamedEnum) var x

# Strings as Paths

# String is a path to a file.
export(String, FILE) var f
# String is a path to a directory.
export(String, DIR) var f
# String is a path to a file, custom filter provided as hint.
export(String, FILE, "*.txt") var f

# Using paths in the global filesystem is also possible,
# but only in tool scripts (see further below).

# String is a path to a PNG file in the global filesystem.
export(String, FILE, GLOBAL, "*.png") var tool_image
# String is a path to a directory in the global filesystem.
export(String, DIR, GLOBAL) var tool_dir

# The MULTILINE setting tells the editor to show a large input
# field for editing over multiple lines.
export(String, MULTILINE) var text

# Limiting editor input ranges

# Allow integer values from 0 to 20.
export(int, 20) var i
# Allow integer values from -10 to 20.
export(int, -10, 20) var j
# Allow floats from -10 to 20, with a step of 0.2.
export(float, -10, 20, 0.2) var k
# Allow values y = exp(x) where y varies between 100 and 1000
# while snapping to steps of 20. The editor will present a
# slider for easily editing the value.
export(float, EXP, 100, 1000, 20) var l

# Floats with Easing Hint

# Display a visual representation of the ease() function
# when editing.
export(float, EASE) var transition_speed

# Colors

# Color given as Red-Green-Blue value
export(Color, RGB) var col # Color is RGB.
# Color given as Red-Green-Blue-Alpha value
export(Color, RGBA) var col # Color is RGBA.

# Another node in the scene can be exported, too.

export(NodePath) var node

Il faut noter que même si le script n’est pas exécuté alors qu’il est en cours d’édition, les propriétés exportées sont toujours modifiables (voir ci-dessous pour « outil »).

Exportation des indicateurs binaires

Les nombres entiers utilisés comme bits de drapeaux peuvent stocker plusieurs valeurs vrai/false (booléen) en une propriété. En utilisant l’indication d’exportation int, FLAGS, ils peuvent être changés dans l’éditeur :

# Individually edit the bits of an integer.
export(int, FLAGS) var spell_elements = ELEMENT_WIND | ELEMENT_WATER

Limiter les drapeaux à un certain nombre de drapeaux nommés est également possible. La syntaxe est très similaire à la syntaxe d’énumération :

# Set any of the given flags from the editor.
export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0

Dans cet exemple, Feu a la valeur 1, Eau a la valeur 2, Terre a la valeur 4 et Vent correspond à la valeur 8. Généralement, les constantes doivent être définies en conséquence (p. ex. const ELEMENT_VENT = 8 et ainsi de suite).

L’utilisation de drapeaux de bits nécessite une certaine compréhension des opérations sur les bits. En cas de doute, les variables booléennes doivent être exportées à la place.

Exportation de tableaux

L’exportation des tableaux fonctionne mais avec une mise en garde importante : alors que les tableaux réguliers sont créés en local pour chaque instance de classe, les tableaux exportés sont partagés entre toutes les instances. Cela signifie que le fait de les éditer dans une instance les fera changer dans toutes les autres instances. Les tableaux exportés peuvent avoir des initialisateurs, mais ceux-ci doivent être des expressions constantes.

# Exported array, shared between all instances.
# Default value must be a constant expression.

export var a = [1, 2, 3]

# Exported arrays can specify type (using the same hints as before).

export(Array, int) var ints = [1,2,3]
export(Array, int, "Red", "Green", "Blue") var enums = [2, 1, 0]
export(Array, Array, float) var two_dimensional = [[1, 2], [3, 4]]

# You can omit the default value, but then it would be null if not assigned.

export(Array) var b
export(Array, PackedScene) var scenes

# Typed arrays also work, only initialized empty:

export var vector3s = PoolVector3Array()
export var strings = PoolStringArray()

# Regular array, created local for every instance.
# Default value can include run-time values, but can't
# be exported.

var c = [a, 2, 3]

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

Chaque fois que la valeur de la variable est modifiée par une source externe (c’est-à-dire qui ne provient pas de l’utilisation locale dans la classe), la fonction setter (setterfunc ci-dessus) sera appelée. Ceci se produit avant que la valeur soit changée. Le setter doit décider quoi faire avec la nouvelle valeur. Inversement, lorsqu’on accède à la variable, la fonction getter (getterfunc ci-dessus) doit retourner la valeur désirée. En voici un exemple :

var myvar 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.

Les fonctions setter ou getter peuvent être omises :

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

Les Getters/Setters sont particulièrement utiles lors de l’exportation de variables vers l’éditeur dans les scripts d’outils ou des plugins, pour valider les entrées.

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

Les scripts, par défaut, 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 à l’intérieur de l’éditeur (tant qu’ils n’exécutent pas le code du jeu ou spécifiquement évitent de le faire). Pour cela, le mot-clé outil existe et doit être placé en haut du fichier :

tool
extends Button

func _ready():
    print("Hello")

Gestion de la mémoire

Si une classe hérite de 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 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

Les signaux sont un moyen d’envoyer des messages de notification depuis un objet pour lequel les autres objets peuvent écouter de façon générique. Créez des signaux personnalisés pour une classe en utilisant le mot-clé signal.

# Signal with no arguments
signal your_signal_name

# Signal with two arguments
signal your_signal_name_with_args(a, b)

Ces signaux peuvent être connectés à des méthodes de la même manière que vous pouvez connecter les signaux de base des nœuds tels que :ref: class_Button or :ref: class_RigidBody.

Voici un exemple qui créé un signal personnalisé dans un script et le connecte à une méthode dans un autre script, en utilisant la méthode Object.connect() :

# your_notifier.gd

signal data_found

var your_data = 42
# your_handler.gd

func your_handler():
   print("Your handler was called!")
# your_game.gd

func _ready():
   var notifier = your_notifier.new()
   var handler = your_handler.new()

   notifier.connect("data_found", handler, "your_handler")

GDScript peut lier des arguments à des connexions entre un signal et une méthode. Lorsque le signal est émis, en appelant la méthode connectée, l’argument lié est donné à la méthode. Ces arguments liés sont plus spécifiques a la connexion qu’au signal ou a la méthode, signifiant que chaque connexion a un lien unique.

Voici un exemple qui créé une connexion entre le signal pressed d’un bouton et une méthode, en attachant l’instance du bouton à la connexion. Le gestionnaire utilise l’argument attaché pour afficher quelle est l’instance de bouton qui a été appuyée.

func pressed_handler(which):
   print("A button was pressed! Button's name was:", which.get_name())

func _ready():
   for button in get_node("buttons").get_children()
      # Connect the button's 'pressed' signal to our 'pressed_handler' method
      # Bind the button to the connection so we know which button was pressed
      button.connect("pressed", self, "pressed_handler", [button])

Les signaux sont générés par la méthode Object.emit_signal(), qui diffuse le signal et les arguments.

Extension d’un exemple précédent pour qu’il utilise toutes les fonctionnalités des signaux de GDScript :

# your_notifier.gd

signal data_found(data)

var your_data = 42

func _process(delta):
   if delta == your_data:
      emit_signal("data_found", your_data)
# your_handler.gd

func your_handler(data, obj):
   print("Your handler was called from: ", obj.get_name(), " with data: ", data)
# your_game.gd

func _ready():
   var notifier = your_notifier.new()
   var handler = your_handler.new()

   notifier.connect("data_found", handler, "your_handler", [notifier])

Coroutines avec yield

GDScript offre un support pour les coroutines via la fonction intégrée yield. L’appel de yield() retournera immédiatement de la fonction courante, avec l’état gelé courant de cette même fonction comme valeur de retour. L’appel de resume sur cet objet résultant reprendra l’exécution et retournera tout ce que la fonction retourne. Une fois repris, l’objet d’état devient invalide. Voici un exemple :

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.

Affichera :

Hello
my dear
world

Il est également possible de passer des valeurs entre yield() et resume(), par exemple :

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.

Affichera :

Hello
world
cheers!

Coroutines et signaux

La force réelle de l’utilisation de yield est lorsqu’il est combiné avec des signaux. yield peut accepter deux paramètres, un objet et un signal. Lorsque le signal est reçu, l’exécution reprend. Voici quelques exemples :

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

Les coroutines elles-mêmes utilisent le signal completed lorsqu’elles passent à un état invalide, par exemple :

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

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 encombrant, surtout lorsque les nœuds et les références externes s’acumulent. Pour cela, GDScript a le mot-clé onready, qui reporte 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")

Mot-clé d’assertion

Le mot-clé assert peut être utilisé pour vérifier des conditions dans les constructions de binaires de débogage. Ces assertions sont ignorées dans les constructions de binaires autres que de débogage.

# Check that 'i' is 0.
assert(i == 0)