Organizing the code

The simple example that we have just seen contains most of the basic ingredients required to write simple games. However, as everything is in one file with very little structure, it would become increasingly difficult to add features to our game. In this lesson, we will not learn anything significantly new about pygame. Rather, we will focus on reorganising the code so that it will be easier to extend it in future lessons.

Let's start by reviewing the program written so far. I have reproduced the code listing below with a few added comments.

  1 import sys
  2 import pygame
  3 
  4 pygame.init()
  5 
  6 # display initialisation
  7 size = width, height = 320, 200
  8 screen = pygame.display.set_mode(size)
  9 window_title = "My game"
 10 pygame.display.set_caption(window_title)
 11 
 12 # object/image initialisation
 13 smilie = pygame.image.load("smilie.bmp")
 14 smilie_rect = smilie.get_rect()
 15 
 16 # object/image speed
 17 speed = [2, 1]
 18 
 19 # background colour
 20 black = 0, 0, 0
 21 
 22 # image processing
 23 smilie = smilie.convert()
 24 colorkey = smilie.get_at((1,1))
 25 smilie.set_colorkey(colorkey, pygame.RLEACCEL)
 26 
 27 while True:
 28     # event processing
 29     for event in pygame.event.get():
 30        if event.type == pygame.QUIT:
 31            sys.exit()
 32            
 33     # move object
 34     smilie_rect.move_ip(speed)
 35     if smilie_rect.left + 10 < 0 or smilie_rect.right - 11 > width:
 36         speed[0] = -speed[0]
 37     if smilie_rect.top + 10 < 0 or smilie_rect.bottom - 12 > height:
 38         speed[1] = -speed[1]
 39     pygame.time.delay(10)
 40     
 41     # update image
 42     screen.fill(black)
 43     screen.blit(smilie, smilie_rect)
 44     pygame.display.flip()    

We note that there are a few places where we deal with processing the image (smilie). What happens if the file for the image does not exist? In this case, we expect python to raise an exception. What we should do is deal with this exception, perhaps like what follows.

try:
    image = pygame.image.load(filename)
except pygame.error, message:
    print 'Cannot load image:', filename
    raise SystemExit, message

With this approach, we leave open the possibility to deal with SystemExit exceptions at a later time.

Given that we will need to process more than one image in more realistic games, it makes sense to put together all the code dealing with images in one single function.

  1 def load_image(filename, colorkey_pixel = None):
  2     try:
  3         image = pygame.image.load(filename)
  4     except pygame.error, message:
  5         print 'Cannot load image:', filename
  6         raise SystemExit, message
  7     image = image.convert()
  8     if colorkey_pixel is not None:  # select transparent colour
  9         colorkey = image.get_at(colorkey_pixel)
 10         image.set_colorkey(colorkey, pygame.RLEACCEL)
 11     return image, image.get_rect()

The load_image() function will attempt to load an image from a file and select a transparent colour if a pixel coordinates is given as a second argument; if we do not want to set a transparent color, we simply have to leave the second argument blank. To process the single image we have been dealing with, we simply need to make the following function call:

smilie, smilie_rect = load_image("smilie.bmp", (1, 1))

We continue grouping together lines of code, defining many functions, to do the following:

  1. Initialise the display
  2. Initialise objects/images
  3. Handle user input
  4. Move objects
  5. Update display

The result is as follows:

  1 import sys
  2 import pygame
  3 
  4 def init_display():
  5     global width, height, screen
  6     size = width, height = 320, 200
  7     screen = pygame.display.set_mode(size)
  8     window_title = "My game"
  9     pygame.display.set_caption(window_title)
 10 
 11 def load_image(filename, colorkey_pixel = None):
 12     try:
 13         image = pygame.image.load(filename)    
 14     except pygame.error, message:
 15         print 'Cannot load image:', filename
 16         raise SystemExit, message
 17     image = image.convert()
 18     if colorkey_pixel is not None:  # select transparent colour
 19         colorkey = image.get_at(colorkey_pixel)
 20         image.set_colorkey(colorkey, pygame.RLEACCEL)
 21     return image, image.get_rect()
 22 
 23 def init_images():
 24     global speed, smilie, smilie_rect, background_colour
 25     smilie, smilie_rect = load_image("smilie.bmp", colorkey_pixel = (1, 1))
 26     speed = [2, 1]
 27     background_colour = 0, 0, 0
 28 
 29 def process_input():
 30     for event in pygame.event.get():
 31        if event.type == pygame.QUIT:
 32            sys.exit()
 33 
 34 def move_objects():
 35     global smilie_rect
 36     smilie_rect.move_ip(speed)
 37     if smilie_rect.left + 10 < 0 or smilie_rect.right - 11 > width:
 38         speed[0] = -speed[0]
 39     if smilie_rect.top + 10 < 0 or smilie_rect.bottom - 12 > height:
 40         speed[1] = -speed[1]
 41         
 42 def update_display():
 43     pygame.time.delay(10)
 44     screen.fill(background_colour)
 45     screen.blit(smilie, smilie_rect)
 46     pygame.display.flip()
 47 
 48 #-- end of definitions; begin program execution    
 49 
 50 pygame.init()
 51 init_display()
 52 init_images()
 53 
 54 while True:
 55     process_input()
 56     move_objects()
 57     update_display()
 58 

Note that while the code may look different, there has been no change in functionality as compared with the previous version as you can (and should!) verify by yourself. However, this new version should be easier to extend, as we will see in the next lesson. A drawback is the need to use global variables; later, when we introduce object-oriented technique, we will be able to eliminate many if not all of those global variables.

If you need it, a copy of this new version can be found here.

The next lesson can be found here.