📜 Scripting

Comme expliqué en introduction, nous allons utiliser le langage GDScript intégré à Godot. Le GDScript est un langage dynamique orienté objet, à la syntaxe similaire à Python. Le langage est étroitement intégré au moteur, le rendant particulièrement agréable à utiliser.

Documenation : https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html

Syntaxe

Variables

Les variables sont dynamiques, une fois une variable déclarée on peut lui assigner n’importe quelle valeur de n’importe quel type. Il est néanmoins possible (et recommandé) de typer les variables statiquement, en précisant le type à la déclaration.

# Déclaration sans initialisation
var my_variable # null par défaut

# Déclaration avec initialisation
var my_variable_initialized = "une mangue"

# Déclaration avec typage statique et initialisation
var my_variable_initialized_typed: Vector2 = Vector2(1, 0)

# Déclaration d'une constante, sa valeur ne pourra pas être modifiée ultérieurement
const MY_CONSTANT: float = 4.2 

Structures de données

# Tableaux
var tableau_mixte = ["Godot", 4.2, true]
var tableau_type: Array[int] = [2, 0, 2]
tableau_type.append(5)
print(tableau_mixte[1]) # affiche 4.2
print(tableau_type) # Affiche [2, 0, 2, 5]

# Dictionnaires
var bazar = {
    "key": 3629,
    5: "le nombre cinq",
    "tableau": [1,2,3,4]
}
var descriptions: Dictionary[String, String] = {
    "sword" : "Awesome combat sword",
    "shield" : "Rusty useless shield",
    "coffe" : "COFFE? WHERE? I NEED IT."
}

Instructions conditionnelles

if score > high_score and not is_in_godmode:
    score = high_score
    print("Bravo!")

if result >= 0.5:
    print("This is great!")
elif result >= 0.25:
    print("Things are going side ways...")
else:
    print("We are doomed!")

Boucles

# Boucles for
for i in range(10):
    print(i)

var liste: Array[String] = ["Rubika", "Game", "Programming"] 
for mot in liste:
    print(mot)

# Boucle while
var counter: int = 0
while counter < 10 :
    print(counter)
    counter += 1

Fonctions

func get_welcome_prompt() -> String:
    return "WELCOME $> "

func print_n_time(n: int, message: String) -> void:
    for i in range(n):
        print(message)

Annotations

Les annotations en GDScript sont des mots-clés précédés du symbole @ qui jouent de rôle de modificateur. Elles permettent d’indiquer à l’éditeur comment traiter notre code, par exemple, pour exposer des variables dans l’inspecteur. Nous verrons les annotations les plus courantes dans les sections suivantes.

Scripting des noeuds

Dans Godot, chaque noeud peut avoir un script qui définit son comportement. Un noeud ne peut avoir qu’un seul script attaché à la fois. 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.

Voici un exemple de script simpliste pour un personnage en vue du dessus top-down.

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_noth",
        "move_south",
    )
    move_and_slide(direction * speed)

Cycle de vie d’un script

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 quatres fonctions les plus communément utilisées :

C’est grosso-modo le même principe que Awake, Update, FixedUpdate etc. dans Unity.

Accéder autres noeuds de l’arbre

Comme expliqué dans Noeuds et Scènes, les noeud peuvent être accédés à l’aide de chemins, comme pour des fichiers.

Pour ce faire nous allons utiliser la fonction get_node(path) ou plutĂ´t le racourci $.

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 = true

Autre fonctionnalités

Nommer une classe avec class_name

Par 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 = 300

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

Casting

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 = 300

L’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.

Getters et Setters

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

Exposer des variables avec @export

L’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.0

Vous 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

Script exécutable en mode édition avec @tool

L’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 = radius

Avec @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.

Précharger des resources avec preload

La 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é, on pourra utiliser des methodes de chargement de ResourceLoader et afficher une barre de chargement.

Détruir un objet avec queue_free et free

Quand un noeud n’est plus nécessaire, il faut le supprimer pour libérer la mémoire.

func destroy_enemy() -> void:
    queue_free() # Supprime l'ennemi

Conseils et bonnes pratiques

Pour améliorer la lisibilité du code je vais vous demander de suivre des conventions de nommage “officielles” :

Pour encore plus de propreté je vous invite à installer le formatteur GDScript, téléchargeable ici : https://github.com/GDQuest/GDScript-formatter/releases

Quelques bonnes pratiques