Search code examples
pythonpygame

I cannot add gravity to my player in pygame


I tried adding gravity to my player in pygame, but I can move the player with key controls but gravity is not working, Here I used sprite class for making this. I got the rect of the image then add the x momentum and y momentum. X momentum worked while moving the player but y momentum didn't work while adding the gravity. Please help!

# Platformer
import pygame

# Basic Setup
pygame.init()
clock = pygame.time.Clock()

# Game Colors
white = (255, 255, 255)
light_blue = (105, 142, 255)

# Game Settings
game_title = "Platformer!"
screen_width = 1280
screen_height = 750
fps = 120

player_speed = 4
player_gravity = 0.2

# Main Window
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption(game_title)


class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.image.load("IMGs/player.png")
        self.image.set_colorkey(white)
        self.rect = self.image.get_rect(center=(screen_width / 2, screen_height / 2))

        self.x_momentum = 0
        self.y_momentum = 0
        self.moving_right = False
        self.moving_left = False

    def move(self):
        # Set the movement to zero at first
        self.x_momentum = 0
        self.y_momentum = 0

        # Move the player on key press
        if self.moving_right:
            self.x_momentum += player_speed
        elif self.moving_left:
            self.x_momentum -= player_speed

        # Add Gravity
        self.y_momentum += player_gravity

        # Move the player
        self.rect.x += self.x_momentum
        self.rect.y += self.y_momentum

    def update(self):
        self.move()


class Main:
    def __init__(self):
        # Sprites
        self.all_sprites = pygame.sprite.Group()

        # Player Sprite
        self.player = Player()
        self.all_sprites.add(self.player)

    def draw(self, surface):
        self.all_sprites.draw(surface)

    def update(self):
        self.all_sprites.update()


main_game = Main()


def main_game_loop():
    while True:
        # Handling Inputs
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                exit(0)
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    main_game.player.moving_right = True
                elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    main_game.player.moving_left = True
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                    main_game.player.moving_right = False
                elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
                    main_game.player.moving_left = False

        # Draw / Render
        screen.fill(light_blue)
        main_game.draw(screen)
        main_game.update()
        pygame.display.update()

        # Manage Speed
        clock.tick(fps)


main_game_loop()


Solution

  • The gravity doesn't work because self.y_momentum is set 0 at the begin of Player.move:

    class Player(pygame.sprite.Sprite):
       # [...]
    
       def move(self):
           # Set the movement to zero at first
           self.x_momentum = 0
           self.y_momentum = 0
     
           # [...]
    

    Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.

    The coordinates for Rect objects are all integers. [...]

    The fraction part of the coordinates gets lost when the position stored in the Rect object is decremented:

    # Move the player
    self.rect.x += self.x_momentum
    self.rect.y += self.y_momentum
    

    If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location (e.g. .topleft) of the rectangle:

    class Player(pygame.sprite.Sprite):
        def __init__(self):
            # [...]
    
            self.x = screen_width // 2
            self.y = screen_height // 2
            self.rect = self.image.get_rect(center = (self.x, self.y))
    
            # [...]
    
        def move(self):
            # [...]
    
            # Move the player
            self.x += self.x_momentum
            self.y += self.y_momentum
            self.rect.topleft = round(self.x), round(self.y)