Jour 1 — Premiers pas en programmation avec Godot#
D'habitude, ce workshop d'initiation se fait avec Processing, un outil pensé pour découvrir la programmation en dessinant. Ce cours tente d'appliquer la même démarche dans Godot, pour vous familiariser avec ce moteur. Ce n'est pas une présentation complète de l'environnement Godot : on s'en sert juste comme d'une feuille de dessin programmable.
Aucune connaissance en programmation n'est nécessaire. On part d'un simple cercle, et on ajoute une seule idée nouvelle à chaque étape.
| Étape | Ce qu'on fait | Ce qu'on apprend |
|---|---|---|
| 0 | Créer un projet, afficher un message | instruction, script, console |
| 1 | Dessiner un cercle coloré | coordonnées, variable, fonction |
| 2 | Le faire trembler au hasard | boucle de jeu, hasard, condition |
| 3 | Créer des baballes en cliquant | événement, liste, boucle for, classe |
| 4 (si on a le temps) | Des baballes en image avec Sprite2D |
héritage, nœuds de Godot |
Comment lire ce document
- Les blocs de code sont à recopier (ou copier-coller) dans Godot.
- Les Défis sont des petites variations à essayer seul.e ou à deux. La solution est cachée sous « Solution » : essayez d'abord !
- Les défis essentiel sont pour tout le monde. Les défis bonus sont pour celles et ceux qui ont fini en avance : on peut les sauter sans problème pour la suite.
- Copier-coller : utilisez de préférence le bouton Copier d'un bloc de code entier. Si vous ne copiez qu'une partie du code, les tabulations du début de ligne peuvent se perdre ou se mélanger avec des espaces, et Godot affiche une erreur (Mixed use of tabs and spaces). Pour réparer : dans Godot, menu Édition → Indentation → Convertir l'indentation en tabulations, puis vérifiez que les lignes sont bien décalées.
- Casser le code fait partie du jeu. Une erreur en rouge n'abîme rien : lisez-la, corrigez, relancez.
- Dans le code, ce qui est en français, c'est nous qui l'avons inventé (
rayon,couleur,Baballe…). Ce qui est en anglais vient de Godot (draw_circle,Color,Vector2,position…).
Ressources :
- Site de Godot : https://godotengine.org/fr/
- Pour s'entraîner à la maison : Learn to Code From Zero with Godot (GDQuest, gratuit, dans le navigateur)
Étape 0 — Créer un projet et dire bonjour#
-
Ouvrez Godot et créez un nouveau projet.

-
Choisissez Scène 2D : Godot crée un nœud
Node2D, la « toile » sur laquelle on va dessiner.
-
Attachez-lui un script (clic droit sur le nœud → Attacher un script, langage GDScript) :

-
Effacez tout ce qu'il y a dans le script et écrivez :
extends Node2D
func _ready():
print("Bonjour !")
-
Lancez le jeu avec le bouton ▶ en haut à droite. Godot demande quelle scène lancer : cliquez sur Choisir l'actuelle, puis enregistrez.

Une fenêtre grise s'ouvre… et « Bonjour ! » s'affiche dans l'onglet Sortie, en bas de l'éditeur.

Ce qu'on vient d'apprendre
- Un script est une liste d'instructions que l'ordinateur exécute dans l'ordre.
print(...)affiche un message dans la console. C'est notre meilleur outil pour comprendre ce qui se passe.func _ready():est une fonction : un bloc d'instructions avec un nom. Godot exécute_readyune fois, au démarrage.- Le décalage (touche Tab) au début de la ligne indique que
_ready. En GDScript, ce décalage est obligatoire.
Défi essentiel : affichez trois messages différents, sur trois lignes.
Solution du défi
extends Node2D
func _ready():
print("Bonjour !")
print("Je m'appelle Godot.")
print("J'apprends à programmer.")
Chaque print affiche une ligne. Les trois instructions sont exécutées dans l'ordre, de haut en bas.
Étape 1 — Afficher un cercle coloré#
Où est quoi ? Les coordonnées#
Chaque point de la fenêtre a une adresse : x (horizontal) et y (vertical), en pixels.
(0, 0) ────────────► x
│
│ ● (400, 300)
│
▼
y
Attention, piège : le point (0, 0) est en haut à gauche, et y augmente vers le bas (l'inverse des maths au lycée).
Le code#
extends Node2D
var rayon = 20 # taille en pixels
var couleur = Color.ORANGE # couleur de remplissage
var couleur_fond = Color("#1d1b33") # couleur du fond de la fenêtre
var position_cercle = Vector2(400, 300) # où placer le cercle (x, y)
func _ready():
RenderingServer.set_default_clear_color(couleur_fond) # couleur du fond
func _draw():
draw_circle(position_cercle, rayon, couleur)
Lancez : un disque orange, plein, sans bord, sur un fond bleu nuit.
Comment ça marche ?
varcrée une variable : une boîte avec un nom, dans laquelle on range une valeur. Ici, quatre boîtes :rayon,couleur,couleur_fondetposition_cercle.Vector2(400, 300): deux nombres rangés ensemble, x et y. On lit x avecposition_cercle.xet y avecposition_cercle.y.- Pourquoi pas simplement
position? Ce nom est déjà pris par Godot (chaque nœud a sa propreposition) : on choisit donc un autre nom pour notre variable. RenderingServer.set_default_clear_color(...)choisit la couleur avec laquelle Godot efface l'écran avant chaque image : c'est la couleur du fond. (On peut aussi la régler sans code : Projet → Paramètres du projet → Rendu → Environnement → Default Clear Color ; activez les « Paramètres avancés » si l'option n'apparaît pas.)_draw()est une fonction que Godot appelle quand il faut dessiner.draw_circle(où, quelle taille, quelle couleur)dessine un disque rempli.- Tout ce qui suit
#est un commentaire : Godot l'ignore, il sert aux humains.
Les couleurs#
Plusieurs façons d'écrire une couleur :
| Écriture | Exemple |
|---|---|
| Un nom anglais | Color.DEEP_PINK, Color.TURQUOISE, Color.GOLD |
| Rouge, vert, bleu entre 0 et 1 | Color(1, 0.4, 0.7) |
| Un code comme en graphisme | Color("#ff66aa") |
| Avec de la transparence (4ᵉ nombre) | Color(1, 0.4, 0.7, 0.5) |
D'autres formes : carrés, lignes et texte#
Dans _draw, on peut dessiner bien plus que des cercles :
func _draw():
draw_circle(position_cercle, rayon, couleur)
draw_rect(Rect2(440, 280, 40, 40), Color.TURQUOISE) # carré
draw_line(Vector2(320, 350), Vector2(480, 350), Color.WHITE, 3) # ligne
draw_string(ThemeDB.fallback_font, Vector2(320, 260), "Bonjour !", HORIZONTAL_ALIGNMENT_LEFT, -1, 24, Color.GOLD) # texte
draw_rect(Rect2(x, y, largeur, hauteur), couleur): un rectangle, placé par son coin haut-gauche (et non par son centre comme le cercle).draw_line(départ, arrivée, couleur, épaisseur): une ligne entre deux points.draw_string(police, position, texte, alignement, largeur, taille, couleur): du texte.ThemeDB.fallback_fontest la police par défaut de Godot,-1veut dire « pas de largeur maximale ».
Défis#
- essentiel Changez la couleur et la taille du cercle, et la couleur du fond.
- essentiel Dessinez un deuxième cercle, plus petit, d'une autre couleur, collé au premier (ajoutez une ligne dans
_draw, par exemple enposition_cercle + Vector2(30, 0)). - bonus Placez le cercle pile au centre de la fenêtre, quelle que soit sa taille.
Solution du défi 1 : couleurs et taille
Il suffit de changer les valeurs rangées dans les variables, en haut du script :
var rayon = 50 # plus grand
var couleur = Color.DEEP_PINK # une autre couleur
var couleur_fond = Color("#ffe066") # un fond jaune
C'est l'intérêt des variables : on change une valeur à un seul endroit, et tout le programme suit.
Solution du défi 2 : un deuxième cercle
func _draw():
draw_circle(position_cercle, rayon, couleur)
draw_circle(position_cercle + Vector2(30, 0), 10, Color.TURQUOISE) # 2e cercle, à droite du premier
Le premier cercle a un rayon de 20 et le deuxième un rayon de 10 : en plaçant le centre du deuxième à 20 + 10 = 30 pixels à droite du centre du premier, les deux cercles se touchent. position_cercle + Vector2(30, 0) veut dire « 30 pixels plus à droite que le premier cercle » : si on déplace le premier, le deuxième suit.
Solution du défi 3 : pile au centre
func _ready():
RenderingServer.set_default_clear_color(couleur_fond)
position_cercle = get_viewport_rect().size / 2 # taille de la fenêtre divisée par 2Ce qu'on vient d'apprendre : coordonnées x / y, variable,
Vector2, commentaire, fonction de dessin.
Étape 2 — Faire bouger le cercle au hasard#
On veut que le cercle tremble : à chaque instant, il avance d'un pixel au hasard (ou recule, ou ne bouge pas), en x et en y.
La boucle de jeu#
Un jeu vidéo, c'est une image fixe redessinée environ 60 fois par seconde. Entre deux images, Godot appelle la fonction _process : c'est là qu'on met ce qui change avec le temps.
extends Node2D
var rayon = 20
var couleur = Color.ORANGE
var couleur_fond = Color("#1d1b33")
var position_cercle = Vector2(400, 300)
func _ready():
RenderingServer.set_default_clear_color(couleur_fond) # couleur du fond
func _process(delta):
# À chaque image : on ajoute -1, 0 ou +1 à la position
position_cercle.x = position_cercle.x + randi_range(-1, 1)
position_cercle.y = position_cercle.y + randi_range(-1, 1)
queue_redraw() # la position a changé : on redessine
func _draw():
draw_circle(position_cercle, rayon, couleur)
Comment ça marche ?
randi_range(-1, 1)tire un nombre entier au hasard entre -1 et 1 : soit -1, soit 0, soit 1.position_cercle.x = position_cercle.x + ...se lit : « la nouvelle valeur deposition_cercle.x, c'est l'ancienne plus un petit nombre au hasard ». Le=en programmation ne veut pas dire « égal » mais « ranger dans ».queue_redraw(): Godot garde le dessin en mémoire et ne rappelle pas_drawtout seul. Quand quelque chose qui sert au dessin change (ici la position du cercle), on lui demande de redessiner. Sans cette ligne, le cercle resterait figé !
Le cercle fait une marche aléatoire (en anglais random walk). C'est lent et hésitant : c'est normal !
Raccourci :
position_cercle.x += randi_range(-1, 1)fait exactement la même chose queposition_cercle.x = position_cercle.x + randi_range(-1, 1).
Défis#
- essentiel Rendez le cercle plus nerveux : créez une variable
pas = 5et tirez au hasard entre-pasetpas. - essentiel Changez la couleur au hasard à chaque image (indice :
randf()donne un nombre à virgule entre 0 et 1). - essentiel Le cercle finit par sortir de l'écran. S'il sort à droite, faites-le revenir à gauche.
- bonus Faites respirer le cercle : le rayon change aussi au hasard (sans devenir négatif !).
Solution des défis 1, 2 et 4
extends Node2D
var rayon = 20
var couleur = Color.ORANGE
var couleur_fond = Color("#1d1b33")
var position_cercle = Vector2(400, 300)
var pas = 5 # défi 1 : la taille des pas
func _ready():
RenderingServer.set_default_clear_color(couleur_fond)
func _process(delta):
# Défi 1 : des pas plus grands
position_cercle.x = position_cercle.x + randi_range(-pas, pas)
position_cercle.y = position_cercle.y + randi_range(-pas, pas)
# Défi 2 : une couleur au hasard (rouge et vert fixes, bleu au hasard)
couleur = Color(1, 0.4, randf())
# Défi 4 : le rayon change au hasard, sans descendre sous 5
rayon = rayon + randi_range(-1, 1)
if rayon < 5:
rayon = 5
queue_redraw() # la position, la couleur et le rayon ont changé : on redessine
func _draw():
draw_circle(position_cercle, rayon, couleur)
Essayez aussi Color(randf(), randf(), randf()) : les trois composantes au hasard, ça clignote beaucoup plus !
Solution du défi 3 : la condition if
func _process(delta):
position_cercle.x += randi_range(-pas, pas)
position_cercle.y += randi_range(-pas, pas)
var taille = get_viewport_rect().size
if position_cercle.x > taille.x: # SI le cercle dépasse à droite…
position_cercle.x = 0 # … ALORS on le remet à gauche
if position_cercle.x < 0:
position_cercle.x = taille.x
if position_cercle.y > taille.y:
position_cercle.y = 0
if position_cercle.y < 0:
position_cercle.y = taille.y
queue_redraw()
if veut dire « si » : les lignes décalées en dessous ne s'exécutent que si la condition est vraie. Comparaisons possibles : >, <, >=, <=, == (égal), != (différent).
Ce qu'on vient d'apprendre : la boucle de jeu (
_process), le hasard, modifier une variable, la conditionif.
Étape 3 — Créer des baballes en cliquant#
Objectif : à chaque clic, une nouvelle baballe apparaît sous la souris et se met à trembler elle aussi. On y va en trois temps.
3a — Écouter la souris : les événements#
Quand on clique, appuie sur une touche ou bouge la souris, Godot crée un événement (event) et appelle la fonction _input. Ajoutez-la à votre script :
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
print("Clic en ", event.position)
Cliquez dans la fenêtre : la position du clic s'affiche dans la Sortie.
event is InputEventMouseButton: « est-ce que l'événement est un bouton de souris ? »event.pressed: « … qui vient d'être enfoncé ? » (et pas relâché)event.button_index == MOUSE_BUTTON_LEFT: « … et c'est le bouton gauche ? » (la molette compte aussi comme un bouton !)and: les trois conditions doivent être vraies en même temps.
3b — Se souvenir de plusieurs cercles : la liste#
Jusqu'ici, une seule variable position_cercle retenait où était le cercle. Avec plusieurs cercles, il faut que chaque cercle retienne sa propre position.
Une variable range une valeur. Pour des dizaines de cercles, on utilise une liste (array) : une rangée de cases numérotées à partir de 0.
positions = [ (400, 300), (120, 80), (650, 410) ]
case 0 case 1 case 2
extends Node2D
var positions = [Vector2(400, 300)] # une liste qui contient déjà 1 position
var rayon = 20
var couleur = Color.ORANGE
var couleur_fond = Color("#1d1b33")
func _ready():
RenderingServer.set_default_clear_color(couleur_fond) # couleur du fond
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
positions.append(event.position) # on ajoute la position du clic à la fin de la liste
print("Nombre de cercles : ", positions.size())
func _process(delta):
# Pour chaque numéro de case i, de 0 jusqu'à la fin de la liste…
for i in range(positions.size()):
positions[i] += Vector2(randi_range(-1, 1), randi_range(-1, 1))
queue_redraw()
func _draw():
# Pour chaque position p rangée dans la liste…
for p in positions:
draw_circle(p, rayon, couleur)
Nouveautés :
[ ... ]crée une liste,append(...)ajoute un élément à la fin,size()donne le nombre d'éléments.positions[i]: l'élément rangé dans la case numéroi.- La boucle
forrépète les lignes décalées une fois pour chaque élément. Écrire 100 foisdraw_circleà la main ? Non merci !
Pourquoi deux façons d'écrire le
for? Dans_draw, on ne fait que lire chaque position :for p in positionssuffit. Dans_process, on veut modifier la liste : il faut passer par le numéro de case,positions[i]. Si on écrivaitp += ..., on modifierait une copie de la position, et rien ne bougerait. Essayez pour voir !
3c — Des baballes toutes différentes : la classe#
Problème : toutes nos baballes ont la même couleur et la même taille. On voudrait que chacune ait les siennes. On pourrait faire trois listes (positions, rayons, couleurs)… mais on s'y perdrait vite.
Solution : on écrit une fiche « Baballe » qui regroupe tout ce qu'une baballe sait (sa position, sa taille, sa couleur) et tout ce qu'elle sait faire (bouger). C'est une classe.
Image : la classe, c'est le moule à gâteau. Chaque baballe créée avec
Baballe.new(...)est un gâteau sorti du moule : un objet. Même forme, mais chaque gâteau peut avoir sa propre déco.
extends Node2D
# ----- Le moule : la classe Baballe -----
class Baballe:
var position
var rayon
var couleur
# _init est appelée à la naissance de chaque baballe
func _init(position_de_depart):
position = position_de_depart
rayon = randi_range(10, 30)
couleur = Color(randf(), randf(), randf())
# Ce qu'une baballe sait faire
func bouger():
position.x += randi_range(-1, 1)
position.y += randi_range(-1, 1)
# ----- Le jeu -----
var baballes = [] # la liste de toutes les baballes
var couleur_fond = Color("#1d1b33")
func _ready():
RenderingServer.set_default_clear_color(couleur_fond) # couleur du fond
baballes.append(Baballe.new(Vector2(400, 300))) # une première baballe au centre
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
baballes.append(Baballe.new(event.position))
func _process(delta):
for b in baballes:
b.bouger()
queue_redraw()
func _draw():
for b in baballes:
draw_circle(b.position, b.rayon, b.couleur)
Nouveautés :
class Baballe:définit le moule. Lesvarà l'intérieur appartiennent à chaque baballe : chacune a sa propreposition, son proprerayon, sa proprecouleur.- Dans la classe,
positionest notre variable : une baballe n'est pas un nœud de Godot, elle n'a pas de position toute faite, alors on lui en donne une. Baballe.new(position)fabrique une nouvelle baballe : Godot appelle alors_initavec la position donnée.b.bouger(): on demande à la baballe b de bouger. Chacun sa responsabilité : le jeu n'a plus besoin de savoir comment une baballe bouge.- Remarquez : cette fois,
for b in baballessuffit pour modifier les baballes. Une variable qui contient un objet ne fait pas une copie : elle désigne l'objet lui-même.
Défis#
- essentiel Clic droit = tout effacer (indice :
MOUSE_BUTTON_RIGHTetbaballes.clear()). - essentiel Donnez à chaque baballe son propre pas : certaines calmes, d'autres très nerveuses.
- bonus Les baballes grossissent petit à petit, et disparaissent au-delà d'un rayon de 100.
- bonus Peinture : créez des baballes quand on glisse la souris bouton enfoncé (indice :
InputEventMouseMotion). - bonus Ajoutez à la classe une fonction
est_touchee(point)qui dit si un point est dans la baballe, et faites éclater la baballe sur laquelle on clique (indice :position.distance_to(point) < rayon).
Solution du défi 1 : clic droit = tout effacer
Dans _input, on ajoute un deuxième test pour le bouton droit :
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
baballes.append(Baballe.new(event.position))
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
baballes.clear() # vide la liste : plus aucune baballe
Comme _process appelle queue_redraw() à chaque image, l'écran se vide tout seul.
Solution du défi 2 : un pas pour chaque baballe
Le pas devient une variable de la classe : chaque baballe a le sien, tiré au hasard à sa naissance.
class Baballe:
var position
var rayon
var couleur
var pas
func _init(position_de_depart):
position = position_de_depart
rayon = randi_range(10, 30)
couleur = Color(randf(), randf(), randf())
pas = randi_range(1, 8) # 1 = calme, 8 = très nerveuse
func bouger():
position.x += randi_range(-pas, pas)
position.y += randi_range(-pas, pas)
Le reste du script ne change pas : c'est tout l'intérêt de la classe.
Solution du défi 3 : grossir et disparaître
Dans la classe, bouger() devient :
func bouger():
position.x += randi_range(-1, 1)
position.y += randi_range(-1, 1)
rayon += 0.1
Et dans _process, on garde seulement les baballes assez petites :
func _process(delta):
for b in baballes:
b.bouger()
baballes = baballes.filter(func(b): return b.rayon < 100) # on ne garde que les baballes de rayon < 100
queue_redraw()
On ne retire jamais d'éléments d'une liste pendant qu'on la parcourt avec for : c'est une source de bugs classique. filter fabrique une nouvelle liste qui ne garde que les baballes qui respectent la condition.
Solution du défi 4 : peindre avec des baballes
Quand la souris bouge, Godot envoie un événement InputEventMouseMotion. On crée une baballe seulement si le bouton gauche est enfoncé :
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
baballes.append(Baballe.new(event.position))
if event is InputEventMouseMotion and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
baballes.append(Baballe.new(event.position))
Input.is_mouse_button_pressed(...) répond à la question « ce bouton est-il enfoncé en ce moment ? ». Bougez vite ou lentement : les baballes s'espacent plus ou moins.
Solution du défi 5 : faire éclater une baballe
Dans la classe, une fonction qui répond vrai ou faux (return) :
func est_touchee(point):
return position.distance_to(point) < rayon # vrai si le point est dans le cercle
Dans _input, le clic gauche cherche d'abord une baballe sous la souris. S'il en trouve une, elle éclate ; sinon, on en crée une nouvelle :
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
var touchee = null # null = « rien pour l'instant »
for b in baballes:
if b.est_touchee(event.position):
touchee = b
if touchee != null:
baballes.erase(touchee) # on retire la baballe de la liste : elle disparaît
else:
baballes.append(Baballe.new(event.position))
distance_todonne la distance entre deux points : si elle est plus petite que le rayon, on a cliqué dans le cercle.- La boucle garde la dernière baballe touchée : c'est celle dessinée par-dessus les autres, celle qu'on voit.
- On retire la baballe après la boucle, pas pendant (même règle qu'au défi 3).
Ce qu'on vient d'apprendre : événement, liste, boucle
for, classe et objet.
Étape 4 (si on a le temps) — Des baballes en image : la classe Sprite2D#
Si on a le temps. Cette étape se fait si l'étape 3 est terminée assez tôt. Sinon, elle sera reprise en début de Jour 2.
Jusqu'ici, on dessinait tout nous-mêmes avec _draw. Godot sait déjà afficher des images : c'est le rôle du nœud Sprite2D. On va transformer notre Baballe en Sprite2D, pour qu'elle hérite de tout ce qu'un sprite sait faire.
Hériter d'une classe de Godot#
class Baballe extends Sprite2D:
se lit : « une Baballe est un Sprite2D, avec en plus ce qu'on ajoute ». C'est l'héritage.
Image : au lieu de fabriquer notre moule à gâteau de zéro, on part d'un moule du commerce (
Sprite2D) et on y ajoute notre déco.
Tout ce que Sprite2D sait faire, notre baballe le sait aussi, sans écrire une ligne :
| Propriété héritée | Ce qu'elle fait |
|---|---|
texture |
l'image à afficher |
position |
où est le sprite (chaque nœud de Godot a sa position) |
scale |
la taille (1 = taille de l'image, 0.5 = moitié) |
rotation |
l'angle, en radians (TAU = un tour complet) |
modulate |
une couleur qui teinte l'image |
Le code#
Chaque projet Godot contient déjà une image : l'icône icon.svg. On l'utilise comme baballe.
extends Node2D
# ----- Le moule : une Baballe qui EST un Sprite2D -----
class Baballe extends Sprite2D:
var pas = 1
func _init(position_de_depart):
texture = load("res://icon.svg") # l'image à afficher
position = position_de_depart
scale = Vector2.ONE * randf_range(0.2, 0.6) # taille au hasard
modulate = Color(randf(), randf(), randf()) # teinte au hasard
rotation = randf_range(0, TAU) # angle au hasard
# Chaque baballe a SA PROPRE boucle de jeu !
func _process(delta):
position.x += randi_range(-pas, pas)
position.y += randi_range(-pas, pas)
rotation += 1 * delta # tourne doucement
# ----- Le jeu -----
var couleur_fond = Color("#1d1b33")
func _ready():
RenderingServer.set_default_clear_color(couleur_fond)
add_child(Baballe.new(Vector2(400, 300))) # une première baballe au centre
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
add_child(Baballe.new(event.position))
Ce qui a disparu par rapport à l'étape 3 :
- Plus de
_drawni dequeue_redraw(): unSprite2Ds'affiche tout seul. - Plus de liste
baballes:add_child(...)range la baballe dans la scène, comme un enfant du nœud principal. C'est Godot qui tient la liste (on peut la retrouver avecget_children()). - Plus de
for b in baballes: b.bouger(): chaque baballe a sa propre fonction_process, que Godot appelle tout seul à chaque image. Chaque objet vit sa vie.
Lancez le jeu, puis regardez l'onglet Scène de l'éditeur et cliquez sur Distant (Remote) : chaque baballe apparaît comme un nœud
Sprite2D, enfant du nœud principal. En cliquant dessus, on voit ses propriétés changer en direct dans l'inspecteur.
Votre propre image#
Glissez une image (.png, .jpg, .svg…) dans le panneau Système de fichiers de Godot, puis remplacez "res://icon.svg" par son chemin, par exemple "res://images/balle.png". Clic droit sur le fichier → Copier le chemin évite les fautes de frappe.
Défis#
- Utilisez votre propre image (dessin, photo, pixel art…).
- Donnez à chaque baballe sa propre vitesse de rotation au hasard, dans un sens ou dans l'autre.
- Clic droit = supprimer toutes les baballes (indice :
for b in get_children():etb.queue_free()). - Faites grandir chaque baballe petit à petit (
scale *= 1.001), et faites-la disparaître avecqueue_free()quand elle devient trop grande. - Affichez le nombre de baballes dans la console à chaque clic (indice :
get_child_count()).
Solution du défi 1 : votre propre image
- Glissez votre image dans le panneau Système de fichiers, par exemple dans un dossier
images. - Clic droit sur le fichier → Copier le chemin.
- Dans
_init, collez ce chemin à la place de"res://icon.svg":
texture = load("res://images/balle.png") # le chemin de VOTRE image
Si l'image est très grande, changez aussi les nombres de randf_range dans la ligne scale (par exemple 0.05, 0.2).
Solution du défi 2 : une vitesse de rotation pour chaque baballe
On ajoute une variable à la classe, tirée au hasard à la naissance. Un nombre négatif fait tourner dans l'autre sens :
class Baballe extends Sprite2D:
var pas = 1
var vitesse_rotation
func _init(position_de_depart):
texture = load("res://icon.svg")
position = position_de_depart
scale = Vector2.ONE * randf_range(0.2, 0.6)
modulate = Color(randf(), randf(), randf())
rotation = randf_range(0, TAU)
vitesse_rotation = randf_range(-3, 3) # négatif = sens inverse
func _process(delta):
position.x += randi_range(-pas, pas)
position.y += randi_range(-pas, pas)
rotation += vitesse_rotation * delta # chacune tourne à sa vitesse
delta est le temps écoulé depuis l'image précédente (en secondes) : vitesse_rotation * delta donne une vitesse en radians par seconde, la même sur un ordinateur rapide ou lent.
Solution du défi 3 : clic droit = supprimer toutes les baballes
Dans _input du jeu :
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
add_child(Baballe.new(event.position))
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
for b in get_children(): # tous les enfants du nœud principal : nos baballes
b.queue_free() # chacune est supprimée à la fin de l'image
Plus de liste à vider comme à l'étape 3 : les baballes sont les enfants du nœud principal, et get_children() les donne toutes.
Solution du défi 4 : grandir et disparaître
Dans le _process de la classe Baballe :
func _process(delta):
position.x += randi_range(-pas, pas)
position.y += randi_range(-pas, pas)
rotation += 1 * delta
scale *= 1.001 # grandit de 0,1 % à chaque image
if scale.x > 2:
queue_free() # « Godot, supprime-moi à la fin de l'image »
queue_free() retire le nœud de la scène et le détruit. La baballe se supprime elle-même : pas besoin de liste ni de filter comme à l'étape 3.
Solution du défi 5 : compter les baballes
Dans _input, juste après avoir ajouté la baballe :
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
add_child(Baballe.new(event.position))
print("Nombre de baballes : ", get_child_count()) # nombre d'enfants du nœud principal
print accepte plusieurs valeurs séparées par des virgules : il les colle bout à bout. Le nombre s'affiche dans l'onglet Sortie.
Ce qu'on vient d'apprendre : héritage (
extends), les nœuds de Godot,Sprite2D,add_childetqueue_free.
Récapitulatif : le vocabulaire du jour#
| Mot | En une phrase | Exemple |
|---|---|---|
| Instruction | un ordre donné à l'ordinateur | print("Bonjour !") |
| Variable | une boîte avec un nom, qui range une valeur | var rayon = 20 |
| Fonction | un bloc d'instructions avec un nom | func _draw(): |
| Boucle de jeu | ce qui se répète à chaque image | func _process(delta): |
| Hasard | un nombre tiré au sort | randi_range(-1, 1) |
| Condition | faire quelque chose seulement si… | if position_cercle.x > 800: |
| Événement | quelque chose que le.la joueur.euse vient de faire | func _input(event): |
| Liste | une rangée de cases numérotées à partir de 0 | var baballes = [] |
Boucle for |
répéter pour chaque élément | for b in baballes: |
| Classe / objet | un moule / ce qui sort du moule | Baballe.new(...) |
| Héritage | partir d'une classe existante et y ajouter des choses | class Baballe extends Sprite2D: |
| Nœud | un élément de la scène Godot, qui peut avoir des enfants | add_child(...), queue_free() |