TP 3 — Animer et rendre un programme interactif⚓︎
Objectifs
À la fin de ce TP, vous saurez :
- distinguer un affichage statique d'un affichage animé ;
- expliquer les rôles des fonctions
updateetdraw; - lancer la boucle principale d'un programme avec
pyxel.run; - modifier la position d'un élément au cours du temps ;
- utiliser
pyxel.btnetpyxel.btnppour lire le clavier ; - limiter le déplacement d'un personnage à la fenêtre ;
- afficher et orienter un sprite mobile ;
- utiliser
pyxel.frame_countpour effectuer une action périodique.
Prérequis
Fonctions, variables globales, conditions, boucles, listes et dictionnaires, ainsi que les notions des TP 1 et 2.
👏 Crédits
Ce TP est adapté d'une ressource conçue et partagée par Florian Picard. Le fichier 4.pyxres utilisé pour le personnage provient des univers Python 2024 de la Nuit du Code. Le TP s'appuie également sur le tutoriel de la Nuit du Code et sur la documentation officielle de Pyxel.
1. La boucle principale d'un programme Pyxel⚓︎
Affichage dynamique
Dans les TP précédents, l'ensemble de l'image était dessiné une seule fois, puis affiché avec pyxel.show() : l'affichage était statique.
Pour un affichage dynamique (programme animé ou interactif), Pyxel affiche une succession d'images, appelées aussi frames. Par défaut, il produit 30 images par seconde. Avant chaque nouvel affichage, Pyxel appelle successivement deux fonctions :
updatemet à jour l'état du programme : positions, clavier, score, collisions, etc. ;drawdessine l'image correspondant à cet état.
La fonction pyxel.run(update, draw) lance cette boucle principale. Elle remplace pyxel.show().
flowchart LR
A["update : mettre à jour"] --> B["draw : dessiner"]
B --> C["Afficher l'image"]
C --> A
Exercice 1 — Une première animation
Recopier puis exécuter le programme suivant. On pourra le nommer tp3.py et on le modifiera au fil du TP.
| 🐍 Script Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Question 1
Quel est le rôle de chacune des clefs "x", "y" et "vitesse" du dictionnaire CARRE ?
Question 2
Quelle instruction provoque le déplacement du carré ? Dans quelle fonction se trouve-t-elle ?
Question 3
Supprimer temporairement l'instruction pyxel.cls(0), puis relancer le programme. Expliquer les traces observées.
Question 4
Compléter la fonction update pour que le carré réapparaisse à gauche lorsqu'il est entièrement sorti de la fenêtre.
Aide
Le carré est entièrement sorti lorsque son abscisse est supérieure ou égale à LARGEUR.
Résultat attendu

2. Commander un déplacement avec le clavier⚓︎
Gestion du clavier
Pyxel associe un nom à chaque touche du clavier. Par exemple, les quatre touches fléchées sont représentées par :
pyxel.KEY_LEFT
pyxel.KEY_RIGHT
pyxel.KEY_UP
pyxel.KEY_DOWN
Les fonctions suivantes permettent de connaître l'état d'une touche.
| Syntaxe | Valeur renvoyée |
|---|---|
pyxel.btn(touche) |
True pendant toute la durée où la touche est maintenue enfoncée. |
pyxel.btnp(touche) |
True au moment où la touche vient d'être pressée. |
pyxel.btnr(touche) |
True au moment où la touche vient d'être relâchée. |
Pour obtenir un déplacement continu, on utilise donc généralement pyxel.btn.
Exercice 2 — Déplacer le carré
Remplacer la fonction update de l'exercice précédent par le squelette suivant.
| 🐍 Script Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 | |
Question 1
Compléter la fonction update pour déplacer le carré de deux pixels dans chacune des quatre directions.
Question 2
Maintenir deux touches fléchées enfoncées simultanément. Pourquoi le carré peut-il se déplacer en diagonale ?
Résultat attendu

Question 3
Remplacer temporairement les quatre if par une structure if — elif — elif — elif. Quel changement observez-vous ?
Question 4
Remplacer temporairement pyxel.btn par pyxel.btnp. Quelle différence observez-vous lorsqu'une touche est maintenue enfoncée ?
Question 5
Compléter la fonction update pour que le carré réapparaisse par le bord opposé s'il sort par un bord.
Question 6
Modifier le programme pour commander le carré avec les touches Z, Q, S et D. Par exemple, la touche Z est désignée par pyxel.KEY_Z.
3. Utiliser un dictionnaire global⚓︎
Variable globale et dictionnaire
Les fonctions imposées à pyxel.run ne reçoivent aucun argument. Les variables qui décrivent l'état du programme sont donc souvent définies en dehors de ces fonctions.
La fonction update peut lire le dictionnaire global CARRE et modifier son contenu, par exemple avec CARRE["x"] = 10. La variable CARRE continue alors de désigner le même dictionnaire : le mot-clé global n'est pas nécessaire.
En revanche, une instruction comme CARRE = {"x": 10, "y": 20} affecterait un nouveau dictionnaire à la variable. Pour effectuer cette affectation dans une fonction, il faudrait déclarer global CARRE.
Dans la suite, l'état du personnage sera regroupé dans un dictionnaire :
PERSONNAGE = {"x": 60, "y": 60, "sens": 1}
La clef "sens" vaudra 1 lorsque le personnage regarde vers la droite et -1 lorsqu'il regarde vers la gauche.
4. Maintenir le personnage dans la fenêtre⚓︎
Vérification des coordonnées
Un sprite carré de côté TAILLE est entièrement dans une fenêtre de dimensions LARGEUR × HAUTEUR si :
et
Exercice 3 — Traiter les bords
On remplace le dictionnaire CARRE par :
PERSONNAGE = {"x": 60, "y": 60, "sens": 1}
Remplacer la fonction update par les deux fonctions suivantes.
Il faut compléter la fonction update_personnage afin que le personnage ne puisse pas sortir de la fenêtre.
Il faut aussi remplacer CARRE par PERSONNAGE dans la fonction draw.
| 🐍 Script Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Question 1
Compléter les quatre conditions.
Question 2
Pourquoi la condition concernant le bord droit fait-elle intervenir LARGEUR - TAILLE et non LARGEUR ?
Question 3
Tester le programme dans les quatre coins de la fenêtre.
Résultat attendu

5. Afficher et orienter un sprite mobile⚓︎
On utilise maintenant le fichier 4.pyxres de la Nuit du Code 2024, déjà employé dans le TP 2. On va utiliser dans ce TP le sprite qui commence au point (0, 16) de la banque d'images 0 et mesure 8 × 8 pixels. La couleur 5 constitue son fond transparent.
La largeur fournie à pyxel.blt peut être positive ou négative :
largeur_affichee = TAILLE * PERSONNAGE["sens"]
Elle vaut 8 lorsque le personnage regarde vers la droite et -8 lorsqu'il regarde vers la gauche.
Exercice 4 — Déplacer un sprite
Question 1
Télécharger le fichier 4.pyxres, le placer dans le dossier du programme et ajouter l'instruction suivante après pyxel.init :
pyxel.load("4.pyxres")
Question 2
Compléter la fonction draw pour effacer l'écran, puis afficher le sprite à la position du personnage. Sa largeur devra dépendre de PERSONNAGE["sens"].
| 🐍 Script Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 | |
Question 3
Vérifier que le sprite change d'orientation lorsque le personnage se déplace vers la gauche ou vers la droite.
Résultat attendu

6. Déclencher une action périodique⚓︎
Gestion du temps
La variable pyxel.frame_count contient le nombre d'images affichées depuis le lancement du programme. Avec la fréquence par défaut de 30 images par seconde :
pyxel.frame_count % 30 == 0est vraie une fois par seconde ;pyxel.frame_count % 15 == 0est vraie deux fois par seconde.
Exercice 5 — Faire clignoter un objet
Question 1
Combien d'images correspondent à une demi-seconde lorsque le programme fonctionne à 30 images par seconde ?
Question 2
On veut afficher un disque pendant une demi-seconde, puis le masquer pendant une demi-seconde.
Compléter la condition suivante, puis ajouter ces lignes dans la fonction draw :
periode = pyxel.frame_count // 15
if ... : # si periode est pair on affiche le disque
pyxel.circ(10, 10, 3, 10)
Résultat attendu

Question 3
Modifier le programme pour que le disque change d'état une fois par seconde.
7. Production finale — Un personnage sous l'averse⚓︎
Exercice 6 : à vous de jouer
Programmer un personnage qui se déplace horizontalement sous une averse avec un éclair qui change en jaune la couleur du ciel toutes les secondes pendant deux dixièmes de seconde. Le ciel est noir par défaut.
Le programme devra respecter le cahier des charges suivant :
- le personnage se déplace vers la gauche ou vers la droite avec les touches fléchées ;
- il ne peut pas sortir de la fenêtre ;
- son sprite s'oriente dans le sens du déplacement ;
- les gouttes descendent automatiquement ;
- lorsqu'une goutte atteint le sol, elle réapparaît en haut de la fenêtre ;
- l'éclair change la couleur du ciel en jaune pendant deux dixièmes de seconde toutes les secondes ;
- les mises à jour sont effectuées dans
updateet l'affichage dansdraw.
Compléter le squelette suivant. La liste des gouttes est fournie.
| 🐍 Script Python | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | |
Question 1
Compléter update_personnage en réutilisant le travail de l'exercice 3. Seuls les déplacements horizontaux sont nécessaires.
Question 2
Compléter update_gouttes. Une goutte replacée en haut pourra recevoir une ordonnée aléatoire comprise entre -HAUTEUR et -1.
Question 3
Sachant que la vitesse de rafraichissement est de 30 frames par secondes, donner les numéros des frames qui correspondent aux premier éclair, puis au second et au troisième.
Compléter update_eclair pour changer la couleur du ciel en jaune pendant deux dixièmes de seconde toutes les .
Question 4
Compléter draw pour afficher les éclairs, le sprite du personnage et toutes les gouttes. Chaque goutte pourra être représentée par un disque de rayon 1.
Question 5
Tester séparément les déplacements du personnage, l'animation des gouttes et leur réapparition, vérifier le déclenchement périodique d'un éclair.
Résultat attendu

Prolongements facultatifs
Choisir une ou plusieurs améliorations :
- mettre le jeu en pause avec la barre d'espace et
pyxel.btnp; - donner aux gouttes des couleurs ou des vitesses différentes ;
- alterner deux sprites pour animer la marche du personnage ;
- détecter les collisions entre le personnage et les gouttes ;
- ajouter un score ou un nombre de vies ;
- afficher un écran de fin.
8. Mémento⚓︎
Mémento du TP3
| Syntaxe | Explication / sens |
|---|---|
pyxel.run(update, draw) |
Lance la boucle principale et appelle successivement les deux fonctions. |
def update(): |
Définit la fonction qui met à jour l'état du programme. |
def draw(): |
Définit la fonction qui dessine une image du programme. |
pyxel.btn(touche) |
Renvoie True tant que la touche est maintenue enfoncée. |
pyxel.btnp(touche) |
Renvoie True au moment où la touche est pressée. |
pyxel.btnr(touche) |
Renvoie True au moment où la touche est relâchée. |
pyxel.KEY_LEFT, pyxel.KEY_RIGHT |
Désignent les touches fléchées gauche et droite. |
pyxel.KEY_UP, pyxel.KEY_DOWN |
Désignent les touches fléchées haut et bas. |
pyxel.frame_count |
Contient le nombre d'images affichées depuis le lancement. |
pyxel.blt(x, y, img, u, v, -largeur, hauteur, couleur) |
Affiche un sprite retourné horizontalement. |