Typage statique en GDScript

Dans ce guide, vous apprendrez :

  • Comment utiliser les types en GDScript
  • Les types statiques peuvent aider à éviter les bugs

Vous êtes libre de choisir où et comment utiliser cette nouvelle fonctionnalité linguistique: vous ne pouvez l’utiliser que dans certains fichiers sensibles GDScript, l’utiliser partout ou écrire du code comme vous l’avez toujours fait !

Les types statiques peuvent être utilisés sur des variables, constantes, fonctions, paramètres et types de retour.

Note

Le GDScript typé est disponible depuis Godot 3.1.

Un bref aperçu de la saisie statique

Avec le GDScript typé, Godot peut détecter encore plus d’erreurs lors de l’écriture du code! Cela vous donne, à vous et à vos coéquipiers, plus d’informations pendant que vous travaillez, les types d’arguments apparaissant lorsque vous appelez une méthode.

Imaginons que vous programmez un système d’inventaire. Vous codez un noeud `` Item``, puis un `` Inventaire``. Pour ajouter des éléments à l’inventaire, les personnes qui travaillent avec votre code doivent toujours passer un `` Item`` à la méthode `` Inventaire.add``. Avec les types, vous pouvez imposer ceci :

# In Item.gd
class_name Item

# In Inventory.gd
class_name Inventory

func add(reference: Item, amount: int = 1):
    var item = find_item(reference)
    if not item:
        item = _instance_item_from_db(reference)
    item.amount += amount

Un autre avantage significatif du GDScript typé est le nouveau ** système d’avertissement **. Depuis la version 3.1, Godot vous avertit lors de la rédaction de votre code: le moteur identifie des sections de votre code susceptibles de générer des problèmes lors de l’exécution, mais vous permet de décider si vous souhaitez ou non laisser le code tel quel. Vous en saurez plus dans un instant.

Les types statiques vous offrent également de meilleures options de complétion du code. Ci-dessous, vous pouvez voir la différence entre les options de complétion de type dynamique et de type statique pour une classe appelée PlayerController.

Vous avez probablement déjà stockée un nœud dans une variable et tapé un point pour se retrouver avec aucune suggestion de saisie semi-automatique :

code completion options for dynamic

Cela est dû au code dynamique. Godot ne peut pas savoir quel type de nœud ou de valeur vous transmettez à la fonction. Cependant, si vous écrivez le type explicitement, vous obtiendrez toutes les méthodes et variables publiques à partir du nœud :

code completion options for typed

À l’avenir, le GDScript typé augmentera également les performances du code : la compilation en temps réel et d’autres améliorations du compilateur sont déjà sur la feuille de route !

Dans l’ensemble, la programmation typée vous donne une expérience plus structurée. Elle aide à prévenir les erreurs et améliore l’aspect d’auto-documentation de vos scripts. Ceci est particulièrement utile lorsque vous travaillez en équipe ou sur un projet à long terme : des études ont montré que les développeurs passent la plupart de leur temps à lire le code d’autres personnes, ou des scripts qu’ils ont écrits dans le passé et ont oubliés. Plus le code est clair et structuré, plus il est rapide à comprendre, plus vous pouvez travailler rapidement.

Comment utiliser le typage statique

Pour définir le type d’une variable ou d’une constante, écrivez deux points après le nom de la variable, suivi de son type. Par exemple : var health: int. Ceci force le type de la variable à rester toujours le même :

var damage: float = 10.5
const MOVE_SPEED: float = 50.0

Godot essaiera de déduire les types si vous écrivez deux points, mais vous omettez le type :

var life_points := 4
var damage := 10.5
var motion := Vector2()

Actuellement, vous pouvez utiliser trois types de… types :

  1. Built-in
  2. Classes et nœuds de base (Object, Node, Area2D, Camera2D, etc.)
  3. Vos propres classes personnalisées. Regarder la nouvelle fonctionnalité: ref: class_name <doc_scripting_continued_class_name> pour enregistrer des types dans l’éditeur.

Note

Vous n’avez pas besoin d’écrire des indicateurs de type pour les constantes, car Godot le définit automatiquement à partir de la valeur attribuée. Mais vous pouvez toujours le faire pour clarifier votre code.

Types de variables personnalisées

Vous pouvez utiliser n’importe quelle classe, y compris vos classes personnalisées, en tant que types. Il y a deux façons de les utiliser dans des scripts. La première méthode consiste à précharger le script que vous voulez utiliser comme type dans une constante :

const Rifle = preload('res://player/weapons/Rifle.gd')
var my_rifle: Rifle

La deuxième méthode consiste à utiliser le mot-clé `` nom_classe`` lors de la création. Pour l’exemple ci-dessus, votre Rifle.gd ressemblerait à ceci :

extends Node2D
class_name Rifle

Si vous utilisez `` nom_classe``, Godot enregistre globalement le type Rifle dans l’éditeur et vous pouvez l’utiliser n’importe où, sans avoir à le précharger dans une constante :

var my_rifle: Rifle

Casting de variable

Le typage est un concept clé dans les langages typés. Le casting est la conversion d’une valeur d’un type à un autre.

Imaginez un ennemi dans votre jeu, qui « hérite de Area2D ». Vous voulez qu’il entre en collision avec le joueur, un KinematicBody2D``avec un script appelé ``PlayerController qui lui est attaché. Vous utilisez le signal « on_body_entered » pour détecter la collision. Avec le code qui est écrit, le corps que vous détecterez sera un PhysicsBody2D``générique, et non votre ``PlayerController via le rappel on_body_entered.

Vous pouvez vérifier si ce PhysicsBody2D` est votre joueur avec le mot-clé de conversion as , et utiliser à nouveau les deux points : pour forcer la variable à utiliser ce type. Ceci force la variable à s’en tenir au type PlayerController :

func _on_body_entered(body: PhysicsBody2D) -> void:
    var player := body as PlayerController
    if not player:
        return
    player.damage()

Comme il s’agit d’un type personnalisé, si body n’hérite pas de PlayerController`, la variable playersera réglée sur null. Nous pouvons utiliser cela pour vérifier si body est le joueur ou non. Nous obtiendrons également l’auto-complétion complète de la variable player grâce à cette conversion.

Note

Si vous essayez de convertir avec un type intégré et que cela échoue, Godot lancera une erreur.

Lignes sécurisées

Vous pouvez également utiliser la conversion de type pour assurer la sécurité des lignes. Les lignes sécurisées sont un nouvel outil dans Godot 3.1 pour vous avertir lorsque des lignes de code ambiguës sont type-sûr. Comme vous pouvez mélanger et faire correspondre du code typé et du code dynamique, Godot n’a parfois pas assez d’informations pour savoir si une instruction va déclencher une erreur ou non au moment de l’exécution.

Cela se produit lorsque vous avez un nœud enfant. Prenons un timer par exemple : avec du code dynamique, vous pouvez obtenir le nœud avec $Timer. GDScript supporte le duck-typing, donc même si votre timer est de type Timer`, c’est aussi un Node et un Object, deux classes auxquelles il appartient. Avec le GDScript dynamique, vous ne vous souciez pas non plus du type du nœud tant qu’il possède les méthodes que vous avez besoin d’appeler.

Vous pouvez utiliser la conversion de type pour dire à Godot le type que vous attendez lorsque vous obtenez un nœud : ($Timer comme Timer)``, ($Player comme KinematicBody2D), etc. Godot s’assurera que le type fonctionne et si c’est le cas, le numéro de ligne deviendra vert à gauche de l’éditeur de script.

Safe vs Unsafe Line

Lignes sécurisées contre lignes non-sécurisées

Note

Vous pouvez désactiver les lignes sécurisées ou modifier leur couleur dans les paramètres de l’éditeur.

Définissez le type de retour d’une fonction à l’aide de la flèche ->

Pour définir le type de retour d’une fonction, écrivez un tiret et un signe supérieur -> après sa déclaration, suivi du type de retour :

func _process(delta: float) -> void:
    pass

Le type void signifie que la fonction ne renvoie rien. Vous pouvez utiliser n’importe quel type, comme pour les variables :

func hit(damage: float) -> bool:
    health_points -= damage
    return health_points <= 0

Vous pouvez également utiliser vos propres nœuds comme types de retour :

# Inventory.gd

# Adds an item to the inventory and returns it.
func add(reference: Item, amount: int) -> Item:
    var item: Item = find_item(reference)
    if not item:
        item = ItemDatabase.get_instance(reference)
    item.amount += amount
    return item

Typé ou dynamique: s’en tenir à un style

Le GDScript typé et le GDScript dynamique peuvent coexister dans le même projet. Mais il est préférable de s’en tenir à l’un ou l’autre pour assurer la cohérence de votre base de code et pour vos pairs. Il est plus facile pour tout le monde de travailler ensemble si vous suivez les mêmes instructions, et plus rapidement pour lire et comprendre le code des autres.

Le code typé prend un peu plus de temps à écrire, mais vous bénéficiez des avantages décrits plus haut. Voici un exemple du même script vide, dans un style dynamique :

extends Node
    func _ready():
        pass
    func _process(delta):
        pass

Et avec le typage statique :

extends Node
    func _ready() -> void:
        pass
    func _process(delta: float) -> void:
        pass

Comme vous pouvez le voir, vous pouvez également utiliser des types avec les méthodes virtuelles du moteur. Les rappels de signaux, comme toutes les méthodes, peuvent également utiliser des types. Voici un signal « body_entered » dans un style dynamique :

func _on_Area2D_body_entered(body):
    pass

Et le même rappel, avec des indications de type :

func _on_area_entered(area: CollisionObject2D) -> void:
    pass

Vous êtes libre de remplacer, par exemple, le CollisionObject2D, par votre propre type, pour convertir les paramètres automatiquement :

func _on_area_entered(bullet: Bullet) -> void:
    if not bullet:
        return
    take_damage(bullet.damage)

La variable « Bullet » pourrait contenir n’importe quel « CollisionObject2D » ici, mais nous nous assurons que c’est notre « Bullet », un nœud que nous avons créé pour notre projet. Si c’est autre chose, comme un Area2D, ou n’importe quel nœud qui n’hérite pas Bullet`, la variable bullet sera null.

Système d’avertissement

Le système d’avertissement complète le GDScript typé. Il est là pour vous aider à éviter les erreurs difficiles à repérer pendant le développement, qui peuvent mener à des erreurs d’exécution.

Vous pouvez configurer les avertissements dans les paramètres du projet dans une nouvelle section intitulée `` GDScript`` :

warning system project settings

système d’avertissement et paramètres de projet

Vous pouvez trouver une liste des avertissements pour le fichier GDScript actif dans la barre d’état de l’éditeur de script. L’exemple ci-dessous comporte 3 avertissements :

warning system example

exemple de système d’avertissement

Pour ignorer des avertissements spécifiques dans un fichier, insérez un commentaire spécial de la forme #warning-ignore:warning-id`, ou cliquez sur le lien ignorer à droite de la description de l’avertissement. Godot ajoutera un commentaire au-dessus de la ligne correspondante et le code ne déclenchera plus l’avertissement correspondant :

warning system ignore example

exemple où le système d’avertissement est ignoré

Les avertissements n’empêcheront pas le jeu de fonctionner, mais vous pouvez les transformer en erreurs si vous le souhaitez. De cette façon, votre jeu ne sera pas compilé à moins que vous ne corrigiez tous les avertissements. Allez à la section GDScript de la section Paramètres du projet pour activer cette option. Voici le même fichier que l’exemple précédent avec les avertissements d’erreurs activées :

warnings as errors

les avertissements en tant qu’erreurs

Cas où vous ne pouvez pas spécifier de types

Pour conclure cette introduction, couvrons quelques cas où vous ne pouvez pas utiliser d’indices de type. Tous les exemples ci-dessous déclenchent des erreurs.

Vous ne pouvez pas utiliser les Enums comme types :

enum MoveDirection {UP, DOWN, LEFT, RIGHT}
var current_direction: MoveDirection

Vous ne pouvez pas spécifier le type de membres individuels dans un tableau. Cela vous donnera une erreur :

var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy]

Vous ne pouvez pas forcer l’affectation de types dans une boucle `` for``, car chaque élément sur lequel le mot-clé `` for`` est déjà associé a un type différent. Donc, vous ** ne pouvez pas ** écrire :

var names = ['John', 'Marta', 'Samantha', 'Jimmy']
for name: String in names:
    pass

Deux scripts ne peuvent pas dépendre l’un de l’autre de manière cyclique :

# Player.gd
extends Area2D
class_name Player

var rifle: Rifle
# Rifle.gd
extends Area2D
class_name Rifle

var player: Player

Résumé

Le GDScript typé est un outil puissant. Disponible depuis la version 3.1 de Godot, il aide à écrire du code plus structuré, à éviter les erreurs courantes et à créer des systèmes évolutifs. À l’avenir, les types statiques apporteront également de belles performances grâce aux optimisations à venir du compilateur.