This article covers the concepts of gravity and jumping in Pygame.
Welcome to Part 2 of game programming with Pygame. In this section we’ll be covering two major topics, giving our player the ability to jump and the implementation of gravity.
This article is part of a series called Game programming with Pygame with each part a direct sequel to the other. Since the code carries over, you might get confused if you haven’t read the previous sections. You can read Part 1 using this link.
Part 1 – Code
Here’s the code from Part 1 which we’ll be using as reference for the rest of the article. The pygame code for Gravity and Jumping will simply be added onto this code.
import pygame from pygame.locals import * import sys pygame.init() vec = pygame.math.Vector2 HEIGHT = 450 WIDTH = 400 ACC = 0.5 FRIC = -0.12 FPS = 60 FramePerSec = pygame.time.Clock() displaysurface = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Game") class Player(pygame.sprite.Sprite): def __init__(self): super().__init__() self.surf = pygame.Surface((30, 30)) self.surf.fill((128,255,40)) self.rect = self.surf.get_rect() self.pos = vec((10, 385)) self.vel = vec(0,0) self.acc = vec(0,0) def move(self): self.acc = vec(0,0) pressed_keys = pygame.key.get_pressed() if pressed_keys[K_LEFT]: self.acc.x = -ACC if pressed_keys[K_RIGHT]: self.acc.x = ACC self.acc.x += self.vel.x * FRIC self.vel += self.acc self.pos += self.vel + 0.5 * self.acc if self.pos.x > WIDTH: self.pos.x = 0 if self.pos.x < 0: self.pos.x = WIDTH self.rect.midbottom = self.pos class platform(pygame.sprite.Sprite): def __init__(self): super().__init__() self.surf = pygame.Surface((WIDTH, 20)) self.surf.fill((255,0,0)) self.rect = self.surf.get_rect(center = (WIDTH/2, HEIGHT - 10)) PT1 = platform() P1 = Player() all_sprites = pygame.sprite.Group() all_sprites.add(PT1) all_sprites.add(P1) while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() displaysurface.fill((0,0,0)) P1.move() for entity in all_sprites: displaysurface.blit(entity.surf, entity.rect) pygame.display.update() FramePerSec.tick(FPS)
If you’ve been following the changes we’ve been doing since Part 1, adding the concept of gravity in a simple one line edit. If you landed directly on this page without going through the prequel to this article, stop right here and go read Part 1. In Part 1 of this game programming series we used concepts like acceleration, velocity and friction which are essential for us to have gravity.
Moving on, the only change you have to do is to the first line in the
move() function of the player. You simply have to change the value of the second parameter from
def move(self): self.acc = vec(0,0.5) pressed_keys = pygame.key.get_pressed() ... ...
Remember that the first parameter represents horizontal acceleration and the second represents vertical. What we’re doing here is giving our player a permanent vertical acceleration of 0.5. In other words, we have created the constant force of gravity.
Note: The value of 0.5 we gave for gravity is designed specifically for this game, given the size of the player and the display screen. You may want to change this value a bit depending on the type of game you are making.
The effects of Gravity
Generally, adding gravity is an easy task. What comes after adding gravity however if difficult. Perhaps you realized this in the previous section, but if we give the Player constant vertical acceleration a.k.a Gravity, won’t he go flying out of the page? If you thought so, you were right.
The GIF above demonstrates this problem perfectly. The solution is to create a set of “rules”. These rules will define nullify the effect of gravity in certain scenarios. For instance, while standing on a platform the player should not be accelerating downwards.
This is where Collision detection comes in. To those who don’t know, Collision detection involves detecting the intersection of two or more objects with each other. In this case, we need to detect when our Player object comes into contact with a platform.
Thankfully the solution is simple. Using the
spritecollide() function, we’re able to determine whether a sprite has collided with others sprites. The syntax of this function is show below.
pygame.sprite.spritecollide( sprite, sprite_group, delete)
- The first parameter takes a single sprite as input, like our Player sprite,
- The second parameter takes a
all_sprites. This function then checks to see if the sprite in first parameter, is touching any of the sprites in the
- The last parameter takes a True or False value, depending on if you want the sprite to be deleted or not after a collision. (Keep this False for most cases).
Using all of these concepts, we create the following piece of code. It’s a new method to be added to the Player class.
... def update(self): hits = pygame.sprite.spritecollide(P1 , platforms, False) if hits: self.pos.y = hits.rect.top + 1 self.vel.y = 0 ... ... platforms = pygame.sprite.Group() platforms.add(PT1)
We have two individual pieces of code here. One of them creates a new sprite group called platforms and adds platform 1 (
PT1) into it. You may not see the importance of this yet with only one platform, but as we start adding in more platforms later you’ll understand.
Back to the
update method. The
spritecollide() function returns it’s value into
hits. We now use an if statement that will activate if a collision has occurred.
Two things are necessary in order to stop the effect of gravity on platforms. One, we need to set the velocity of the Player to zero, using
self.vel.y = 0. Note, we only set the vertical velocity to zero, otherwise we wouldn’t be able to move horizontally on platforms. Secondly, we relocate the player on top of the platform by updating the
self.pos.y variable with the top y-coordinate of the platform.
Using hits checks the first collision in the list that’s returned.
Once again, here’s a short video demonstrating the new additions.
Now that we’ve successfully added gravity, we can add the jumping mechanic to our player. There’s no point in having a jump feature without gravity so implementing gravity always comes first.
The jump method is extremely simple. All you have to do is assign a vertical velocity to the player object. It has to be negative since we want to go up.
def jump(self): self.vel.y = -15
-15 is an arbitrary value that we picked. You may wish to alter this slightly depending on the style and environment of your game.
Next up we actually have to call the method we just created. It’s not so simple as just passing
jump() into the Game Loop. We want this
method() to trigger only when press a specific button. In this case, we’ll be mapping the “Space” button as the trigger for jumping.
All you have to do is create a new set of if statements in the game loop. You need to check for two things. The first if statements checks to see if a button has been pressed on the keyboard. If yes, it checks to see if that button press was the space-bar. If yes, it will call the jump method of the Player.
while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: P1.jump() ... ...
However, this results in a small problem or “loop hole”. If the person keeps pressing space bar, the player will jump again in mid air. We’ve shown this problem in the GIF below.
Now, some of you may be looking for this kind of jumping system, but fixing this problem is important for the kind of game we are developing here.
To achieve the output shown above, you will have to modify your
update function slightly.
def update(self): hits = pygame.sprite.spritecollide(P1 ,platforms, False) if P1.vel.y > 0: if hits: self.vel.y = 0 self.pos.y = hits.rect.top + 1
The new addition is a single line, an if statement. This makes sure that the velocity is not set to zero unless there is already some initial velocity. This avoids a bug where the effect of the jump function is nullified due to the
Now to actually fix the double jumping problem.
def jump(self): hits = pygame.sprite.spritecollide(self, platforms, False) if hits: self.vel.y = -15
To sum up, this new
jump method() checks to see if the Player object is in contact with an platform. If so, only then it will allow the jump to be carried out. This automatically ensures the player cannot jump again until he is in contact with the floor again.
If you think that our Jumping mechanic is lacking in it’s implementation, don’t worry. This isn’t the end of it. We’re taking things slowly, putting in the major features first, and then improving upon them. You can expect to see an upgrade to the Jumping mechanic around Part 4 of this series.
In the next part, we’re finally going to get around to level generation in Pygame. This section is probably going to be the hardest from this entire series. As usual, you will find the complete pygame code for our Gravity and Jumping section in the next part.
This marks the end of the Pygame, Gravity and Jumping article. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the article content can be asked in comments section.