Passons désormais au troisème chapitre de ce tuto / cours de SNT, consacré à la découverte de Pygame en Python ! 😃
Pour cette nouvelle étape, nous allons animer notre sprite (Il aura ainsi une bien meilleure tête...) ! 🤩
Pour ce faire, au lieu d'afficher toute notre feuille de sprite / spritesheet : "rabidja.png" (qui reste toujours dans le dossier "graphics"), nous allons la découper en rectangles, appelés frames, et on va ensuite faire défiler ces frames avec un compteur (timer) comme pour un vrai dessin animé ! 😎

rabidja.png
Ainsi, si on regarde notre spritesheet, on remarque que les dessins n'ont pas été agencés n'importe comment. On trouve ainsi 5 lignes, chacune correspondant à une animation différente ☺️ :
- La première correspond à l'état Idle, c'est-à-dire quand Rabidja ne fait rien. On remarque qu'il y a 8 frames qui se mettent en boucle.
- La deuxième est similaire mais correspond à la marche / walk.
- La troisième correspond au saut / jump et se limite à 2 frames.
- La quatrième que nous n'utiliserons pas dans ce tuto (mais que vous retrouverez dans les Big Tuto SDL 2 / SFML 2) correspond au double saut / double jump.
- La cinquième ne servira pas non plus ici et correspond à la mort / death. (Oh non ! 😱).
La technique va donc être de découper notre feuille en rectangles de 50 x 40 pixels (la taille de notre sprite) avec une formule mathématique très basique :
x = N° de la frame x PLAYER_WIDTH (= largeur du sprite soit 40 pixels)
y = Etat du joueur (Idle/walk/jump) x PLAYER_HEIGHTH (= hauteur du sprite soit 50 pixels).
Ainsi, si on est Idle, y va valoir 0 (0 x 50), si on marche (walk), y va valoir 50 (1 x 50), et si on saute (jump), y va valoir 100 (2 x 50) puisqu'on aura donné les valeurs 0, 1 et 2 aux constantes Idle, walk et jump.
Pour la frame, c'est pareil avec x, où on va multiplier la valeur de la frame par la largeur du sprite.
Ainsi, frame 0, x = 0, frame 1, x = 40, frame 2, x = 2 x 40 = 80 et ainsi de suite. Il faudra juste faire attention au saut qui se limite à deux frames (mais on verra ça plus tard ! 😋).
Maintenant qu'on a la théorie, passons à la pratique ! 😅
On va reprendre le projet précédent. Rappelez-vous qu'à chaque fois que vous voulez compiler et lancer le programme avec la flèche verte, il faut le faire dans le main, sinon il ne se passera rien !!! 😨

Passons maintenant à la mise à jour du code source du fichier Constantes.py ! 🤪
Comme d'habitude recopiez / modifiez les passages qui ne sont pas grisés. 😆
Fichier : Constantes.py
# Créé par Jay81, le 16/06/2024 en Python 3.7 """Constantes du jeu""" # Paramètres de la fenêtre LARGEUR = 640 HAUTEUR = 360 # Personnalisation de la fenêtre TITRE = "Rabidja" ICONE = "" # Listes des images du jeu (graphics) BACKGROUND = "graphics/background.png" SPRITE_JOUEUR = "graphics/rabidja.png" # Taille du sprite du jouer PLAYER_HEIGHTH = 50 PLAYER_WIDTH = 40 # Machine à états DROITE = 1 GAUCHE = 2 IDLE = 0 WALK = 1 JUMP = 2 DOUBLEJUMP = 3 DEAD = 4 # Timer pour l'animation TIMER = 4 |
Bon, il n'y a pas grand chose de bien compliqué dans ces constantes. On commence par rentrer la taille de notre sprite (comme ça, si on en change pour mettre Nounouille la Super Grenouille 👽, on n'aura pas à changer ces valeurs dans TOUT le code 😜), et on met ensuite à jour notre machine à états, comme on l'a théorisée ci-dessus (on rentre y compris la mort et le double jump, au cas où on voudrait poursuivre ce tuto plus tard 😉).
Enfin, on rentre un compte à rebours (timer) pour notre animation. C'est-à-dire qu'on va attendre 4 fps / 60 (images par seconde) avant de changer de frame. Vous pourrez vous amuser à changer la valeur de ce timer plus tard pour accélérer ou ralentir la vitesse de l'animation. 😎
Passons maintenant au Main (programme principal). Idem, recopiez / modifiez les passages qui ne sont pas grisés. 😀
Fichier : Main.py
# Créé par Jay81, le 16/06/2024 en Python 3.7 # Import de pygame import pygame from pygame.locals import * #Import des constantes et classes du jeu from Constantes import * from Player import * #Initialisation de pygame pygame.init() clock = pygame.time.Clock() # Ouverture de la fenêtre de jeu en résolution 640 x 360 pixels fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR)) #Titre pygame.display.set_caption(TITRE) #Chargement du fond d'écran (background) fond = pygame.image.load(BACKGROUND).convert() #Création du joueur player = Player() # Boucle principale du jeu continuer = True while continuer : # On teste les événements for event in pygame.event.get(): # Si on appuie sur quitter, on ferme le jeu if event.type == QUIT: continuer = False # On affiche le fond d'écran (background) fenetre.blit(fond,(0,0)) #On met à jour le joueur player.update() # On affiche le sprite player.draw(fenetre) #On rafraichit l'écran pygame.display.update() clock.tick(60) # limite les FPS à 60 pygame.quit() |
Etudions maintenant les ajouts au code. 😉
D'abord, on va créer une horloge (clock) en utilisant pygame. Celle-ci nous permettra de gérer le nombre d'images par seconde pour que notre jeu ne tourne ni trop vite, ni trop lentement. 🤪
clock = pygame.time.Clock()
|
Ensuite, dans notre boucle infinie, on va faire deux nouveaux appels à notre class Player() en lui demandant de mettre à jour l'animation de notre joueur (update()) puis de le dessiner (draw()). On verra l'implémentation de ces deux méthodes quand on mettra à jour le fichier Player.py. 😉
#On met à jour le joueur
player.update()
# On affiche le sprite
player.draw(fenetre)
|
Enfin, à la fin de chaque boucle, on fait appel à notre horloge (clock) définie ci-dessus, pour nous limiter à 60 fps (frames ou images par seconde). Comme vous pouvez le voir, c'est très simple car Pygame gère tout le code à notre place ! 😇
clock.tick(60) # limite les FPS à 60
|
Et voilà ! Passons maintenant au nerf de la guerre, là où on va trouver le plus de changements : Player.py. Là encore, recopiez / modifiez les passages qui ne sont pas grisés. 😀
Fichier : Player.py
# Créé par Jay81, le 16/06/2024 en Python 3.7 # Import de pygame import pygame from pygame.locals import * #Import des constantes et classes du jeu from Constantes import * """Classe permettant de créer un personnage""" class Player: def __init__(self): # Chargement du fichier du sprite self.sprite = pygame.image.load(SPRITE_JOUEUR).convert_alpha() # Coordonnées du sprite self.x = 0 self.y = HAUTEUR - PLAYER_HEIGHTH - 40 # Direction du sprite self.direction = DROITE # Animation self.frame = 0 self.timer = TIMER self.state = IDLE self.maxframes = 7 # Mise à jour du sprite def update(self): # On gère le timer (compte à rebours pour faire défiler l'animation) if self.timer < 0: if self.frame < self.maxframes: self.frame += 1 else: self.frame = 0 self.timer = TIMER else: self.timer -= 1 # On dessine le sprite du joueur def draw (self, surface): surface.blit(self.sprite, (self.x, self.y), |
Analysons maintenant ces ajouts plus en détails ! 🤓
D'abord, on va changer l'ordonnée y où on va afficher / blitter notre lapin maléfique ! 👺 En effet, on avait mis 0 avant, mais là, il se retrouverait dans le ciel, ce qui n'est pas terrible... 😿
Alors, on va le blitter au sol en ajoutant à y la HAUTEUR de la fenêtre moins la hauteur de notre perso, moins encore 40 pixels. Bien entendu, cette valeur a été définie pour coller à notre background / fond d'écran et vous pouvez la changer pour tester. 😁
self.y = HAUTEUR - PLAYER_HEIGHTH - 40
|
Ensuite, on va mettre en place les variables pour animer notre sprite, comme dans notre théorie ci-dessus. On va créer frame pour savoir quelle frame (image) on doit afficher. On va aussi créer maxframes et lui donner la valeur 7, comme ça, en partant de 0, on se limite à 8 frames (oui, oui, si vous comptez de 0 à 7, ça fait 8, vous pouvez tester sur vos doigts ! 🤣). Ensuite, on crée notre compte à rebours / timer pour savoir quand changer de frame et enfin on donne la valeur Idle à notre variable state (état), parce que par défaut, notre personnage va commencer en étant statique et en regardant à droite (comme dans la plupart des jeux vidéo... 😜)
# Animation self.frame = 0 self.timer = TIMER self.state = IDLE self.maxframes = 7 |
C'est maintenant, que ça va (un peu) se compliquer. 😅
On va céer une fonction update() (= mise à jour - vous allez devenir des cracks en anglais ! 🧐) qui va mettre à jour l'animation de notre perso. Plus tard, elle pourra aussi gérer ses déplacements, mais on verra ça au chapitre prochain 😜.
En gros, l'idée, c'est qu'à chaque tour de boucle, on va enlever 1 à notre timer (qui vaut 4 au départ, mais vous pourrez le modifier dans les Constantes 🙂). On teste ensuite, pour voir s'il vaut 0 ou moins. Dans ce cas, le compte à rebours est écoulé et on augmente notre frame SAUF si elle est à la frame max (7). Si c'est le cas, on retourne alors à la frame 0. Dans tous les cas, on réinitialise notre timer et on repart pour un tour !
Normalement, ça devrait tourner comme une horloge. 🤩
# Mise à jour du sprite def update(self): # On gère le timer (compte à rebours pour faire défiler l'animation) if self.timer < 0: if self.frame < self.maxframes: self.frame += 1 else: self.frame = 0 self.timer = TIMER else: self.timer -= 1 |
Voilà, il ne nous reste plus qu'à afficher notre sprite aux bonnes coordonnées, comme dans la théorie vue ci-dessus (c'est le même calcul mais en langage Python). ☺️
# On dessine le sprite du joueur def draw (self, surface): surface.blit(self.sprite, (self.x, self.y), |
Et voilà, si vous appuyez sur la flèche verte dans le fichier Main.py, vous verrez maintenant Rabidja s'animer sous vos yeux ébahis ! Vous pouvez jouer avec la vitesse du timer et les coordonnées de blit du sprite pour tester un peu plus. ☺️

A bientôt pour le chapitre 4 ! 😄

English
Français