Question

What is the best way to calculate gravity in pygame? I basically just need it so when the player pushes "Up", the character jumps. Here is my code so far (just a white screen with a red block that moves around)

import pygame
import random

# Define colors
black    = (   0,   0,   0)
white    = ( 255, 255, 255)
green    = (   0, 255,   0)
red      = ( 255,   0,   0)

#Classes
class Player(pygame.sprite.Sprite):
    def __init__(self, color, width, height):
        pygame.sprite.Sprite.__init__(self) 
        self.image = pygame.Surface([width, height])
        self.image.fill(color)
        self.rect = self.image.get_rect()

    def move(self, x_change, y_change):
        self.rect.x += x_change
        self.rect.y +=  y_change


#Lists
all_sprites_list = pygame.sprite.Group()

#Spawn player
player = Player(red,16,16) 
all_sprites_list.add(player)
player.rect.x = 0
player.rect.y = 484

#Initalize
pygame.init()
#Set the width and height of the screen [width,height]
screen_height = 700
screen_width = 500
size=[screen_height,screen_width]
screen=pygame.display.set_mode(size)
#Name on top tab
pygame.display.set_caption("My Game")

#DONT CHANGE
done = False
clock=pygame.time.Clock()

#MAIN LOOP
while done == False:
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done = True # Quit

        if event.type == pygame.KEYUP:
                    # If it is an arrow key, reset vector back to zero
                    if event.key == pygame.K_LEFT:
                        None      

    keyDown = pygame.key.get_pressed()
    if keyDown[pygame.K_RIGHT]:
        player.move(3, 0)   
    if keyDown[pygame.K_LEFT]:
        player.move(-3, 0)        
    if keyDown[pygame.K_DOWN]:
        player.move(0, 3)                        
    if keyDown[pygame.K_UP]:
        player.move(0,-3)

    #If player hits side of screen, do this
    if player.rect.x < 0:
        player.rect.x = 0
    if player.rect.x > 684:
        player.rect.x = 684
    if player.rect.y < 0:
        player.rect.y = 0
    if player.rect.y > 484:
        player.rect.y = 484    


    #Clear screen
    screen.fill(white)

    #Drawing
    all_sprites_list.draw(screen)

    #FPS Lock
    clock.tick(60)

    #Update screen
    pygame.display.flip()

# Close the window and quit.
pygame.quit()
Was it helpful?

Solution

Super Mario clones typically move a specific amount of time up, then down until he hits something solid (the floor, or possibly a turtle). As you may know, it looks very unrealistic.

The physics formula for "jumping and falling" is: d = (at²)/2, where d is the distance jumped up, a is the gravity, and t is the time. However, for a simple game that can be adjusted :)

  1. Set a target 'height' for your jumps; for example, 32 pixels in total.
  2. Use 1 for gravity.
  3. Measure time t in game ticks.
  4. That gives you the time it takes to reach 32 pixels:

     32 = (t²)/2
     t² = 64
     t = 8
    
  5. The 'speed' of your jump is v = at. Since you assume a=1 above, v = t, i.e., v = 8. As it appears, your initial ypos needs to be set to speed/2 (probably because this is an approximation..)

  6. In each next game tick, add speed to your y position and subtract 1 from speed
  7. Now the following will happen in each game tick from the moment you press 'up' (assuming y=0 at start):

    1. speed = 8 -> y=4 (since this is "jump 0")
    2. speed = 7 -> y=11
    3. speed = 6 -> y=17
    4. speed = 5 -> y=22
    5. speed = 4 -> y=26
    6. speed = 3 -> y=29
    7. speed = 2 -> y=31
    8. speed = 1 -> y=32
    9. speed = 0 -> y=32
    10. speed = -1 -> y=31
    11. speed = -2 -> y=29 ... down to 0 again (but check on overflow for lower-than-zero).

On the "half speed" fix: strange .. the math works out this way (you hover for just a moment on exactly the right height) but I can't figure out off-hand why you should not start with the actual speed.


Edit

Just thought of the reason why my maths seemed off.

The problem lies in my statement that if your initial velocity -- at Start of Jump -- is 8 (pixels per game tick), you end up exactly 8 pixels higher at the "end" of that tick. You don't, because the "gravity" kicks in immediately, as soon as you leave the ground. At the "end" of the game tick, your speed has decreased to 7; therefore, your average speed is not 8 from start to end of this tick, it's only 7.5, and you end up on an y-pos of 7.5 pixels. The same goes for every next tick; your speed still decreases with 1 per tick.

So, after the (correctly calculated!) total jumping-up time of 8 ticks, you traveled

7.5 + 6.5 + 5.5 + 4.5 + 3.5 + 2.5 + 1.5 + 0.5 = 32 pixels

It's possible to "correctly" implement it, but it introduces floating-point arithmetics in what is currently an integer-only calculation, and it would require you to draw your sprite at an y position of "7.5 pixels". Floating-point calculations may suffer from rounding and 'exact' comparison issues, so that's why it is best to avoid it where possible -- certainly for this kind of simple physics.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top