Welcome to our Pygame Tutorial Part 2.
Game Creation – Part 2
Our game is still pretty incomplete. There’s no fun in playing a game with the same thing happening over and over again. There is no end point, no variation in the game difficulty and most importantly, there are no consequences of colliding with the enemy.
In this section we’re going to cover Sprite Grouping, Collision Detection, User events and some other minor features.
Here’s the Code Version 2. We’ve made several additions, changed several lines and even removed some lines. Take a good look at the code before moving on to the explanation.
#Imports
import pygame, sys
from pygame.locals import *
import random, time
#Initializing
pygame.init()
#Setting up FPS
FPS = 60
FramePerSec = pygame.time.Clock()
#Creating colors
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Other Variables for use in the program
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
SPEED = 5
#Create a white screen
DISPLAYSURF = pygame.display.set_mode((SCREEN_WIDTH,SCREEN_HEIGHT))
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("Game")
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Enemy.png")
self.rect = self.image.get_rect()
self.rect.center = (random.randint(40,SCREEN_WIDTH-40), 0)
def move(self):
self.rect.move_ip(0,SPEED)
if (self.rect.top > 600):
self.rect.top = 0
self.rect.center = (random.randint(30, 370), 0)
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Player.png")
self.rect = self.image.get_rect()
self.rect.center = (160, 520)
def move(self):
pressed_keys = pygame.key.get_pressed()
#if pressed_keys[K_UP]:
#self.rect.move_ip(0, -5)
#if pressed_keys[K_DOWN]:
#self.rect.move_ip(0,5)
if self.rect.left > 0:
if pressed_keys[K_LEFT]:
self.rect.move_ip(-5, 0)
if self.rect.right < SCREEN_WIDTH:
if pressed_keys[K_RIGHT]:
self.rect.move_ip(5, 0)
#Setting up Sprites
P1 = Player()
E1 = Enemy()
#Creating Sprites Groups
enemies = pygame.sprite.Group()
enemies.add(E1)
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)
all_sprites.add(E1)
#Adding a new User event
INC_SPEED = pygame.USEREVENT + 1
pygame.time.set_timer(INC_SPEED, 1000)
#Game Loop
while True:
#Cycles through all events occuring
for event in pygame.event.get():
if event.type == INC_SPEED:
SPEED += 2
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAYSURF.fill(WHITE)
#Moves and Re-draws all Sprites
for entity in all_sprites:
DISPLAYSURF.blit(entity.image, entity.rect)
entity.move()
#To be run if collision occurs between Player and Enemy
if pygame.sprite.spritecollideany(P1, enemies):
DISPLAYSURF.fill(RED)
pygame.display.update()
for entity in all_sprites:
entity.kill()
time.sleep(2)
pygame.quit()
sys.exit()
pygame.display.update()
FramePerSec.tick(FPS)
That’s alot of code, but considering the average game created in Python pygame, it’s still very small. We’ll now proceed to discuss all the new changes and additions, block by block.
Explanation
#Creating Sprites Groups
enemies = pygame.sprite.Group()
enemies.add(E1)
all_sprites = pygame.sprite.Group()
all_sprites.add(P1)
all_sprites.add(E1)
In this section, we’ve created “groups” for our sprites. A Sprite group is sort of like a classification. It’s much easier to deal with 2 or 3 groups, rather than having to deal with dozens or even hundreds of sprites. Keeping them in one group allows us to easily access every sprite in that group.
In the example above, we’ve created two groups one called enemy
and the other called all_sprites
. (This code doesn’t have more than one enemy, but since multiple enemies could easily be added here, we’ve created a separate group for it). To add a Sprite to a group, you just have to use the add()
function.
INC_SPEED = pygame.USEREVENT + 1
pygame.time.set_timer(INC_SPEED, 1000)
We talked about event objects earlier, such as QUIT. Python Pygame, gives us the option to create custom events called “User events”. Here we’ve created an event called INC_SPEED. To do so, we called the pygame.USEREVENT and added one into it (to ensure that it will have a unique ID). More about events in a separate tutorial.
Next we use the Pygame time module’s set_timer()
function to call the INC_SPEED event object every 1000 milliseconds, or 1 second.
while True:
for event in pygame.event.get():
if event.type == INC_SPEED:
SPEED += 2
The next piece of code if about the Game loop. In the for loop where we iterate over every event that occurs, we insert an if statement to check for the INC_SPEED event occuring. If it does, we increase the SPEED variable by 2. This variable us used by the Enemy class to determine it’s speed.
All in all, the purpose of this code is to make the game more challenging as time passes.
#To be run if collision occurs between Player and Enemy
if pygame.sprite.spritecollideany(P1, enemies):
DISPLAYSURF.fill(RED)
pygame.display.update()
for entity in all_sprites:
entity.kill()
time.sleep(2)
pygame.quit()
sys.exit()
This section of code is related to collision detection in Python pygame. Remember how we created groups earlier? You’re about to see a massive benefit that we get from having meaningful groups.
The spritecollideany()
function takes two parameters, the first must be a regular Sprite, like P1 or E1. The second must be a Sprite group, such as Enemies or all_sprites. This function compares the sprite passed in the first parameter, to see if it’s touching any of the sprites in the group passed in parameter two.
In our case, it checks to see whether our Player has collided with any of the sprites in the enemies group. The benefit of this function is that even if there are 1000 enemy sprites, we don’t have to check collisions with them individually, and instead just use this function.
Finally, the collision holds True, we kill all the sprites using the kill()
function, fill the screen with red, wait two seconds and close the entire program.
(Calling kill()
will remove the sprite from the group, hence it will no longer be drawn to the screen. If you don’t use groups, and try kill()
on a sprite, you might not get the desired effect)
for entity in all_sprites:
DISPLAYSURF.blit(entity.image, entity.rect)
entity.move()
Yet another benefit of the grouping system, we can now call the “move” functions for all the sprites and redraw them in just 3 lines of code. If you’ve noticed, we’ve removed the two draw()
functions from both the Player and Enemy class in our code.
Next Section
In the next tutorial, we will add a bunch of bonus features like Backgrounds, Music and Text.
Proceed to the next tutorial by clicking the button below. The complete code for the whole game + the images used will be found at the end of the series. There are a total of 3 tutorials, including this one.
Other Resources
Some of the things we didn’t cover (fully) in this Tutorial are listed below, where we’ve covered them properly in other articles. If you’re interested in learning more about Python Pygame and making your games look better, make sure you read them.
- Pygame Font and Text
- Pygame Audio Mixer
- Pygame UserEvents
- Pygame Platformer Series
- Pygame RPG Tutorial Series
In a game like this, static backgrounds can be a little boring, whereas scrolling backgrounds help add to the “realistic” aspect to the game. We have another article on our site where we’ve discussed how to create “scrolling backgrounds” in Pygame. We’ve used the exact same code as shown above, so you should have no trouble adjusting.
If you’re into reading books, I advise you to check out this one, Making games with Python and Pygame. It’s a great book, written in a fun and engaging style. In fact, this article actually takes quite a bit of inspiration from it too. It has almost a dozen different types of game projects in it, each explained through a step by step process.
This marks the end of the Python Pygame Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the Tutorial can be asked in the comments section below.