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)
      # Si on est au sol if self.onGround: # Timer de 0 à 7 pour la marche: if self.timer < 0: if self.frame < self.maxframes: self.frame += 1 else: self.frame = 0 self.timer = TIMER else: self.timer -= 1
# Si on saute, il n'y a plus que deux frames
else: if self.timer < 0: if self.frame == 0: self.frame = 1 else: self.frame = 0 self.timer = TIMER else: self.timer -= 1 # 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; # Gestion des inputs (entrées) clavier if self.input == GAUCHE: if self.x > 5: self.x -= PLAYER_SPEED self.state = WALK self.direction = GAUCHE elif self.input == DROITE: if self.x < LARGEUR - PLAYER_WIDTH: self.x += PLAYER_SPEED self.state = WALK self.direction = DROITE else: self.state = IDLE # 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 self.input = NONE # On dessine le sprite du joueur def draw (self, surface): # 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),
((self.frame + 2) * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH)) else: spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH))
else: # 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) # Attention, L'animation de saut n'a que deux frames et à gauche
               # elles sont inversées !!! Ainsi ; 0 devient 8 et devient 9:                 if self.frame == 0: frame = 8 elif self.frame == 1: frame = 9 surface.blit(spritecopy, (self.x, self.y),
(frame * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH)) else: spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH))

  

   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)
      # Si on est au sol if self.onGround: # Timer de 0 à 7 pour la marche: if self.timer < 0: if self.frame < self.maxframes: self.frame += 1 else: self.frame = 0 self.timer = TIMER else: self.timer -= 1
# Si on saute, il n'y a plus que deux frames
else: if self.timer < 0: if self.frame == 0: self.frame = 1 else: self.frame = 0 self.timer = TIMER else: self.timer -= 1

    

   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), 
((self.frame + 2) * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH)) else: spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH))
else: # 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) # Attention, L'animation de saut n'a que deux frames et à gauche
               # elles sont inversées !!! Ainsi ; 0 devient 8 et devient 9:                 if self.frame == 0: frame = 8 elif self.frame == 1: frame = 9 surface.blit(spritecopy, (self.x, self.y),
(frame * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH)) else: spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGHTH,PLAYER_WIDTH,PLAYER_HEIGHTH))

  

     

   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 ! 😄

   

   

This site uses cookies to enable you to log in. We do not store or sell any personal data. By continuing to use this website, you agree to their use. Thanks!