Vous en etes deja a un stade super pratique : vous savez connecter des signals, vous avez une scene Main avec Player, HUD et un menu... bref, la base est la. Et maintenant, vous vous demandez peut-etre comment faire des evenements reguliers (score qui monte, clignotement, spawn, cooldown) sans transformer _process(delta) en usine a gaz (et sans cramer du CPU pour rien). Accrochez-vous, on va voir ca ensemble : les Timers, c'est simple, propre, et franchement top... mais seulement si on evite 2 ou 3 pieges classiques.
Contexte rapide (ou on en est)
Vous avez un projet Godot 4.x avec :
- une scene Main (racine),
- un Player,
- un HUD (avec au moins un Label),
- un menu simple (pause ou ecran de debut, peu importe).
Vous connaissez deja les signals (c'est essentiel), et vous voulez declencher des actions a intervalle regulier sans faire des calculs chaque frame.
Objectifs (clairs, mesurables)
A la fin de ce module, vous saurez :
- Utiliser un Timer node et son signal
timeout. - Comprendre la difference entre periodicite (repeter) et duree (delai).
- Creer une petite "boucle" de score (ou un clignotement) sans coder dans
_process. - Ajouter et configurer un Timer dans une scene (
wait_time,autostart,one_shot, pause). - Diagnostiquer les pieges classiques (timer non demarre, autostart, pause mode).
Mini glossaire (rapide, utile)
- Timer : noeud qui compte a rebours et emet un signal quand il arrive a zero.
- timeout : signal emis par le Timer quand le temps est ecoule.
- wait_time : duree (en secondes) avant un
timeout. - one_shot : si
true, le timer ne se relance pas tout seul (une seule fois). - repeat : quand
one_shot = false, le timer repart automatiquement apres chaquetimeout. - autostart : si
true, le timer demarre automatiquement quand la scene est prete. - time_left : temps restant avant le prochain
timeout. - pause mode : comportement du noeud quand le jeu est en pause (il peut continuer ou s'arreter selon votre config).
Pourquoi les Timers (et pourquoi ce n'est pas _process) ?
Un evenement regulier, c'est tentant de le faire comme ca : "j'accumule du delta, si ca depasse 1 seconde je fais mon truc". Ca marche... mais c'est du code en plus, du risque de bug en plus, et ca tourne chaque frame.
Un Timer, c'est l'inverse : vous lui dites "toutes les 1.0 secondes, fais-moi un timeout", et lui se debrouille (et votre _process reste clean). C'est leger, c'est lisible, et c'est facile a ajuster dans l'editeur (ce qui est mega confortable quand vous testez).
Notion cle : duree vs periodicite (one-shot vs repeat)
Avant de coder, retenez juste cette image mentale :
- Duree (delai) : "attends 3 secondes puis fais X". C'est souvent un one-shot.
- Periodicite (boucle) : "toutes les 1 seconde, fais X". C'est un Timer en repeat.
Dans Godot, ca se regle avec one_shot :
one_shot = true: une seule fois, puis stop.one_shot = false: le Timer continue en boucle (periodique).
Vous allez utiliser les deux dans un jeu, mais pour un score qui monte, on veut du repeat.
Pas a pas guide : un score temporel dans Main
On va creer un compteur time_survived qui s'incremente toutes les secondes, et on l'affiche dans un Label du HUD. L'objectif est que ce soit visible tout de suite quand vous lancez la scene.
1) Ajouter le Timer dans la scene Main
- Ouvrez
Main.tscn. - Dans l'arborescence, selectionnez Main.
- Clic droit -> Ajouter un enfant -> cherchez Timer.
- Renommez-le ScoreTimer.
Petit detail important : un Timer n'a pas de visuel, donc sa "position" dans la scene, on s'en fiche. Par contre, il doit etre dans l'arbre (dans la scene en cours) pour tourner.
Checkpoint
- Vous voyez
ScoreTimersousMain. - Il apparait comme un noeud Timer (pas un Node generic).
2) Configurer le Timer (inspecteur)
Selectionnez ScoreTimer, puis dans l'inspecteur :
- Wait Time :
1.0 - One Shot : decoche (donc repeat)
- Autostart : coche (pour demarrer tout seul)
- Process Callback : laissez par defaut pour l'instant
C'est simple, mais c'est la que se cache un piege : si Autostart n'est pas coche, il ne se passera... rien. (Oui, c'est bete, mais c'est ultra frequent.)
Checkpoint
wait_time = 1.0one_shot = falseautostart = true
3) Connecter timeout() a Main
- Cliquez sur
ScoreTimer. - Onglet Node (ou "Noeud") a droite.
- Double-clic sur timeout().
- Choisissez la cible Main.
- Validez.
Godot cree une fonction du style :
func _on_score_timer_timeout():
Checkpoint
- Dans le script de Main, vous avez bien une fonction
_on_score_timer_timeout()(meme vide).
4) Ecrire le code du compteur + mise a jour HUD
Dans Main.gd, on pose une variable et on prepare les chemins.
extends Node2D
var time_survived: int = 0
@onready var score_timer: Timer = $ScoreTimer
@onready var hud_label: Label = $HUD/Label # adaptez le chemin
Ici, c'est clair : score_timer, c'est votre Timer, hud_label, c'est l'affichage (et oui, vous devrez peut-etre ajuster $HUD/Label selon votre scene).
Ensuite, dans _ready(), on peut securiser le demarrage. Meme si autostart est coche, c'est pratique d'avoir un plan B si vous changez les reglages plus tard.
func _ready() -> void:
if not score_timer.autostart:
score_timer.start()
Et enfin, dans le callback du signal :
func _on_score_timer_timeout() -> void:
time_survived += 1
hud_label.text = "Temps survecu: " + str(time_survived) + "s"
Checkpoint
- Le jeu se lance sans erreur.
- Le label change toutes les 1 secondes : 1s, 2s, 3s...
5) Tester (et verifier que c'est bien le Timer qui bosse)
Lancez Main (F6) ou le jeu (F5). Si tout va bien :
- le compteur monte tout seul,
_processn'est pas utilise,- votre HUD reste fluide.
Si rien ne bouge, ne paniquez pas : c'est quasiment toujours un piege de la section "Erreurs frequentes" (autostart pas coche, mauvais chemin vers le Label, signal pas connecte, pause mode...).
Variante rapide : clignotement (BlinkTimer)
Vous voulez un effet "attention" sur un label, genre clignotement toutes les 0.5 secondes ? C'est exactement le meme schema, et c'est parfait pour comprendre la periodicite.
- Ajoutez un second Timer sous Main : BlinkTimer
- Configurez :
wait_time = 0.5one_shot = falseautostart = true
- Connectez
timeout()vers Main - Code :
func _on_blink_timer_timeout() -> void:
hud_label.visible = not hud_label.visible
C'est tout. Et surtout : ce clignotement ne depend pas du framerate, et vous n'avez pas ecrit une seule ligne dans _process.
Aller plus loin : piloter un Timer (start, stop, time_left)
Un Timer, ce n'est pas juste "tick toutes les secondes". Vous pouvez le controler tres simplement.
Demarrer, arreter, relancer
score_timer.start(): demarre (ou redemarre) le timer avec sonwait_time.score_timer.start(2.5): demarre avec un delai de 2.5s (sans changerwait_timedans l'inspecteur).score_timer.stop(): arrete le timer (plus detimeout).
Exemple pratique : vous stoppez le score quand le joueur meurt, puis vous le relancez au restart.
Lire le temps restant (time_left)
time_left est utile pour un cooldown ou pour afficher "prochaine vague dans X secondes".
func _process(_delta: float) -> void:
# Juste pour debug (pas besoin en prod)
# print(score_timer.time_left)
pass
Ici on l'affiche en debug, mais notez bien : le Timer tourne sans _process. _process ne sert que si vous voulez lire/afficher time_left en continu.
One-shot : un delai unique propre
Cas classique : vous voulez attendre 3 secondes avant d'afficher un message, lancer un spawn, ou autoriser une action.
Avec un Timer node :
func launch_one_shot_example() -> void:
score_timer.one_shot = true
score_timer.start(3.0)
Attention : si vous transformez votre ScoreTimer en one-shot, il ne fera plus votre score periodique. En general, on cree un Timer dedie (genre IntroDelayTimer) pour ne pas melanger les roles.
Erreurs frequentes (si tu vois X, alors c'est surement Y)
1) "Rien ne se passe"
- Cause probable : le timer n'a pas demarre
- Fix : cochez
autostart, ou appelezstart()dans_ready()
Astuce simple : selectionnez le Timer en jeu (Remote dans l'arbre) et regardez si time_left diminue.
2) "J'ai une erreur sur le chemin du Label"
- Cause :
$HUD/Labelne correspond pas a votre scene - Fix : clic droit sur le Label -> "Copier le chemin du noeud" et collez-le dans le script
3) "Mon timer ne tourne pas en pause" (ou l'inverse)
Si vous avez un menu pause, vous allez tomber dessus.
- Symptome A : vous mettez le jeu en pause et le score s'arrete, mais vous vouliez qu'il continue (par exemple un menu qui clignote).
- Symptome B : vous mettez pause et le timer continue, mais vous vouliez tout figer.
Fix : regardez le pause mode du Timer (et du HUD) : selon votre choix, un noeud peut stopper ou continuer en pause. C'est un reglage de noeud, pas juste une question de code.
Et si vous voulez forcer un arret manuellement :
score_timer.paused = truequand vous ouvrez le menuscore_timer.paused = falsequand vous reprenez
4) "start() ne marche pas"
- Cause : vous tentez de demarrer un Timer qui n'est pas encore dans l'arbre de scene
- Fix : demarrez-le apres
_ready(), ou assurez-vous que le noeud existe deja (bon chemin, bonne scene instanciee)
5) "Je veux du 0.01s, mais c'est instable"
- Cause : un Timer est traite par frame, donc tres petit
wait_time= irregularites (limite framerate) - Fix : pour des micro-intervalles, utilisez une logique
_process/_physics_processadaptee, ou augmentez l'intervalle
Exercices (1 facile + 1 challenge)
Exercice facile : "Temps: X" toutes les secondes
Objectif : refaire le compteur sans copier-coller aveugle (c'est la que ca rentre).
- Ajoutez un Timer SurvivalTimer sous Main.
- Reglages :
wait_time=1.0,autostart=true,one_shot=false. - Connectez
timeout()a Main. - Dans Main : variable
temps: int = 0. - Dans
_on_survival_timer_timeout():temps += 1- mise a jour d'un label
$HUD/TempsLabel
Code minimal :
var temps: int = 0
@onready var temps_label: Label = $HUD/TempsLabel
func _on_survival_timer_timeout() -> void:
temps += 1
temps_label.text = "Temps: " + str(temps)
Checkpoint
- Apres 30 secondes, vous voyez "Temps: 30"
- Aucun code de compteur dans
_process
Challenge : acceleration progressive (difficulte qui monte)
Objectif : rendre le timer de plus en plus rapide, sans descendre sous une limite. Ici, on joue sur la periodicite en diminuant wait_time.
Dans le script de Main :
@export var acceleration: float = 0.05
@export var min_wait: float = 0.1
func _on_score_timer_timeout() -> void:
time_survived += 1
hud_label.text = "Temps survecu: " + str(time_survived) + "s"
score_timer.wait_time = max(min_wait, score_timer.wait_time - acceleration)
Petit detail : on ne relance pas start() a chaque fois, donc la nouvelle periode s'applique au cycle suivant. C'est souvent suffisant, et ca evite des effets de reset bizarres.
Checkpoint
- Au debut : 1 tick par seconde
- Apres un moment : ca s'accelere (sans devenir completement chaotique)
- Le timer ne descend jamais sous
min_wait
Recap + checklist (ce qui doit etre vrai a la fin)
Vous devez pouvoir cocher ca (sinon, revenez sur l'etape correspondante) :
- J'ai un Timer dans Main avec
wait_timeconfigure. - Je sais la difference :
one_shot=true(une fois) vsfalse(repete). - Je comprends "duree" (delai) vs "periodicite" (boucle).
- Le signal
timeout()est connecte, et la fonction s'appelle bien. - Mon compteur
time_survivedaugmente sans utiliser_process. - Mon HUD s'actualise sans erreur de chemin.
- Je sais demarrer/arreter (start/stop) un timer.
- Je sais quoi verifier si ca bloque a cause de la pause (pause mode,
paused).
Mini quiz (pour verifier que c'est acquis)
- Si
one_shot = true, le Timer emettimeout...
- A) toutes les secondes
- B) une seule fois, puis il s'arrete
- C) seulement si
_processest present
- Votre compteur ne monte pas. Quelle verification en premier ?
- A) verifier
autostartou unstart()dans_ready() - B) ajouter un
_processavec undelta += ... - C) changer le nom de la scene
- Vous mettez le jeu en pause et votre timer s'arrete, mais vous vouliez qu'il continue. Vous regardez...
- A) le framerate
- B) le pause mode du noeud (et/ou
paused) - C) le texte du Label
Reponses attendues : 1) B, 2) A, 3) B.
Pont vers le module suivant
Maintenant que vous avez des evenements reguliers propres (score, clignotement, spawn, cooldown), vous avez une base parfaite pour synchroniser des effets visuels. Et justement, la suite logique, c'est l'animation fluide : Tweens et Animations (le genre de truc qui rend un jeu "vivant" en 10 minutes). On y va ?