Intro to Pygame for Gamers!
If you read my previous blog “Meet Technologist: Tyson Roussel” you know I love Pokemon games. So when I was learning Java & OOP (Object Oriented Programming) the first thing that came to mind was Pokemon. Each pokemon could inherit the main class of pokemon, while each pokemon has different stats and moves based on the element type. The main pokemon class could have some methods to interact with the “Pokemon trainer” (the user). Some popular examples include Pikachu’s “pika pika”, Charizard’s “riiizard”, Mewtwo’s “mewtwo”, and Snorlax’s “snore”. That could be a method in the Pokemon class called “makeSound();” but in Pikachu’s class that is inherited from the Pokemon class the method is overridden to respond(return) with “pika pika” instead of the “makeSound();” response in the Pokemon class. If you want to learn more about the principles of OOP with pokemon take a look at this blog I found that goes more into detail. Pokemon is a great example of OOP!
With all that I learned last week and pokemon still on my mind, I thought let's learn how to make a game. So while I was browsing through libraries I found a few. “LWJGL” and “libGDX” are great libraries for game development using Java. Since this week I will be learning Python I found a library called “Pygame”. (All links go to the library website or documentation)
Prerequisites:
I am going to show how I created my simple brick breaker game. I’m going to start with python and pygame already installed and set up. If you don't know how to install Python or pygame, I will leave the links to the documentation here. (Windows, Mac, Linux) Now that we are all set up, let's get started!
So to get started we first need to set up our project. Inside the IDE(Integrated development environment) of your choice, you are going to want to:
- Create your main directory where your project will be stored
- Create a main python file inside your directory (With the .py extension)
- Create a directory for your images or assets.
In this project, you will need 3, a ball, a brick, and a paddle.
Coding the project out:
Now that we have our images and the main file created we can start to code out the project.
Step 1: Import & Initialize Pygame
Inside your python file on line 1, we are going to import pygame. Importing pygame imports the package with all the available pygame modules. Once we imported pygame we have to initialize the modules, we will do so with “pygame.init()
”.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
Step 2: Creating a Screen & Setting a caption
We have to set up a screen where our game is going to be displayed. We will create a “Screen” variable where we will set and store the screen size. This is where our game will be played. To do so we will use the built-in method “pygame.display.set_mode()
”, this method takes two parameters the width and height of the screen. After setting up the screen size let's give our game a title we will do this by using “pygame.display.set_caption()
” this method takes a string as a parameter which will be the title of our game.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
Step 3: Creating a Game Loop
You may be asking what is a game loop. A game loop is a loop that runs our game, once we lose or exit the game it will stop the program. We will create a game loop with a while loop, a while loop runs until a condition becomes true or false. In our case, it will run until our variable becomes true.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
game_over = False
#Our conditional for our while loop, It will keep looping until this becomes True
while not game_over: #Loop Runs until game_over is True
for event in pygame.event.get(): #Loops over all events in pygame
if event.type == pygame.QUIT: #Checks for the event type "QUIT" which is exiting your screen
game_over = True
#If the user triggers the event it will change game_over = True, and exit out of the loop
#Once the loop ends it will run pygame.quit()
pygame.quit()
That is our game loop! Just to summarize it, we are looping while game_over = False
then we are looping through the pygame events, if we close out of the window it will trigger the pygame.QUIT
event, game_over will become True and it will break out of the loop. Once it breaks out of the loop it will trigger the built-in function called “pygame.quit()
” which ends the program.
Step 4: Setting up Frames per second & Screen color
Let's set up the game's frames per second, it will be useful later on to change the speed of the ball. To do that we will set up a Clock, and the rate the clock ticks at. (I will go more in detail later when we set up the ball movement) To set up our screen color, or the background of our game, we will use screen.fill((RGB))
insede of our loop.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
game_over = False
#Our conditional for our while loop, It will keep looping until this becomes True
clock = pygame.tyme.Clock()
while not game_over: #Loop Runs until game_over is True
dt = clock.tick(50)#Setting up the frames per second
screen.fill((0, 0, 0)) #This makes the screen black each loop
for event in pygame.event.get(): #Loops over all events in pygame
if event.type == pygame.QUIT: #Checks for the event type "QUIT" which is exiting your screen
game_over = True
#If the user triggers the event it will change game_over = True, and exit out of the loop
#Once the loop ends it will run pygame.quit()
pygame.quit()
Step 5: Adding our images to our game
We are going to need to add our images to the game, to do so we will store them in a variable, and that variable will call a built-in function(pygame.image.load(“File/path”)
) to get the image. Since images come in different pixel types we will then convert the image into the same pixel type for our game. It helps render the pictures faster. We will do that using “imgVariable.convert_alpha
”. Then we will get the rectangle dimensions of our images with “imgVariable.get_rect()
” which saves the values of “left, top, width, height”. We will use these dimensions to put images in place, tell when collusion(When one img touches another one) happens, and more.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
#bat
bat = pygame.image.load("./images/paddle.png")
bat = bat.convert_alpha()
bat_rect = bat.get_rect()
#ball
ball = pygame.image.load('./images/football.png')
ball = ball.convert_alpha()
ball_rect = ball.get_rect()
#brick
brick = pygame.image.load('./images/brick.png')
brick = brick.convert_alpha()
brick_rect = brick.get_rect()
game_over = False
#Our conditional for our while loop, It will keep looping until this becomes True
clock = pygame.tyme.Clock()
while not game_over: #Loop Runs until game_over is True
dt = clock.tick(50)#Setting up the frames per second
screen.fill((0, 0, 0)) #This makes the screen black each loop
for event in pygame.event.get(): #Loops over all events in pygame
if event.type == pygame.QUIT: #Checks for the event type "QUIT" which is exiting your screen
game_over = True
#If the user triggers the event it will change game_over = True, and exit out of the loop
#Once the loop ends it will run pygame.quit()
pygame.quit()
Step 6: Placing the Bricks in Rows and Columns
Now that we have our brick, we are going to have it cover the screen with bricks. First, we have to create a list that will store the positions of our bricks which we will eventually loop over to display on the screen, then we have to specify how many rows and columns of bricks we want in our game. We will hard code the rows, and the columns we will get dynamically based on the screen size. We need to find how many columns will fit on the screen with some gaps to spread out the bricks.
We will start by adding the amount of gap we want in between each brick, in my case I chose a 10-pixel gap. let’s store it in a variable called “brick_gap
” that we will use to dynamically set the number of columns that will fit on the screen. We will use a calculation to find out how many bricks fit in, that calculation will be stored in our “brick_cols
” variable. To calculate that we will grab the screen size with “screen.get_width()
” and divide that by the height of the brick plus the brick gap. That will store how many bricks can fit in the screen.
Let's put them up on the screen, we will loop over the bricks list to get the positions to display all the bricks. We will use “Screen.blit(img, position)
”, blit is used to put the image up on the screen. Blit takes the 2 parameters, the image and where its positioned. Since we are looping over a list it will display a brick at all available locations that are in our “bricks
” list. In order to see our bricks we will need to update the screen of each loop with “pygame.display.update()
”. We now have the bricks up on the screen and its dynamic. So if you make your screen bigger or smaller it will put in the right amount of bricks to fit the screen.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
#bat
bat = pygame.image.load("./images/paddle.png")
bat = bat.convert_alpha()
bat_rect = bat.get_rect()
#ball
ball = pygame.image.load('./images/football.png')
ball = ball.convert_alpha()
ball_rect = ball.get_rect()
#brick
brick = pygame.image.load('./images/brick.png')
brick = brick.convert_alpha()
brick_rect = brick.get_rect()
bricks = [] #Holding position of bricks
brick_rows = 5
brick_gap = 10
brick_cols = screen.get_width() // (brick_rect[2] + brick_gap)
side_gap = (screen.get_width() - (brick_rect[2] + brick_gap) * brick_cols + brick_gap) // 2
#Creating a for loop to get the amount of bricks and position them on the screen
for y in range(brick_rows):
brickY = y * (brick_rect[3] + brick_gap) #Setting the location of the bricks in the Y axis
for x in range(brick_cols):
brickX = x * (brick_rect[2] + brick_gap) + side_gap #setting the location of the bricks in the X axis
bricks.append((brickX,brickY))#Adding the positions of all the bricks to the screen
game_over = False
#Our conditional for our while loop, It will keep looping until this becomes True
clock = pygame.tyme.Clock()
while not game_over: #Loop Runs until game_over is True
dt = clock.tick(50)#Setting up the frames per second
screen.fill((0, 0, 0)) #This makes the screen black each loop
for b in bricks: #Looping through the positions of bricks
screen.blit(brick, b) #Drawing them on the screen
for event in pygame.event.get(): #Loops over all events in pygame
if event.type == pygame.QUIT: #Checks for the event type "QUIT" which is exiting your screen
game_over = True
#If the user triggers the event it will change game_over = True, and exit out of the loop
pygame.display.update() #Updates screen after each loop
#Once the loop ends it will run pygame.quit()
pygame.quit()
Step 7: Adding bat to screen & Moving the bat with arrow keys
First, we are going to set the position of the bat, we will do so by changing our “bat_rect[1]
” we will get the screen size and subtract 100 pixels from the top and bottom to set the bat at the bottom of the screen. Now, let's move the bat, it's really simple. We first need to get a list of all the key presses in pygame, we will do so by storing “pygame.key.get_pressed()
” into a variable, that will store a list of all the available keypresses. Now we just need to check to see if the left or right arrow is pressed, we will do so with a conditional if pressed[K_LEFT]: x -= 0.5 * dt
So we are seeing if the left key is pressed, if so we are going to change x by subtracting 0.5 times by dt which is our frame rate, I told you it was going to come in handy…that allows the movement to be consistent and smooth. To move to the right we will apply the same logic but instead of decreasing “x” we are going to add to “x”. Then we are going to set bat_rect[0] = x
, and now we have a moving bat. We are so close to finishing our first game!
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
#bat
bat = pygame.image.load("./images/paddle.png")
bat = bat.convert_alpha()
bat_rect = bat.get_rect()
bat_rect[1] = screen.get_height() - 100
#ball
ball = pygame.image.load('./images/football.png')
ball = ball.convert_alpha()
ball_rect = ball.get_rect()
#brick
brick = pygame.image.load('./images/brick.png')
brick = brick.convert_alpha()
brick_rect = brick.get_rect()
bricks = [] #Holding position of bricks
brick_rows = 5
brick_gap = 10
brick_cols = screen.get_width() // (brick_rect[2] + brick_gap)
side_gap = (screen.get_width() - (brick_rect[2] + brick_gap) * brick_cols + brick_gap) // 2
#Creating a for loop to get the amount of bricks and position them on the screen
for y in range(brick_rows):
brickY = y * (brick_rect[3] + brick_gap) #Setting the location of the bricks in the Y axis
for x in range(brick_cols):
brickX = x * (brick_rect[2] + brick_gap) + side_gap #setting the location of the bricks in the X axis
bricks.append((brickX,brickY))#Adding the positions of all the bricks to the screen
game_over = False
#Our conditional for our while loop, It will keep looping until this becomes True
clock = pygame.tyme.Clock()
while not game_over: #Loop Runs until game_over is True
dt = clock.tick(50)#Setting up the frames per second
screen.fill((0, 0, 0)) #This makes the screen black each loop
for b in bricks: #Looping through the positions of bricks
screen.blit(brick, b) #Drawing them on the screen
screen.blit(bat, bat_rect)#Sets the bat in the position of bat_rect
for event in pygame.event.get(): #Loops over all events in pygame
if event.type == pygame.QUIT: #Checks for the event type "QUIT" which is exiting your screen
game_over = True
pressed = pygame.key.get_pressed()#gets all pressable keys
if pressed[K_LEFT]: #If left arrow is pressed it will move 0.5 * 50 to the left
x -= 0.5 * dt
if pressed[K_RIGHT]:#If right arrow is pressed it will move 0.5 * 50 to the right
x += 0.5 * dt
#If the user triggers the event it will change game_over = True, and exit out of the loop
pygame.display.update() #Updates screen after each loop
#Once the loop ends it will run pygame.quit()
pygame.quit()
Part 8: Setting up the ball
First, let's see the ball on the screen, we will do that with the blit command inside the loop. Now let's add a few values to the ball that we will use later, the first one is ball_start = (200, 200)
this is just the position the ball will start at and where it will return to once it hits the ground; Next, we are going to need ball_speed = (3.0, 3.0)
this is the x and y speed that the ball will move; We are going to need a boolean so ball_served = False
this indicates if the ball will be moving or not; sx, sy = ball_speed
will be the x and y values for the speed of the ball, we will change those later, And lastly, we need to update our starting point of the ball we will do so by ball_rect.topleft = ball_start
. Now we need to update the ball when 4 things happen, 1 when the ball is served, 2 when the ball hits the sides of the screen, 3 when the ball hits the bat, and 4 when the ball hits the brick.
To handle when the ball gets served, in our loop we will add another condition if the space key is pressed. We will simply set ball_served = True
.
When the ball hits the side of the screen we will change set the position of the ball to the side of the screen so the ball doesn't go out of the screen, then we will change the x and y values of the ball to change directions to simulate a bounce. When it hits the top we want to flip the y speed to go down, when it hits the left or right we will flip the x speed, and when it hits the bottom we want to reset the ball so we will set our boolean ball_served = false
.
To see if the ball has hit the bat we need to see if the sides of the ball are in the length of the bat, then we will need to determine if the bottom of the ball is touching the top of the bat. If those conditions are true we will change the direction of the ball in the y-axis to go upwards.
Finally breaking bricks. We are first going to need to see if the brick has been deleted delete_brick = None
for now it will be set to none. Now we need to loop over the bricks then we need to check to see if the ball has hit a brick, if it has we will set delete_brick = b
b is the position of that brick. Then let's delete the brick, we will do so by checking if delete_brick
is None, if so we will remove the brick from the list. It will delete the value of b stored in delete_brick
. Now our code is deleting bricks…but it's deleting all of them! We need to only delete one and bounce the ball down when it hits a brick. To do that we will check to see what part of the ball has hit what side of the brick and change the direction based on what side the brick was hit from.
import pygame #Imports the package and modules
pygame.init() #Initializes the modules
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Breaking Bricks")
#bat
bat = pygame.image.load("./images/paddle.png")
bat = bat.convert_alpha()
bat_rect = bat.get_rect()
bat_rect[1] = screen.get_height() - 100
#ball
ball = pygame.image.load('./images/football.png')
ball = ball.convert_alpha()
ball_rect = ball.get_rect()
ball_start = (200, 200)
ball_speed = (3.0, 3.0)
ball_served = False
sx, sy = ball_speed
ball_rect.topleft = ball_start
#brick
brick = pygame.image.load('./images/brick.png')
brick = brick.convert_alpha()
brick_rect = brick.get_rect()
bricks = [] #Holding position of bricks
brick_rows = 5
brick_gap = 10
brick_cols = screen.get_width() // (brick_rect[2] + brick_gap)
side_gap = (screen.get_width() - (brick_rect[2] + brick_gap) * brick_cols + brick_gap) // 2
#Creating a for loop to get the amount of bricks and position them on the screen
for y in range(brick_rows):
brickY = y * (brick_rect[3] + brick_gap) #Setting the location of the bricks in the Y axis
for x in range(brick_cols):
brickX = x * (brick_rect[2] + brick_gap) + side_gap #setting the location of the bricks in the X axis
bricks.append((brickX,brickY))#Adding the positions of all the bricks to the screen
game_over = False
#Our conditional for our while loop, It will keep looping until this becomes True
clock = pygame.tyme.Clock()
while not game_over: #Loop Runs until game_over is True
dt = clock.tick(50)#Setting up the frames per second
screen.fill((0, 0, 0)) #This makes the screen black each loop
for b in bricks: #Looping through the positions of bricks
screen.blit(brick, b) #Drawing them on the screen
screen.blit(bat, bat_rect)#Sets the bat in the position of bat_rect
screen.blit(ball, ball_rect)#Sets the ball in the position of ball_rect
for event in pygame.event.get(): #Loops over all events in pygame
if event.type == pygame.QUIT: #Checks for the event type "QUIT" which is exiting your screen
game_over = True
pressed = pygame.key.get_pressed()#gets all pressable keys
if pressed[K_LEFT]: #If left arrow is pressed it will move 0.5 * 50 to the left
x -= 0.5 * dt
if pressed[K_RIGHT]:#If right arrow is pressed it will move 0.5 * 50 to the right
x += 0.5 * dt
if pressed[K_SPACE]:
ball_served = True
if bat_rect[0] + bat_rect.width >= ball_rect[0] >= bat_rect[0] and \
ball_rect[1] + ball_rect.height >= bat_rect[1] and \
sy > 0:
#if the bat x position + bat width is >= ball x position >= bat x position and
#ball x position + ball height >= bat x position and
#if the ball is heading in a downwards direction:
sy *= -1
sx *= 1.01 #increase difficulty
sy *= 1.01
continue
delete_brick = None
for b in bricks:
bx, by = b
if bx <= ball_rect[0] <= bx + brick_rect.width and \
by <= ball_rect[1] <= by + brick_rect.height:
delete_brick = b
if ball_rect[0] <= bx + 2: #Changing the direction of the ball when it hits the left
sx *= -1
elif ball_rect[0] >= bx + brick_rect.width - 2:#Changing the direction of the ball when it hits the right side
sx *= -1
if ball_rect[1] <= by + 2:
sy *= -1
elif ball_rect[1] >= by + brick_rect.height - 2:
sy *= -1
break
if delete_brick is not None:
bricks.remove(delete_brick)
#top
if ball_rect[1] <= 0: #if the ball hits the top it changes the y speed to go down
ball_rect[1] = 0
sy *= -1
#bottom
if ball_rect[1] >= screen.get_height() - ball_rect.height: #if the ball hits the bottom it will change the value of ball_served and reset the position of the ball
ball_served = False
ball_rect.topleft = ball_start
#left
if ball_rect[0] <= 0: #if the ball hits the right it will change the direction of the ball direction to
ball_rect[0] = 0
sx *= -1
#right
if ball_rect[0] >= screen.get_width() - ball_rect.width:
ball_rect[0] = screen.get_width() - ball_rect.width
sx *= -1
bat_rect[0] = x
if ball_served:
ball_rect[0] += sx
ball_rect[1] += sy
#If the user triggers the event it will change game_over = True, and exit out of the loop
pygame.display.update() #Updates screen after each loop
#Once the loop ends it will run pygame.quit()
pygame.quit()
Part 9: Enjoy your new game
WOOOOOO, we finished our game! It's a simple game but it was so much fun to code out and learn about pygame during my weekend. I hope you enjoyed it as much as I did. Feel free to ask any questions if you are confused or lost on any part of the tutorial.
Thank you for reading, stay tuned for more blogs. Let me know what mechanics you would add to the game in the future!