Game Development with Python
In this article, we will talk about game development basics in Python. We will review the Pygame library.
Pygame library includes several modules with functions for drawing graphics, playing sounds, handling mouse input, etc.
GUI vs CLI
The Python programs that you can write with Python's built-in functions only deal with the text through the
input() functions. Your program can display text on the screen and let the user type in the text from the keyboard. These types of programs have a command-line interface (CLI). These programs are limited because they can't display graphics, have colors, or use the mouse. These CLI programs only get input from the keyboard with the
input() function and even then, the user must press Enter before the program can respond to the input.
Pygame provides functions for creating programs with a graphical user interface (GUI). Instead of a text-based CLI, programs with a graphics-based GUI can show a window with images and colors.
Hello World with Pygame
The following program will display a black windows with the Hello World! caption.
import pygame, sys from pygame.locals import * pygame.init() disp = pygame.display.set_mode((400, 300)) pygame.display.set_caption('Hello World!') while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update()
pygame.display.set_mode return the
pygame.Surface object for the window. A tuple in the argument tells the
set_mode() function how wide how high to make the window in pixels (400, 300).
A game loop is a loop where the code does the following:
- Handles events
- Updates the game state
- Draws the game state to the screen
Any time the user does one of several actions such as pressing a keyboard key or moving the mouse on the program's window, a
pygame.event.Event object is created by the Pygame library to record this event. We can find out which events have happened by calling the
pygame.event.get() function, which returns a list of
Any time the user does one of several actions, such as pressing a keyboard key or moving the mouse, a
pygame.event.Event object is created by the Pygame library to record this event. We can find out witch events have happened by calling the
pygame.event.get() function, which returns a list of
The QUIT Event and pygame.quit() Function
Event objects have a member variable named
type which tells us what kind of event the object represents. Pygame has a constant variable for each of possible types in the
pygame.locals modules. Since we used the
from pygame.locals import * form of the
import statement, wy only have to type
QUIT instead of
If the Event object is a quit event, then the
sys.exit() functions are called. The
pygame.quit() function deactivates the Pygame library.
Surface objects are objects that represent a rectangular 2D image. The Surface object returned by
pygame.display.set_mode() is called the display Surface.
In Pygame, we represent colors with tuples of three integers (RGB values). The first value in the tuple is how much red is in the color. An integer value of 0 means there is no red in this color, and the value of 255 means there is the maximum amount of red in the color. The second value is for green and the third value is for blue.
Because you can use any combination of 0 to 255 for each of the three primary colors, this means that Pygame can draw 16 777 216 different colors (256256256). However, if you try to use a number larger than 255 or a negative number, you will get an error.
You can mix the amount of red, green, and blue to form other colors. Here are the RGB values for a few common colors:
| Color | RGB Values | |-----------|-----------------| | Aqua | (0, 255, 255) | | Black | (0, 0, 0) | | Blue | (0, 0, 255) | | Fuchsia | (255, 0, 255) | | Gray | (128, 128, 128) | | Green | (0, 128, 0) | | Lime | (0, 255, 0) | | Maroon | (128, 0, 0) | | Navy Blue | (0, 0, 128) | | Olive | (128, 128, 0) | | Purple | (128, 0, 128) | | Red | (255, 0, 0) | | Silver | (192, 192, 192) | | Teal | (0, 128, 128) | | White | (255, 255, 255) | | Yellow | (255, 255, 0) |
You can get the transparent shading effect by adding a fourth 0 to 255 integer value to your color values. This value is known as the alpha value. It is a measure of how opaque color is.
In order to draw using transparent colors, you must create a Surface object with the
convert_alpha() method. For example, the following code creates a Surface object that transparent colors can be drawn on:
surf = disp.convert_alpha()
It is important to note that you cannot use transparent colors on Surface objects not returned from a
convert_alpha() call, including the display Surface that was returned from
You need to know how to represent a color because Pygame's drawing functions need a way to know which color you want to draw with. A tuple of three or four integers is one way. Another way is a
pygame.Color() object. You can create Color objects by calling the
pygame.Color() constructor function and passing either three or four integers. You can store this Color object in variables just like you can store tuples in variables.
import pygame pygame.Color(255, 0, 0) myColor = pygame.Color(255, 0 , 0, 128) myColor == (255, 0, 0, 128)
Any drawing function in Pygame that has a parameter for color can have either a tuple form or Color object form of color passed for it. Even though they are different data types, a Color object is equal to a tuple of four integers if they both represent the same color.
Pygame has two ways to represent rectangular areas. The first is a tuple of four integers:
- The X coordinate of the top left corner
- The Y coordinate of the top left corner
- The width (in pixels) of the rectangle
- The height of the rectangle
The second way is a
pygame.Rect object. For example, the code below creates a Rect object with a top left corner at (10, 20) that is 200 pixels wide and 300 pixels tall:
import pygame spamRect = pygame.Rect(10, 20, 200, 300) spamRect == (10, 20, 200, 300)
Rect object automatically calculates the coordinates for other features of the rectangle. For example, if you need to know the X coordinate of the right edge of the
pygame.Rect object we stored in the
spamRect variable, you can access the Rect object's
Here is a list of all the attributes that
pygame.Rect objects provide:
| Attribute Name | Description | |--------------------|------------------------------------------------------------------------| | myRect.left | The int value of the X-coordinate of the left side of the rectangle | | myRect.right | The int value of the X-coordinate of the right side of the rectangle | | myRect.top | The int value of the Y-coordinate of the top side of the rectangle | | myRect.bottom | The int value of the Y-coordinate of the bottom side of the rectangle | | myRect.centerx | The int value of the X-coordinate of the center of the rectangle | | myRect.centery | The int value of the Y-coordinate of the center of the rectangle | | myRect.width | The int value of the width of the rectangle | | myRect.height | The int value of the height of the rectangle | | myRect.size | A tuple of two ints: (width, height) | | myRect.topleft | A tuple of two ints: (left, top) | | myRect.topright | A tuple of two ints: (right, top) | | myRect.bottomleft | A tuple of two ints: (left, bottom) | | myRect.bottomright | A tuple of two ints: (right, bottom) | | myRect.midleft | A tuple of two ints: (left, center) | | myRect.midright | A tuple of two ints: (right, center) | | myRect.midtop | A tuple of two ints: (centerx, top) | | myRect.midbottom | A tuple of two ints: (centerx, bottom) |
Primitive Drawing Functions
Pygame provides several different functions for drawing different shapes onto a surface object. These shapes such as rectangles, circles, ellipses, lines, or individual pixels are often called drawing primitives.
import pygame, sys from pygame.locals import * pygame.init() #set up the window surf = pygame.display.set_mode((500, 400), 0, 32) pygame.display.set_caption('Drawing') #set up the colors BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) #draw on the surface object surf.fill(WHITE) pygame.draw.polygon(surf, GREEN, ((146, 0), (291, 106), (236, 277), (56, 277), (0, 106))) pygame.draw.line(surf, BLUE, (60, 60), (120, 60), 4) pygame.draw.line(surf, BLUE, (120, 60), (60, 120)) pygame.draw.line(surf, BLUE, (60, 120), (120, 120), 4) pygame.draw.circle(surf, BLUE, (300, 50), 20, 0) pygame.draw.ellipse(surf, RED, (300, 250, 40, 80), 1) pygame.draw.rect(surf, RED, (200, 150, 100, 50)) pixObj = pygame.PixelArray(surf) pixObj = BLACK pixObj = BLACK pixObj = BLACK pixObj = BLACK pixObj = BLACK del pixObj #game loop while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update()
Here is a short description of each function:
- fill(color). The
fill()method is a method of
pygame.Surfaceobjects. It will completely fill in the entire Surface object with whatever color value you pass as for the
- pygame.draw.polygon(surface, color, pointlist, width). This function draws a polygon with specified parameters
- pygame.draw.line(surface, color, startpoint, endpoint, width). This function draws a line between the
- pygame.draw.circle(surface, color, center_point, radius, width). This function draws a circle.
- pygame.draw.ellipse(surface, color, bounding_rectangle, width). This function draws an ellipse. In order to tell the function how large where to draw the ellipse, you must specify the bounding rectangle of the ellipse. A bounding rectangle is the smallest rectangle that can be drawn around a shape. The
bounding_rectangleparameter can be a
pygame.Rectobject or a tuple of four integers.
- pygame.draw.rect(surface, color, rectangle_tuple, width). This function draws a rectangle.
There isn't a single function you can call that will set a single pixel to a color (unless you call
pygame.draw.line() with the same start and endpoint). The Pygame library needs to run some code in the background before and after drawing to a Surface object. If it had to do this for every pixel you wanted to set, your program would run much slower.
Instead, you should create a
pygame.PixelArray object of a Surface object and then set individual pixels.
The PixelArray object that is returned from
pygame.PixelArray() can have individual pixels set by accessing them with two indexes. To tell Pygame that you are finished drawing individual pixels, delete the PixelArray object with a
del statement. Deleting the PixelArray object will unlock the Surface object so that you can once again draw images on it. If you forget to delete the PixelArray object, the next time you try to draw an image to the Surface, the program will raise an error.
The pygame.display.update() Function
After you are done calling the drawing functions to make the display Surface object look the way you want, you must call
pygame.display.update() to make the display Surface appear.
The one thing that you must remember is that
pygame.display.update() will only make the display Surface appear on the screen. If you want the images on other Surface objects to appear on the screen, you must copy them to the display Surface object with the
blit() method, which will be explained later.
Animated images are the result of drawing an image on the screen, then a split second later drawing a slightly different image.
import pygame, sys from pygame.locals import * pygame.init() FPS = 30 fpsClock = pygame.time.Clock() surf = pygame.display.set_mode((400, 300), 0, 32) pygame.display.set_caption('Animation') WHITE = (255, 255, 255) catImg = pygame.image.load(image.png) catx = 10 caty = 10 direction = 'right' while True: surf.fill(WHITE) if direction == 'right': catx += 5 if catx == 280: direction = 'down' elif direction == 'down' catx += 5 if caty == 220: direction = 'left' elif direction == 'left' catx -= 5 if catx == 10: direction = 'up' elif direction == 'up' catx -= 5 if caty == 10: direction = 'right' surf.blit(catImg, (catx, caty)) for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() pygame.display.update() fpsClock.tick(FPS)
FPS and pygame.time.Clock Objects
The frame rate or refresh rate is the number of pictures that the program draws per second, and it is measured in FPS (frames per second). A low frame rate in video games can make the game look choppy.
pygame.time.Clock object can help us make sure our program runs at a certain maximum FPS. A call to the
tick() method of a Clock object in the game loop can make sure the game runs at the same speed no matter how fast of a computer it runs on.
Drawing Images with pygame.image.load() and blit()
Pygame is able to load images onto Surface objects from PNG, JPG, GIF, and BMP image files. The
pygame.image.load() function call will return a Surface object from the display Surface object, so we must copy the image's Surface object to the display Surface object. Blitting is drawing the contents of one Surface onto another. It is done with the
blit() Surface object method.
Pygame provides some functions for fonts and creating text. The steps for making text appear on the screen:
- Create a
fontObj = pygame.font.Font('freesansbold.ttf', 32)
- Create a Surface object with the text drawn on it by calling the Font object's
GREEN = (0, 255, 0) BLUE = (0, 0, 128) textSurfaceObj = fontObj.render('Hello world!', True, GREEN, BLUE)
- Create a Rect object from the Surface object by calling the Surface object's
textRectObj = textSurfaceObj.get_rect()
This Rect object will have the width and height correctly set for the text that was rendered, but the top and left attributes will be zero.
- Set the position of the Rect object by changing one of its attributes.
- Blit the surface object with the text onto the Surface object returned by
pygame.display.update()to make the display Surface appear on the screen.
Anti-aliasing is a graphics technique for making text and shapes look less blocky by adding a bit of blur to their edges. To make Pygame's text use anti-aliasing, just pass
True for the second parameter of the
render() method. The
pygame.draw.aalines() functions have the same parameters as
pygame.draw.lines(), except they will draw anti-aliased lines instead of aliased lines.
You must create a
pygame.mixer.Sound object by calling the
pygame.mixer.Sound() constructor function. Pygame can load WAV, MP3, or OGG files. To play the sound, call the Sound object's
play() method. If you want to stop the Sound object from playing call the
soundObj = pygame.mixer.Sound('music.wav') soundObj.play() import time time.sleep(1) soundObj.stop()
Pygame can only load one music file to play in the background at a time. To load a background music file, call the
pygame.mixer.music.load() function. This file can be WAV, MP3, or MIDI format. To begin playing the loaded sound file as the background music, call the
pygame.mixer.music.play(-1, 0.0) function. The
-1 argument makes the background music forever loop when it reaches the end of the sound file. The
0.0 argument means to start playing the sound file from the beginning. To stop playing the background music, call the
pygame.mixer.music.load('backgroundmusic.mp3') pygame.mixer.music.play(-1, 0.0) pygame.mixer.music.stop()
This article covered the basics of Python's Pygame library for game development, like images, animations, and sounds.
Sign up now and apply for roles at companies that interest you.
Engineers who find a new job through WorksHub average a 15% increase in salary.Start with GitHubStart with TwitterStart with Stack OverflowStart with Email