Q:

Gravity in pygame

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

A:

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()

