Here is the fifth and final tutorial, dedicated to discovering Pygame in Python! 😃 There may be others later, but for many, it will already be great to have made it (alive) this far! 😅

   Now we're going to handle the jumps! 😮

   Let's start with the constants  first, this will allow us to do a bit of theory before moving on! 😉

   

File: Constants.py

# Created by Jay81, on 06/16/2024 in Python 3.7

"""Game constants"""

# Window settings 
WIDTH = 640 
HEIGHT = 360

# Window customization 
TITLE = "Rabidja" 
ICON = ""

# List of game images (graphics) 
BACKGROUND = "graphics/background.png" 
SPRITE_JOUEUR = "graphics/rabidja.png"

# Player sprite size 
PLAYER_HEIGTH = 50 
PLAYER_WIDTH = 40

# State machine 
NONE = 0 
RIGHT = 1 
LEFT = 2

IDLE = 0 
WALK = 1 
JUMP = 2 
DOUBLEJUMP = 3 
DEAD = 4

# Timer for animation 
TIMER = 8

# Player speed 
PLAYER_SPEED = 4

# Constants defining gravity and maximum fall speed 
GRAVITY_SPEED = 0.6 
MAX_FALL_SPEED = 15 
JUMP_HEIGHT = 10 

HEIGHT_FLOOR = HEIGHT - PLAYER_HEIGTH - 40

  

   We will first change the value of our TIMER constant so that the animation is slower but you can do as you wish. 😆

  

# Timer for animation 
TIMER = 8

  

   We will then create several constants to manage our jump:

  • GROUND_HEIGHT will correspond to the height from the ground at which Rabidja was displayed until now. The goal will be to test if we are above (so we jump) or at that level (or below), in which case, we must place Rabidja on the ground and manage him as if he were walking or standing still. ☺️
  • GRAVITY_SPEED corresponds to the gravity in real life. That is to say that every frame, Rabidja is made to fall 0.6 pixels. If he is already on the ground, the ground holds him and he is forbidden from going below (otherwise, he would go through the ground and fall forever! 😱)
  • MAX_FALL_JUMP corresponds to the maximum speed of the character's fall (here 15 pixels but you can modify it). Same, it's like in real life, there is a maximum speed at which we can fall and still happy, otherwise we would end up reaching the speed of light!!! 🥵
  • JUMP_HEIGHT corresponds to the force vector which allows jumping (like in physics 🤪).

   Thanks to this, if we press the jump key, in the 1st frame our hero will jump 10 pixels, but caught by gravity, he will lose 0.6 pixels in the second frame, which will make him only rise 9.4 pixels, then 8.8 in frame 3 and so on... When his jump force reaches 0, he will have reached the maximum point of the jump and its value will then become negative, making him fall faster and faster, until reaching the maximum fall speed. 🧐

   You don't have to understand everything to implement it in code and test it. You can have fun changing these values ​​later to better understand how physics works. 😅

   Finally, note that we are not respecting the true values ​​of terrestrial physics here, because we are not capable of making fabulous jumps in real life and a platform game without spectacular jumps would no longer be a platform game. 😌

   

# Constants defining gravity and maximum fall speed 
GRAVITY_SPEED = 0.6 
MAX_FALL_SPEED = 15 
JUMP_HEIGHT = 10 

HEIGHT_FLOOR = HEIGHT - PLAYER_HEIGTH - 40

  

    

   Now let's update the main() !

File: Main.py

# Created by Jay81, on 06/16/2024 in Python 3.7

# Import from pygame 
import pygame
from pygame.locals import *

#Import game constants and classes 
from Constants import *
from Player import *

#Initializing pygame
pygame.init()
clock = pygame.time.Clock()

# Open the game window in 640 x 360 pixel resolution 
window = pygame.display.set_mode((WIDTH, HEIGHT)) #, pygame.FULLSCREEN|SCALED)

#Boolean to know if the game is in full screen (Fullscreen)
isFullScreen = False
#timer to delay the press between two keys if necessary timer = 60 #Title pygame.display.set_caption(TITLE) #Loading the wallpaper (background) background = pygame.image.load(BACKGROUND).convert() #Player Creation player = Player() # Main game loop continues = True while continues : # We test the events for event in pygame.event.get(): # If we press quit, we close the game if event. type == QUIT: continues = False # We test the presses on the Left / Right keys player. input = NONE
dicKeys = pygame.key.get_pressed() if dicKeys[K_LEFT]: player. input = LEFT if dicKeys[K_RIGHT]: player. input = RIGHT if dicKeys[K_LEFT] & dicKeys[K_RIGHT]: player. input = NONE if dicKeys[K_ESCAPE]: continues = False if dicKeys[K_f]: if timer <= 0 : if isFullScreen: isFullScreen = False window = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(TITLE) #pygame.display.toggle_fullscreen() else : isFullScreen = True window = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN|SCALED) timer = 60 # We decrement our timer (countdown) if it is greater than 0 if timer > 0 : timer -= 1 # Jump management if dicKeys[K_c]: player.jump = True # Display the background
window.blit
(background,( 0 , 0 )) # We update the player player.update() # We display the sprite player.draw(window)
#We refresh the screen pygame.display.update() clock.tick( 60 ) # limits FPS to 60 pygame.quit()

   

   And there you have it! 😃 Now, we will explain these modifications step by step.

   We start by creating a boolean to manage the switch to full screen, as well as a timer ⏰ to delay the press between two keys (otherwise, if you leave your finger pressed on a key, the boolean changes value 60 times per second! 😮 It's a bit fast for humans like us! 👽). We must therefore create a countdown / timer ⏰ which delays the support between two presses on a key. Here, we set it to 60 frames, or one second . 🤖

   

#Boolean to know if the game is in full screen (Fullscreen) 
isFullScreen = False 
#timer to delay the press between two keys if necessary timer = 60

  

   To avoid bugs 🐞, if you press multiple keys, you first reset the input variable (to erase any previous key presses when you're not pressing anything anymore 😁). And then, you prohibit the simultaneous pressing of the LEFT and RIGHT keys, canceling them in order to avoid, again, bugs 🕷.

   

    # We test the presses on the Left / Right keys 
    player. input = NONE
     dicKeys = pygame.key.get_pressed()
     if dicKeys[K_LEFT]:
        player. input = LEFT
     if dicKeys[K_RIGHT]:
        player. input = RIGHT 
    if dicKeys[K_LEFT] & dicKeys[K_RIGHT]:
        player.input = NONE

    

   We then manage the transition from full screen to window mode and vice versa by pressing the F key . We use our timer to delay and prevent the screen from flashing completely between full screen and window... 😕

   Note however that there is a bug 🐜 in the version of pygame provided with Edupython and it does not work very well . You have to update pygame using another IDE for it to work. Unfortunately Edupython seems to be at the end of development at the moment and it is no longer updated. 😓

  

if dicKeys[K_f]:

        if timer <= 0 :
                 if isFullScreen:
                    isFullScreen = False
                    window = pygame.display.set_mode((WIDTH, HEIGHT))
                    pygame.display.set_caption(TITLE)
                    #pygame.display.toggle_fullscreen() 
                else :
                    isFullScreen = True
                    window = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN|SCALED)

                timer = 60

    #We decrement our timer (countdown) if it is greater than 0 
    if timer > 0 :
        timer -= 1

    

   And don't forget to use the JUMP key to jump (here C but you can change it if you want 😉).

  

    # Jump management 
    if dicKeys[K_c]:
        player.jump = True

    

  

   And there you have it, now let's move on to the Player.py file , which contains most of the changes! ☺️

   

File:  Player.py

# Created by Jay81, on 06/16/2024 in Python 3.7

# Import from pygame 
import pygame
from pygame.locals import *

#Import game constants and classes 
from Constants import *


"""Class for creating a character""" 
class  Player :

    def  __init__ ( self ):
          # Load the sprite file
         self.sprite = pygame.image.load(SPRITE_PLAYER).convert_alpha()

         # Sprite coordinates 
         self.x = 0
         self.y = GROUND_HEIGHT

         # Sprite Direction
         self.direction = RIGHT

         # Animation 
         self.frame = 0
         self.timer = TIMER
         self.state = IDLE
         self.maxframes = 7

         # Keyboard input management 
         self.input = NONE

         # Jump handling 
         self.jump = False 
         self.dirY = 0 
         self.onGround = True



    # Sprite update 
    def  update ( self ):

         # We manage the timer (countdown to scroll the animation) 
      # If we are on the ground if self.onGround: # Timer from 0 to 7 for walking: if self.timer < 0 : if self.frame < self.maxframes: self.frame += 1 else : self.frame = 0 self.timer = TIMER else : self.timer -= 1
# If we jump, there are only two frames
else : if self.timer < 0 : if self.frame == 0 : self.frame = 1 else : self.frame = 0 self.timer = TIMER else : self.timer -= 1 # Gravity always makes the character fall: we therefore increment the Y vector self.dirY += GRAVITY_SPEED # But we limit it so that the player doesn't start falling too quickly anyway if self.dirY >= MAX_FALL_SPEED: self.dirY = MAX_FALL_SPEED; # Keyboard input management if self.input == LEFT: if self.x > 5 : self.x -= PLAYER_SPEED self.state = WALK self.direction = LEFT elif self. input == RIGHT: if self.x < WIDTH - PLAYER_WIDTH: self.x += PLAYER_SPEED self.state = WALK self.direction = RIGHT else : self.state = IDLE # Jump handling 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 > GROUND_HEIGHT : self.y = GROUND_HEIGHT self.onGround = True #Manage the character's animation if he is jumping/falling if not self.onGround: self.state = JUMP elif not (self.state == WALK): self.state = IDLE self.input = NONE # We draw the player sprite def draw (self, surface): # Jump management: if we touch the ground if self.onGround: # Depending on the direction, we orient the player by doing a flip to the left if self.direction == LEFT: spritecopy = pygame.transform.flip(self.sprite, True , False ) surface.blit(spritecopy, (self.x, self.y),
((self.frame + 2) * PLAYER_WIDTH,self.state * PLAYER_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH)) else : spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH))
else : # Depending on the direction, we orient the player by doing a flip to the left             if self.direction == LEFT: spritecopy = pygame.transform.flip(self.sprite, True , False ) # Attention, The jump animation has only two frames and on the left
               # they are reversed!!! So ; 0 becomes 8 and becomes 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_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH)) else : spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH))

  

   We start by replacing the height from the ground with our constant then we create 3 new variables to manage our jumps:

  • jump which is a boolean to know if we are jumping or not.
  • dirY which is our force vector which will allow us to jump.
  • and onGround which is another boolean to know if we are touching the ground or not.

      

         # Jump handling 
         self.jump = False 
         self.dirY = 0 
         self.onGround = True

    

   Then we need to update the timer ⏱ depending on whether it is jumping or not, because the jump animation is only two frames long. So, in this case, we need to alternate between 0 and 1. 😆

  

# We manage the timer (countdown to scroll the animation) 
      # If we are on the ground if self.onGround: # Timer from 0 to 7 for walking: if self.timer < 0 : if self.frame < self.maxframes: self.frame += 1 else : self.frame = 0 self.timer = TIMER else : self.timer -= 1
# If we skip, there are only two frames
else : if self.timer < 0 : if self.frame == 0 : self.frame = 1 else : self.frame = 0 self.timer = TIMER else : self.timer -= 1

     

   Here, we manage gravity by decreasing the force vector (dirY) by the gravity value (GRAVITY_SPEED) at each frame (as explained above). We then test our dirY vector and limit it to the maximum fall speed: MAX_FALL_SPEED if it exceeds it. 😉

  

         # Gravity always makes the character fall: 
# we therefore increment the Y vector
self.dirY += GRAVITY_SPEED # But we limit it so that the player doesn't
# start falling too quickly anyway
if self.dirY >= MAX_FALL_SPEED: self.dirY = MAX_FALL_SPEED;

    

   Here, we manage the jump itself. If we press the JUMP key and we are on the ground (otherwise, we would end up with infinite jumps... 😨), we give our force vector the value  - JUMP_HEIGHT, we set onGround to False and we reset the animation frame to 0 to avoid bugs 🐛 (because I remind you that the jump animation is less frames long 😉).

   If we had pressed the JUMP key while we were already in the air, then we set self.jump to False to cancel the key press. 😋

   But wait! 😐 Why is the value of JUMP_HEIGHT negative? 😱

   This is because our orthonormal reference point is reversed in a video game. The point (0,0) is at the very top left. So you have to add to go down and subtract to go up ! The complete opposite of logic, yes I know... 🙄

   

   There you go, once we have our force vector dirY , we add it to our Rabidja y coordinate to make it go up or down depending on its value. We just have to check that it does not exceed the height of the ground , and if necessary stick it there otherwise it would fall to infinity, the poor thing!!! ☹👻

   We then update our state machine to:

  • put him in JUMP state if he goes airborne, ✈️
  • and put it in IDLE (immobile) if it is neither in the air nor running. ⛺

   

         # Jump handling 
         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 > GROUND_HEIGHT :
             self.y = GROUND_HEIGHT
             self.onGround = True

         #Manage the character's animation if he is jumping/falling 
         if  not self.onGround:
             self.state = JUMP
         elif  not (self.state == WALK):
              self.state = IDLE

    

   Now that the hardest part is done, all we have to do is manage its display! 🤓

   For walking, the code remains the same. 💻

   For the jump, it's almost the same code, except that since there are only two frames, we have to transform frames 0 and 1 on the right into 8 and 9 for the left . Otherwise, it's the same thing! 😄

  

        # Jump management: if we touch the ground 
        if self.onGround:
             # Depending on the direction, we orient the player by doing a flip to the left 
            if self.direction == LEFT:
                spritecopy = pygame.transform.flip(self.sprite, True , False )
                surface.blit(spritecopy, (self.x, self.y), 
((self.frame + 2) * PLAYER_WIDTH,self.state * PLAYER_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH)) else : spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH))
else : # Depending on the direction, we orient the player by doing a flip to the left             if self.direction == LEFT: spritecopy = pygame.transform.flip(self.sprite, True , False ) # Attention, The jump animation has only two frames and on the left
               # they are reversed!!! So ; 0 becomes 8 and becomes 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_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH)) else : spritecopy = self.sprite surface.blit(spritecopy, (self.x, self.y),
(self.frame * PLAYER_WIDTH,self.state * PLAYER_HEIGTH,PLAYER_WIDTH,PLAYER_HEIGTH))

  

     

   And that concludes this last chapter of the course! 😅 For the next one, it will be about training to teach you how to manipulate the code with some challenges ranging from the simplest to the most complicated and you will be able to evaluate your skills according to your successes! 🤩

  See you soon for new challenges! 😄

   

   

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!