I am writing this blog because I really love programming and creativity.
I believe that the only way to get better at something is to practice and get involved in your passions in as many ways as you can. The main language that I use is Python and I wanted to improve my skills as a programmer by also learning Pygame. I got interested in programming years ago by programming games in JOGL, and ever since then, I just didn’t want to stop.
Today’s blog is broken into two parts. First, I want to lay the foundation for creating a game in Pygame and setting up a simple Pygame program . Then, I want to give some ideas about what kind of games you can make by giving examples of character movements.
By the end of this tutorial, you will know how to set up a program in PyGame and also how to program basic character movements for platforms, top-down games, and classic arcade-style shooter games. Interested? Keep reading!
Getting Started
CLICK HERE TO DOWNLOAD THE SOURCE CODE FROM GITHUB.
First things first. I will assume you have already installed Pygame on your computer, so I will forego the ‘how to download Pygame’ spiel. Of course, if you haven’t already and still need to do so, take a moment and swing on over to the Pygame website and download it for your correct OS.
Set Up Pygame Program
Before we can begin making awesome games in Pygame, we need to layout the basic framework. You can either save this file and use it as a basis for all you games (so you don’t have to always rewrite it), or just refer back to it later to get you started.
To do this, open up a new file, and call it basicpygame.py. Insert the following code:
1 2 3 4 5 |
# import the necessary packages import pygame, sys from pygame import * pygame.init() |
Let’s start by importing the necessary pygame and sys modules on line 2. pygame contains all the necessary functions we need, while sys is not needed for PyGame, but it is necessary later when we want to close the game window.
pygame.init() always needs to be called after importing the Pygame module, but before making a call to any Pygame functions. Don’t forget it!
1 2 3 4 5 6 7 |
# Constants WINDOWWIDTH = 500 WINDOWHEIGHT = 400 # create display surface DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('Empty Screen') |
In lines 2 and 3, we assign values for the width and height of the window that we want to create in number of pixels. Feel free to change these sizes.
pygame.display.set_mode() returns the Surface object for the window (described by the tuple value (WINDOWWIDTH, WINDOWHEIGHT) ). Line 7 sets the title at the top of the window, in this case, with the string value ‘Empty Screen’.
1 2 3 4 5 6 7 |
while True: # main game loop for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update() |
The while True in line 1 denotes the main game loop. While still inside this loop, this bit of code will handle events, update the state of the game, and draw the results of the game state to the screen. Mouse clicks, key presses, or any other events that happen during are continuously monitored and updated to the screen.
Line 2 will iterate sequentially over any events that may occur. Lines 3 – 5 handle what happens if the user closes the window to exit the game.
The pygame.display.update() function in line 7 draws any new changes between the last update and the new one to the screen.
To test this program, open up a terminal window and type:
1 |
$ python basicpygame.py |
You should see a fabulous black window!
CLICK HERE TO DOWNLOAD THE SOURCE CODE FROM GITHUB.
Making Our Character Move
There is a lot to unpack here. So I am going to break this section down into a few parts. I will briefly discuss how pixel coordinates work in Pygame, followed by explaining how to draw shapes which we will use for our character. Then, I will go over the different types of movements and boundary checking for each. Strap in and hold on to your butts.
Window Coordinates
Pixel coordinates in the window we just created are not like the graphs you learned in high school algebra. This is an important concept to understand when gaming, and understanding it now can you save you tons of frustration in the future when thinking about how your character or enemies move.
In our window, the top-left corner is (0,0) and as you go right, the values of the index of x increase, so x = (0, 1, 2, 3, …, 499). Our WINDOWWIDTH = 500, but the index of the first pixel is 0, so the last last pixel’s index is 500 – 1, or 499. If your character is on the right side of the window and you want it to go left, then you will need to subtract pixels, or go negative pixels. For example, if my character is at (250,0) and I wanted it to go to (100,0), then I would need to go -150 pixels left in the window. It is similar for y, only the values increase as you go down, and will decrease as you go up.
This is also important to consider when checking for the boundaries of the window. Otherwise, your character can run off the screen (and never come back).
Creating Our “Character”
I found it much easier before becoming overwhelmed with creating character sprites and designing my own game characters to first just focus on making my characters move. No matter how great a game may look, if the gameplay isn’t great, no one will play it.
For this tutorial, we will use our good old friend, the square, to act as our character. You can always change the shape, if you’d like. Look here for a really good tutorial about how to draw in Pygame. Let’s see how to draw our square.
Open a new file, name it character.py, and copy in the foundation code from the basicgame.py file.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# import the necessary packages import pygame, sys from pygame import * pygame.init() # Constants WINDOWWIDTH = 500 WINDOWHEIGHT = 400 FPS = 30 # frames per second BLACK = (0,0,0) WHITE = (255,255,255) |
Lines 1 – 9 are the same, and FPS in line 10 sets the number of frames per second (this will be used later).
BLACK and WHITE are the constants for the colors we will use. We will use BLACK when we update the screen, and WHITE for the color of our square. I recommend this color picker if you wish to look for new colors and find their RGB values.
1 2 3 4 5 6 7 8 9 10 11 12 |
class Player(pygame.sprite.Sprite): '''This class represents the player''' def __init__(self, width, height): '''call the base (Sprite) constructor class''' super().__init__() # Create the player with width, height and color attributes self.image = pygame.Surface([width,height]) self.image.fill(WHITE) # Draw the hero self.rect = self.image.get_rect() |
This defines the class for our Player sprite. Line 3 is the constructor method for the class which points to the current object. We pass in the arguments self, and the width and height of our player.
Line 5 shows the super().__init__() which calls the base (Sprite) constructor class, rather than having to type pygame.sprite.Sprite.__init__(self).
Then we create the player surface with the given width and height, specify the color in lines 8-9 and then the get_rect method in line 12 creates a new rect with the size of the image and the x, y coordinates. You can also pass in other arguments to get_rect, for example to specify new coordinates to start.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# FPS to control screen updates FPSCLOCK = pygame.time.Clock() # create display surface DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('The Lone Character') # List that contains all sprites in the game active_sprites_list = pygame.sprite.Group() # Spawn sprite and set x, y location player = Player(30, 30) # with width 30 and height 30 player.rect.x = WINDOWWIDTH / 2 - player.rect.centerx player.rect.y = 330 # Add the sprites to the list of objects active_sprites_list.add(player) |
The frame rate of our game is the number of images that our program draws per second (defined earlier by FPS = 30). Line 2 creates the FPSCLOCK variable which monitors our program to make sure it runs the correct number of frames per second.
active_sprites_list in line 9 creates a list that will be used to store our player sprite and will be used later to update the location within the window and draw it to the window. The player is added to the list in line 17 with the function call add(player).
In lines 13 and 14, we specify the x and y coordinates where we want the character to first spawn. We want the character to start center of the window width, so our x coordinate is half the WINDOWWIDTH – half of the character width (player.rect.centerx).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
while True: # main game loop for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() # Game logic goes here active_sprites_list.update() # Drawing code goes here DISPLAYSURF.fill(BLACK) # Draw sprites at once all/refresh the position of the player active_sprites_list.draw(DISPLAYSURF) pygame.display.update() # update screen FPSCLOCK.tick(FPS) # limit frames per second |
In the main game loop, check for any events, update() the the character list in line 8, fill the new Surface object in BLACK (line 11), and then draw the character sprites onto the Surface in line 14.
tick(FPS) is called after update() in line 17. When you have a moving character later, you can change FPS to see how it affects your program.
Test the code by typing the following in the terminal window:
1 |
$ python character.py |
Now, let’s discuss how to get that little square moving and how to keep him from running away with boundary checking.
Movement: Side-to-Side
This is the easiest kind of movement to do. Think Space Invaders or Galaga, where the character can only move left and right and if we touch the boundaries of the window then we can’t go any further. We will use this as our foundation to build the other movements, and it is also a good way to understand the basics of making our sprite move.
For the following parts, I will not show the entire program, but only discuss the parts of the code that have been added or changed (look out for ####### CODE OMITTED#######). Refer to the previous code if you need help.
Let’s first think about what we want the character to do. We will use the left and right arrow keys to move the player, and we need to be able to update the character’s animation each frame. Also, if the character moves too far left or too far right we need to create a way to make out character stop.
Open a new file, name it sidetoside.py, and copy the content of character.py into it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Player(pygame.sprite.Sprite): '''This class represents the player''' def __init__(self, width, height): ####### CODE OMITTED ####### # Create player variables self.changex = 0 # value to move along x def move_left(self, move_x): '''move player left''' self.changex -= move_x def move_right(self, move_x): '''move player right''' self.changex += move_x def update(self): '''update player movement''' self.rect.x += self.changex '''boundary checking''' if self.rect.x < 0: self.rect.x = 0 if self.rect.x > WINDOWWIDTH - self.rect.width: self.rect.x = WINDOWWIDTH - self.rect.width |
From line 9 – 15, we create two functions: move_left and move_right, and each take two arguments, self and move_x. The value of move_x tells us how many pixel spaces to move every frame when the left key is pressed, and similar for the function move_right for the right key. Notice self.changex -= move_x is subtracting the values, since we want to move in a negative direction in the window. Then in the update() function on line 17, the character sprite will be shifted that number of pixels, line 19.
Lines 22 – 26 handle the boundary checking of our sprite. If the character is moving left and the left side of the rect = 0 (where x = 0), then the character is “blocked”, or simply, it keeps getting reset back to 0. Going right, if the character hits the right edge of the window, then it is also “blocked”. We check for WINDOWWIDTH – self.rect.width, because self.rect.x is measuring the left-most value of our sprite. If we only write self.rect.x > WINDOWWIDTH in line 25, then most of our character will be off the screen before it is “blocked”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
while True: # main game loop for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: player.move_left(5) if event.key == pygame.K_RIGHT: player.move_right(5) if event.type == pygame.KEYUP: if event.key == pygame.K_LEFT: player.move_left(-5) if event.key == pygame.K_RIGHT: player.move_right(-5) |
In the main game loop, lines 7 – 11 handle if a key is pressed, while lines 13 – 17 handle if that key is released. If the left arrow key is pressed, then the player’s function move_left is called and the argument passed is the number of pixels we want our character to move per frame. Similarly for the right arrow key. When the key is released, the values passed are negative causing the square to stop moving.
Enter the following code in the terminal to test:
1 |
$ python sidetoside.py |
Movement: Top-down Perspective (Four Directions)
This is one of my personal favorite styles, and can be used in numerous ways: RPGs, racing games, action-adventure games, etc. Programming is similar to the side-to-side games, but now we must also consider the y coordinate values, extra boundary checking, and the up and down keys.
Let’s dive right in. Open a new file, name it topdown.py, and copy the sidetoside.py code into it. We are just going to quickly expand on this code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class Player(pygame.sprite.Sprite): '''This class represents the player''' def __init__(self, width, height): ####### CODE OMITTED ####### # Create player variables self.changex = 0 # value to move along x self.changey = 0 # value to move along y ####### CODE OMITTED ####### def move_up(self, move_y): '''move player right''' self.changey -= move_y def move_down(self, move_y): '''move player right''' self.changey += move_y def update(self): '''update player movement''' self.rect.x += self.changex self.rect.y += self.changey '''boundary checking''' ####### CODE OMITTED ####### if self.rect.y < 0: self.rect.y = 0 if self.rect.y > WINDOWHEIGHT - self.rect.height: self.rect.y = WINDOWHEIGHT - self.rect.height |
In line 8, we add a variable for the value for changes in y.
Lines 12 – 18 are the functions for when the player moves either up or down. The number of pixels that the character moves in the y direction is handled in line 23, and boundary checking when moving up or down is handled in lines 29 – 33.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
while True: # main game loop for event in pygame.event.get(): ####### CODE OMITTED ####### if event.type == pygame.KEYDOWN: ####### CODE OMITTED ####### if event.key == pygame.K_UP: player.move_up(5) if event.key == pygame.K_DOWN: player.move_down(5) if event.type == pygame.KEYUP: ####### CODE OMITTED ####### if event.key == pygame.K_UP: player.move_up(-5) if event.key == pygame.K_DOWN: player.move_down(-5) |
The event checking for a top-down game is similar to the side-to-side. We still need to check if a key is being pressed (lines 5 – 10) or has been released (lines 12 – 17), but now for all four directions.
Enter the following code in the terminal to test:
1 |
$ python topdown.py |
Movement: Platformer or Side-Scroller
I would have to say that the distinctive feature of the platformer is the jump. Without that and the game falls flat. In order to do this, we need to add some code to the sidetoside.py file allowing for the character to jump and consider the effects of gravity.
Open a new file and call it platformer.py. Copy in the code from sidetoside.py. Then proceed to add the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class Player(pygame.sprite.Sprite): ####### CODE OMITTED ####### # Create player variables self.changex = 0 # value to move along x self.changey = 0 # value to move along y ####### CODE OMITTED ####### def gravity(self): '''calculate gravity''' if self.changey == 0: self.changey = 0 else: self.changey += .40 if self.rect.bottom >= 360 and self.changey >= 0: self.changey = 0 self.rect.y = 330 def jump(self): '''make player jump''' if self.rect.bottom == 360: self.changey = -10 def update(self): '''gravity''' self.gravity() ####### CODE OMITTED ####### |
Here we need to consider movement in the y direction. Lines 10 – 19 calculate for the effect of gravity on the player during the jump. If there is no change in changey, then there is no jumping. But once the character has starting moving upward, they start falling back down again. Change the value in line 15 if you are interested to see its effect on the rate of falling or make your sprite jump higher. Lines 17 – 19 stop the player from continuing to fall.
1 2 3 4 5 6 7 8 |
while True: # main game loop for event in pygame.event.get(): ####### CODE OMITTED ####### if event.type == pygame.KEYDOWN: ####### CODE OMITTED ####### if event.key == pygame.K_UP: player.jump() |
Here, when the up key is pressed in lines 7 – 8, the function is called to make the player jump.
CLICK HERE TO DOWNLOAD THE SOURCE CODE FROM GITHUB.
Summary
In this tutorial, we covered how to make the basic mold for creating a game in Pygame, how to make a character sprite, and how to make our character move in three different styles: side-to-side shooter, top-down classic RPG, and platformer.
There is definitely no one single way to write these codes. If you find a method that you like better, then use it. Once you get the character moving, start thinking about what kind of game you want to build, what genre, how will your enemies move, etc. Try to create how your characters will move first before bogging yourself down in the look of the characters or the background.
Please feel free to leave a comment or ask a question.
References:
Invent with Python: This is what got me started in Pygame. A very big source of help and inspiration when coding in Pygame, and useful if you are looking for ideas for your next game.
Using Pygame to move your character around: Very informative and detailed site.