Dans Godot, chaque noeud peut avoir un script qui définit son comportement.
Le script hérite toujours d’une classe de noeud (comme
Node2D, CharacterBody2D, etc.) et peut
redéfinir des fonctions virtuelles pour réagir aux événements du
moteur.
Un noeud ne peut avoir qu’un seul script attaché à la fois.
Voici un exemple de script simpliste pour un personnage en vue du dessus.
extends CharacterBody2D
@export var speed: float = 200
func _ready() -> void:
motion_mode = CharacterBody2D.MOTION_MODE_FLOATING
$AnimationPlayer.play("spawn")
func _physics_process(delta: float) -> void:
var direction = Input.get_vector(
"move_west",
"move_east",
"move_north",
"move_south",
)
velocity = direction * speed
move_and_slide()Le moteur appelle automatiquement certaines fonctions spéciales durant le cycle de vie d’un noeud. Ces fonctions, dites virtuelles, peuvent être redéfinies dans votre script pour contrôler le comportement du noeud.
Voici les quatre fonctions les plus communément utilisées :
| Fonction | Appelée quand ? |
|---|---|
_ready() |
Une seule fois, quand le nœud est ” prêt “” (lui et tous ses enfants sont entrés dans l’arbre du jeu). Idéal pour l’initialisation. |
_input(event) |
À chaque événement d’entrée (clavier, souris, etc.) |
_process(delta) |
À chaque frame (delta = temps écoulé depuis la
dernière frame) |
_physics_process(delta) |
À intervalle fixe (60 FPS par défaut), utilisé pour la physique |
C’est grosso-modo le même principe que Awake,
Update, FixedUpdate etc. dans
Unity.
Les annotations en GDScript sont des mots-clés
précédés du symbole @ qui jouent le rôle de
modificateur.
Elles permettent d’indiquer à l’éditeur comment traiter notre code, notamment pour exposer des variables dans l’inspecteur.
Il existe de nombreuses
annotations, mais la plus intéressante est l’annotation
@export.
Celle-ci permet de rendre une variable modifiable directement depuis l’inspecteur de l’éditeur, sans avoir à changer le code. Elle se place devant la déclaration de la variable :
@export var <nom_de_la_variable>: <type_de_la_variable> = <valeur_par_défaut>La valeur définie dans l’inspecteur remplace la valeur par défaut écrite dans le script. Cela permet d’avoir plusieurs noeud qui utilisent le même script, mais avec des réglages différents.
@export var speed: float = 300.0
@export var max_health: int = 100
@export var player_name: String = "Noé"Ces trois variables apparaissent dans l’inspecteur du noeud, et peuvent être ajustées sans ouvrir le script.
Un jeu Godot étant composé de noeuds, on aura, en tout logique besoin de les manipuler depuis notre le code.
Que ce soit pour déclencher une animation, désactiver une collision, jouer un son etc., il faudra qu’on puisse accéder aux autres noeuds dans l’arbre.
Il est possible de référencer les noeuds de différentes façon :
Comme expliqué dans Noeuds et Scènes, les noeuds peuvent être accédés à l’aide de chemins, comme pour des fichiers.
Pour ce faire on utilise la fonction built-in
get_node(path) ou le raccourci $.
extends Node2D
func _ready() -> void:
# Accès à noeud via get_node
var animation_player: AnimationPlayer = get_node("Zone/AnimationPlayer")
animation_player.play()
# Accès à noeud en utilisant la syntaxe $ (équivalent à get_node)
var collision: CollisionShape2D = $Zone/Collision
collision.disabled = trueLe raccourci $ est plus performant que
get_node(path), donc il est à privilégier si on fait des
accès par chemin.
Bien que pratique, l’accès par chemin rend le script directement dépendant d’une structure d’aboresence :
La façon privilégié pour accéder les noeuds, c’est la référence directe.
class_namePar défaut, les scripts GDScript sont anonymes.
L’annotation class_name permet de donner un nom global à
votre classe, la rendant accessible partout dans votre projet et visible
dans l’éditeur.
class_name Player
extends CharacterBody2D
@export var speed: float = 300Une fois nommée, vous pouvez référencer cette classe dans d’autres scripts et elle apparaîtra dans la liste des types disponibles lors de la création de noeuds.
L’opérateur as permet de caster une variable
vers un certain type. Si la conversion échoue, la variable vaudra
null.
func _ready() -> void:
var node = get_node("Player")
var player := node as Player
if player:
player.speed = 300L’opérateur := permet de déduire automatiquement le type
de la variable à partir de sa valeur d’initialisation, évitant ainsi de
répéter le type explicitement.
Les getters et setters permettent d’exécuter du code personnalisé lors de la lecture ou de l’écriture d’une variable. C’est utile pour valider des valeurs, déclencher des événements ou mettre à jour l’interface.
var health: int = 100:
set(value):
health = clamp(value, 0, 100)
update_health_bar()
get:
return health
var speed: float = 200.0:
set(value):
if value < 0:
get_tree().quit() # negative speed is punished by closing the game
speed = value
var is_alive: bool:
get:
return health > 0
func update_health_bar() -> void:
$HealthBar.value = health@exportL’annotation @export permet d’exposer des variables dans
l’inspecteur de Godot, les rendant modifiables
directement depuis l’éditeur sans toucher au code.
@export_group("Stats")
@export var health: int = 100
@export var speed: float = 200.0Vous pouvez également exporter des références directes aux noeuds, évitant ainsi l’utilisation de chemins :
@export var animation_player: AnimationPlayer
@export var sprite: Sprite2D
func _ready() -> void:
# Plus besoin de $AnimationPlayer
animation_player.play("idle")
sprite.modulate = Color.RED@toolL’annotation @tool rend un script exécutable dans
l’éditeur, avant même de lancer le jeu. C’est utile pour créer des
outils personnalisés ou visualiser des changements en temps réel.
@tool
extends Node2D
@export var radius: float = 50.0:
set(value):
radius = value
resize()
func resize() -> void:
var shape := $Collision.shape as CircleShape2D
shape.radius = radiusAvec @tool, votre code s’exécute dans l’éditeur, c’est
très puissant mais c’est aussi un moyen de se tirer une balle dans le
pied, donc à utiliser avec précaution.
preloadLa fonction preload charge une resource au moment de la
compilation du script, garantissant qu’elle est disponible
immédiatement.
const BULLET_SCENE = preload("res://scenes/bullet.tscn")
const EXPLOSION_SOUND = preload("res://sounds/explosion.wav")
func shoot() -> void:
var bullet = BULLET_SCENE.instantiate()
add_child(bullet)Pour un chargement dynamique (au moment de l’exécution), utilisez
plutôt load().
L’inconvénient de preload est qu’il rend le temps de
chargement initial plus long, mais load, qui réalise le
chargement sur le moment même, peut produire des lags.
Dans des scénarios plus compliqués, on pourra utiliser des méthodes
de chargement de ResourceLoader et afficher une barre de
chargement.
queue_free et freeQuand un noeud n’est plus nécessaire, il faut le supprimer pour libérer la mémoire.
queue_free() : Supprime le noeud à la
fin de la frame actuelle (méthode recommandée)free() : Supprime le noeud
immédiatement (peut causer des bugs si d’autres scripts y font encore
référence)func destroy_enemy() -> void:
queue_free() # Supprime l'ennemiPour améliorer la lisibilité du code je vais vous demander de suivre des conventions de nommage “officielles” :
snake_case
(ex: player_health, is_enabled)UPPER_SNAKE_CASE (ex:
MAX_HEALTH, GRAVITY)PascalCase (ex:
AnimatedButton, Enemy)PascalCase (ex:
Player, MainMenu)Pour encore plus de propreté je vous invite à installer le formateur GDScript, téléchargeable ici : https://github.com/GDQuest/GDScript-formatter/releases
@export aux chemins de noeuds
pour éviter les erreurs si vous renommez des noeuds.