Gravity in pygame

21,566

There is a tutorial for creating a bouncing ball which I think might be helpful to you.

Now, to add gravity to that simulation, you'd simply add some extra speed in the y-direction every time through the loop:

speed[1] += gravity

What you end up with is kind of goofy however, since the image quickly descends below the bottom of the window never to be seen again :)

The next step is therefore to clip the position of the ball so it must remain in the window:

import os
import sys, pygame
pygame.init()

size = width, height = 320, 240
speed = [1, 1]
black = 0, 0, 0
gravity = 0.1

screen = pygame.display.set_mode(size)

image_file = os.path.expanduser("~/pybin/pygame_examples/data/ball.png")
ball = pygame.image.load(image_file)
ballrect = ball.get_rect()

def clip(val, minval, maxval):
    return min(max(val, minval), maxval)

while 1:
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()

    speed[1] += gravity

    ballrect = ballrect.move(speed)
    if ballrect.left < 0 or ballrect.right > width:
        speed[0] = -speed[0]
    if ballrect.top < 0 or ballrect.bottom > height:
        speed[1] = -speed[1]

    # clip the position to remain in the window

    ballrect.left = clip(ballrect.left, 0, width)
    ballrect.right = clip(ballrect.right, 0, width)        
    ballrect.top = clip(ballrect.top, 0, height)
    ballrect.bottom = clip(ballrect.bottom, 0, height) 

    screen.fill(black)
    screen.blit(ball, ballrect)
    pygame.display.flip()

Okay, now you can incorporate that in your current code and you'll be off and running. However, there are some things you can do to make your code more organized and less repetitive.

For example, consider the massive if...then blocks that follow

for event in pygame.event.get(): 

You could rewrite it as something like:

delta = {
    pygame.K_LEFT: (-20, 0),
    pygame.K_RIGHT: (+20, 0),
    pygame.K_UP: (0, -20),
    pygame.K_DOWN: (0, +20),  
    }
for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        deltax, deltay = delta.get(event.key, (0, 0))
        ball.speed[0] += deltax
        ball.speed[1] += deltay

You could also benefit from putting all the logic associated with the movement of your image into a class:

class Ball(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self) 
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.speed = [0, 0]
        area = pygame.display.get_surface().get_rect()
        self.width, self.height = area.width, area.height

    def update(self):
        self.rect = self.rect.move(self.speed)
        if self.rect.left < 0 or self.rect.right > self.width:
            self.speed[0] = -self.speed[0]
        if self.rect.top < 0 or self.rect.bottom > self.height:
            self.speed[1] = -self.speed[1]
        self.rect.left = clip(self.rect.left, 0, self.width)
        self.rect.right = clip(self.rect.right, 0, self.width)        
        self.rect.top = clip(self.rect.top, 0, self.height)
        self.rect.bottom = clip(self.rect.bottom, 0, self.height)                

Notice the update method is very similar to the code presented by the tutorial. One of the nice things about creating a Ball class is that the rest of your program does not need to know much about how a Ball moves. All the logic is in Ball.update. Moreover, it makes it easy to instantiate many balls. And you could create other classes (airplanes, birds, paddles, etc.) that move differently too and add them to your simulation relatively painlessly.

So, putting it all together, you would end up with something like this:


"""
http://stackoverflow.com/a/15459868/190597 (unutbu)
Based on http://www.pygame.org/docs/tut/intro/intro.html
Draws a red ball bouncing around in the window.
Pressing the arrow keys moves the ball
"""

import sys
import pygame
import os


image_file = os.path.expanduser("~/pybin/pygame_examples/data/ball.png")

delta = {
    pygame.K_LEFT: (-20, 0),
    pygame.K_RIGHT: (+20, 0),
    pygame.K_UP: (0, -20),
    pygame.K_DOWN: (0, +20),  
    }

gravity = +1

class Ball(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self) 
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.speed = [0, 0]
        area = pygame.display.get_surface().get_rect()
        self.width, self.height = area.width, area.height

    def update(self):
        self.rect = self.rect.move(self.speed)
        if self.rect.left < 0 or self.rect.right > self.width:
            self.speed[0] = -self.speed[0]
        if self.rect.top < 0 or self.rect.bottom > self.height:
            self.speed[1] = -self.speed[1]
        self.rect.left = clip(self.rect.left, 0, self.width)
        self.rect.right = clip(self.rect.right, 0, self.width)        
        self.rect.top = clip(self.rect.top, 0, self.height)
        self.rect.bottom = clip(self.rect.bottom, 0, self.height)                

def clip(val, minval, maxval):
    return min(max(val, minval), maxval)

class Main(object):
    def __init__(self):
        self.setup()
    def setup(self):
        pygame.init()
        size = (self.width, self.height) = (640,360)
        self.screen = pygame.display.set_mode(size, 0, 32)
        self.ball = Ball()
        self.setup_background()
    def setup_background(self):
        self.background = pygame.Surface(self.screen.get_size())
        self.background = self.background.convert()
        self.background.fill((0, 0, 0))
        self.screen.blit(self.background, (0, 0))
        pygame.display.flip()
    def draw(self):
        self.screen.blit(self.background, (0, 0))
        self.screen.blit(self.ball.image, self.ball.rect)
        pygame.display.flip()
    def event_loop(self):
        ball = self.ball
        friction = 1
        while True:
            for event in pygame.event.get():
                if ((event.type == pygame.QUIT) or 
                    (event.type == pygame.KEYDOWN and 
                     event.key == pygame.K_ESCAPE)):
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    deltax, deltay = delta.get(event.key, (0, 0))
                    ball.speed[0] += deltax
                    ball.speed[1] += deltay
                    friction = 1
                elif event.type == pygame.KEYUP:
                    friction = 0.99

            ball.speed = [friction*s for s in ball.speed]
            ball.speed[1] += gravity
            ball.update()
            self.draw()
            pygame.time.delay(10)

if __name__ == '__main__':
    app = Main()
    app.event_loop()
Share:
21,566
Fashana
Author by

Fashana

Updated on May 15, 2020

Comments

  • Fashana
    Fashana about 4 years

    I'm making a platform game with pygame, and I would like to add gravity to it. Right now I only have a picture which moves when I press the arrow keys, and my next step would be gravity. Here's my code:

    import pygame, sys
    from pygame.locals import *
    
    pygame.init()
    
    FPS = 30
    fpsClock = pygame.time.Clock()
    
    DISPLAYSURF = pygame.display.set_mode((400, 300), 0, 32)
    pygame.display.set_caption("Jadatja")
    
    WHITE = (255, 255, 255)
    catImg = pygame.image.load("images/cat.png")
    catx = 10
    caty = 10
    movingRight = False
    movingDown = False
    movingLeft = False
    movingUp = False
    
    while True: #main game loop
    
        #update
        for event in pygame.event.get():
        if event.type == KEYDOWN:
            if event.key == K_RIGHT:
                #catx += 5
                movingRight = True
                movingLeft = False
            elif event.key == K_DOWN:
                #caty += 5
                movingDown = True
                movingUp = False
            elif event.key == K_LEFT:
                #catx -= 5
                movingLeft = True
                movingRight = False
            elif event.key == K_UP:
                #caty -= 5
                movingUp = True
                movingDown = False
    
        if event.type == KEYUP:
            if event.key == K_RIGHT:
                movingRight = False
            if event.key == K_DOWN:
                movingDown = False
            if event.key == K_LEFT:
                movingLeft = False
            if event.key == K_UP:
                movingUp = False
    
    
        #actually make the player move
        if movingRight == True:
            catx += 5
        if movingDown == True:
            caty += 5
        if movingLeft == True:
            catx -= 5
        if movingUp == True:
            caty -= 5
    
    
        #exit
        for event in pygame.event.get():
            if event.type == KEYUP:
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit()
    
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
    
        #draw
        DISPLAYSURF.fill(WHITE)
        DISPLAYSURF.blit(catImg, (catx, caty))
    
    
    
        pygame.display.update()
        fpsClock.tick(FPS)
    

    I'm not 100% sure if this code is as smooth as I think it is, but I hope you guys can make something of it.

    Thanks