Voilà le cinquième et dernier tuto / cours de SNT, consacré à la découverte de Pygame en Python ! 😃 Il y en aura peut-être d'autres plus tard, mais pour beaucoup, ce sera déjà bien beau d'être parvenus (vivants) jusque là ! 😅
Nous allons maintenant gérer les sauts ! 😮
Commençons d'abord par les constantes, ça nous permettra de faire un peu de théorie avant de passer à la suite ! 😉
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 NONE = 0 DROITE = 1 GAUCHE = 2 IDLE = 0 WALK = 1 JUMP = 2 DOUBLEJUMP = 3 DEAD = 4 # Timer pour l'animation TIMER = 8 # Vitesse du joueur PLAYER_SPEED = 4 # Constantes définissant la gravité et la vitesse max de chute GRAVITY_SPEED = 0.6 MAX_FALL_SPEED = 15 JUMP_HEIGHT = 10 HAUTEUR_SOL = HAUTEUR - PLAYER_HEIGHTH - 40 |
On va d'abord changer la valeur de notre constante TIMER pour que l'animation soit moins rapide, mais vous pouvez faire comme vous le souhaitez. 😆
# Timer pour l'animation
TIMER = 8
|
On va ensuite créer plusieurs constantes pour gérer notre saut :
- HAUTEUR_SOL va correspondre à la hauteur du sol à laquelle on affichait Rabidja jusqu'à présent. Le but sera de tester si on est au-dessus (donc qu'on saute) ou à ce niveau-là (ou en-dessous), auquel cas, on doit placer Rabidja sur le sol et le gérer comme s'il marchait ou était immobile. ☺️
- GRAVITY_SPEED correspond à la gravité dans la vraie vie. C'est-à-dire qu'à chaque frame, on fait chuter Rabidja de 0,6 pixels. S'il est déjà sur le sol, le sol le retient et on lui interdit d'aller en-dessous (sinon, il passerait à-travers le sol et tomberait à l'infini ! 😱)
- MAX_FALL_JUMP correspond à la vitesse maximale de chute du personnage (ici 15 pixels mais vous pouvez la modifier). Pareil, c'est comme dans la vraie vie, il y a une vitesse maximale à laquelle on peut chuter et encore heureux, sinon on finirait par atteindre la vitesse de la lumière !!! 🥵
- JUMP_HEIGHT correspond au vecteur force qui permet de sauter (comme en physique 🤪).
Grâce à ça, si on appuie sur la touche saut, à la 1ère frame notre héros va bondir de 10 pixels, mais rattrapé par la gravité, il va perdre 0,6 pixel à la seconde frame, ce qui va ne le faire plus monter que de 9,4 pixels, puis 8,8 à la frame 3 et ainsi de suite... Quand sa force de saut atteindra 0, il aura atteint le point maximal du saut et sa valeur deviendra alord négative, le faisant retomber de plus en plus vite, jusqu'à atteindre la vitesse maximale de chute. 🧐
Vous n'êtes pas obligé de tout comprendre pour l'implémenter dans le code et le tester. Vous pourrez vous amuser à changer ces valeurs plus tard pour mieux comprendre comment fonctionne la physique. 😅
Notez enfin, que nous ne respectons pas ici les vraies valeurs de la physique terrestre, car nous ne sommes pas capables de faire des bonds fabuleux dans la vraie vie et un jeu de plateformes sans sauts spectaculaires, ce ne serait plus un jeu de plateformes. 😌
# Constantes définissant la gravité et la vitesse max de chute
GRAVITY_SPEED = 0.6
MAX_FALL_SPEED = 15
JUMP_HEIGHT = 10
HAUTEUR_SOL = HAUTEUR - PLAYER_HEIGHTH - 40
|
Mettons maintenant à jour le main() !
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)) #, pygame.FULLSCREEN|SCALED) #Booleen pour savoir si le jeu est en plein écran (Fullscreen) isFullScreen = False #timer pour temporiser l'appui entre deux touches si besoin timer = 60 #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 teste les appuis sur le touches Gauche / Droite player.input = NONE dicKeys = pygame.key.get_pressed() if dicKeys[K_LEFT]: player.input = GAUCHE if dicKeys[K_RIGHT]: player.input = DROITE if dicKeys[K_LEFT] & dicKeys[K_RIGHT]: player.input = NONE if dicKeys[K_ESCAPE]: continuer = False if dicKeys[K_f]: if timer <= 0: if isFullScreen: isFullScreen = False fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR)) pygame.display.set_caption(TITRE) #pygame.display.toggle_fullscreen() else: isFullScreen = True fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR), pygame.FULLSCREEN|SCALED) timer = 60 #On décrémente notre timer (compte à rebours) s'il est supérieur à 0 if timer > 0: timer -= 1 # Gestion du saut if dicKeys[K_c]: player.jump = True # 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() |
Et voilà ! 😃 Maintenant, on va expliquer ces modifications pas à pas.
On commence par créer un booléen pour gérer le passage au plein écran, ainsi qu'un timer ⏰ pour temporiser l'appui entre deux touches (sinon, si vous laissez le doigt appuyé sur une touche, le booléen change de valeur 60 fois par seconde ! 😮 C'est un peu rapide pour des humains comme nous ! 👽). Il faut donc créer un compte à rebours / timer ⏰ qui temporise la prise en charge entre deux appuis sur une touche. Ici, on l'a réglé à 60 frames, soit une seconde. 🤖
#Booleen pour savoir si le jeu est en plein écran (Fullscreen)
isFullScreen = False
#timer pour temporiser l'appui entre deux touches si besoin
timer = 60
|
Pour éviter des bugs 🐞, si l'on appuie sur plusieurs touches, on commence par réinitialiser la variable input (pour effacer tout appui précédent sur une touche alors qu'on n'appuie plus sur rien 😁). Et ensuite, on interdit l'appui simultané sur les touches GAUCHE et DROITE, en les annulant afin d'éviter, là encore, les bugs 🕷.
# On teste les appuis sur le touches Gauche / Droite
player.input = NONE
dicKeys = pygame.key.get_pressed()
if dicKeys[K_LEFT]:
player.input = GAUCHE
if dicKeys[K_RIGHT]:
player.input = DROITE
if dicKeys[K_LEFT] & dicKeys[K_RIGHT]:
player.input = NONE
|
On gère ensuite le passage du plein écran (fullscreen) au mode fenêtre et vice versa en appuyant sur la touche F. On utilise pour cela notre timer afin de temporiser et éviter que l'écran ne clignote à fond entre plein écran et fenêtre... 😕
Notez cependant qu'il y a un bug 🐜 dans la version de pygame fournie avec Edupython et que cela ne fonctionne pas très bien. Il faut mettre à jour pygame en utilisant un autre IDE (Environnement de Développement) pour que ça fonctionne. Malheureusement Edupython semble être en fin de développement actuellement et il n'est plus mis à jour. 😓
if dicKeys[K_f]:
if timer <= 0:
if isFullScreen:
isFullScreen = False
fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR))
pygame.display.set_caption(TITRE)
#pygame.display.toggle_fullscreen()
else:
isFullScreen = True
fenetre = pygame.display.set_mode((LARGEUR, HAUTEUR), pygame.FULLSCREEN|SCALED)
timer = 60
#On décrémente notre timer (compte à rebours) s'il est supérieur à 0
if timer > 0:
timer -= 1
|
Et on n'oublie pas de gérer la touche SAUT pour sauter (ici C mais vous pouvez la changer si vous le désirez 😉).
# Gestion du saut
if dicKeys[K_c]:
player.jump = True
|
Et voilà, passons maintenant au fichier Player.py, qui contient la plupart des changements ! ☺️
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_SOL # Direction du sprite self.direction = DROITE # Animation self.frame = 0 self.timer = TIMER self.state = IDLE self.maxframes = 7 # Gestion des inputs (entrées) clavier self.input = NONE # Gestion des sauts self.jump = False self.dirY = 0 self.onGround = True # Mise à jour du sprite def update(self): # On gère le timer (compte à rebours pour faire défiler l'animation) |
On commence par remplacer la hauteur du sol par notre constante puis on crée 3 nouvelles variables pour gérer nos sauts :
- jump qui est un booléen pour savoir si on est en train de sauter ou pas.
- dirY qui est notre vecteur force qui va nous permettre de sauter.
- et onGround qui est un autre booléen pour savoir si on touche le sol ou pas.
# Gestion des sauts
self.jump = False
self.dirY = 0
self.onGround = True
|
Il faut ensuite qu'on mette à jour le timer ⏱ selon si est en saut ou pas, car l'animation du saut ne fait que deux frames. Il faut donc, dans ce cas-là, alterner entre 0 et 1. 😆
# On gère le timer (compte à rebours pour faire défiler l'animation)
|
Ici, on gère la gravité en diminuant à chaque frame le vecteur force (dirY) de la valeur de la gravité (GRAVITY_SPEED) (comme expliqué plus haut). On teste ensuite notre vecteur dirY et on le limite à la vitesse de chute maximale : MAX_FALL_SPEED s'il la dépasse. 😉
# La gravité fait toujours tomber le perso : on incrémente donc le vecteur Y
self.dirY += GRAVITY_SPEED
# Mais on le limite pour ne pas que le joueur se mette à tomber trop vite quand même
if self.dirY >= MAX_FALL_SPEED:
self.dirY = MAX_FALL_SPEED;
|
Ici, on gère le saut à proprement parler. Si on appuie sur la touche SAUT / JUMP et qu'on est au sol (sinon, on finirait avec des sauts infinis... 😨), on donne à notre vecteur force la valeur - JUMP_HEIGHT, on passe onGround à False et on réinitialise la frame d'animation à 0 pour éviter les bugs 🐛 (car je vous rappelle que l'animation du saut fait moins de frames 😉).
Si, on avait appuyé sur la touche SAUT alors qu'on était déjà en l'air, alors on passe self.jump à False pour annuler l'appui sur la touche. 😋
Mais attends ! 😐 Pourquoi la valeur de JUMP_HEIGHT est négative ? 😱
C'est parce que notre repère orthonormé est inversé dans un jeu vidéo. Le point (0,0) est tout en haut à gauche. Il faut donc additionner pour aller vers le bas et soustraire pour monter ! Tout l'inverse de la logique, oui je sais... 🙄
Voilà, une fois qu'on a notre vecteur force dirY, on l'additionne à notre coordonnée y de Rabidja pour le faire monter ou descendre selon sa valeur. On doit simplement vérifier qu'il ne dépasse pas la hauteur du sol, et le cas échéant l'y coller sinon il tomberait à l'infini, le pauvre !!! ☹👻
On met ensuite à jour notre machine à état pour :
- le mettre en état JUMP s'il s'envoie en l'air, ✈️
- et le mettre en IDLE (immobile) s'il n'est ni en l'air, ni en marche. ⛺
# Gestion du saut
if self.jump:
if self.onGround == True:
self.dirY = -JUMP_HEIGHT
self.onGround = False
self.frame = 0
self.jump = False
self.y += self.dirY
if self.y > HAUTEUR_SOL :
self.y = HAUTEUR_SOL
self.onGround = True
#Gestion de l'animation du personnage s'il est en saut / chute
if not self.onGround:
self.state = JUMP
elif not (self.state == WALK):
self.state = IDLE
|
Maintenant que le plus dur est fait, il ne nous reste plus qu'à gérer son affichage ! 🤓
Pour la marche, le code reste identique. 💻
Pour le saut, c'est quasiment le même code, sauf que comme il n'y a que deux frames, il faut qu'on transforme les frames 0 et 1 de la droite en 8 et 9 pour la gauche. Sinon, c'est la même chose ! 😄
# Gestion du saut : si on touche le sol
if self.onGround:
# Selon la direction, on oriente le joueur en faisant un flip pour la gauche
if self.direction == GAUCHE:
spritecopy = pygame.transform.flip(self.sprite, True, False)
surface.blit(spritecopy, (self.x, self.y),
|
Et voilà qui vient clôturer ce dernier chapitre de cours ! 😅 Pour le prochain, il s'agira d'entraînements pour vous apprendre à manipuler le code avec quelques défis allant du plus simple au plus compliqué et vous pourrez évaluer vos compétences en fonction de vos réussites ! 🤩
A bientôt pour de nouveaux défis ! 😄

English
Français