First, as always, move into your games directory, create a directory for this week's lab, and move into that. E.g.
cd games mkdir lab7 cd lab7Next, copy across some files for this week's lab: backdrop.gif, ship.gif, star.gif.
Today we'll be creating our python files from scratch, e.g.
gedit lab7a.py &
Once the file has been created, you'll need to make it executable, e.g.
chmod u+x *.py
Pre-amble: clean program structure
#! /usr/bin/python import sys, pygame, math # ===================================================================== # GLOBAL VARIABLES # - list all variables that need to be globally accessible # define the size of the pygame display window scrSize = scrWidth, scrHeight = 640,480 gameScreen = None # the main display screen backImage = None # the loaded background image scrRefreshRate = 40 # the pause (in milliseconds) between updates # ===================================================================== # SETUP ROUTINE # - initializes the pygame display screen and background image def gameSetup(): global gameScreen, scrSize, backImage pygame.init() gameScreen = pygame.display.set_mode(scrSize) backImage = pygame.image.load('backdrop.gif') gameScreen.blit(backImage, (0,0)) # ===================================================================== # GAME OBJECTS # - define the different types of in game objects (e.g. ships, # characters, etc) # ===================================================================== # EVENT HANDLING ROUTINE # - processes any pending in game events, # returns True if the game should keep playing, # returns False if the game should quit def processEvents(): # process each pending event for event in pygame.event.get(): # if the user closed the window send False back # to tell the game to quit playing if event.type == pygame.QUIT: return False # if this point is reached then no quit event was # encountered, so return True to keep playing return True # ===================================================================== # MAIN GAME CONTROL ROUTINE # - sets up the game and runs the main game update loop # until instructed to quit def main(): # identify any global variables the main routine needs to access global gameScreen, backImage, scrRefreshRate # initialize pygame and the game's display screen gameSetup() # run the main game loop keepPlaying = True while keepPlaying: # handle any pending events keepPlaying = processEvents() # switch to the new display image pygame.display.flip() # pause before initiating the next loop cycle pygame.time.delay(scrRefreshRate) # the main game loop has completed, so exit the game sys.exit() # ===================================================================== # INITIATE THE GAME # - calls the main() routine if __name__ == "__main__": main() |
Sprites for in-game objects
Part of our previous lab looked
at reorganizing our game code to use Python classes to hold information
about the various types of objects in our game.
There is also a special PyGame class called Sprites that have a variety of builtin properties and routines that we will look at today.
We will create a GameObject class, based on the PyGame Sprites class, that allows us to do some cleanly automated updating of objects.
Our game object class will have two routines (or methods) associated with it:
# ===================================================================== # GAME OBJECT # - controls the basic movable in-game objects (ships in this case) class GameObject(pygame.sprite.Sprite): # the constructor (initialization routine) for the # movable game objects def __init__(self, image, x, y, direction, speed): # initialize a pygame sprite for the object pygame.sprite.Sprite.__init__(self) # load the image for the object self.origImage = pygame.image.load(image) self.image = self.origImage # set up the initial position for the object self.position = self.x, self.y = x, y # set up the initial direction for the object self.facingDir = direction # set up the initial velocity of the object self.speed = speed # the update routine adjusts the object's current position and # image based on its speed and direction def update(self): # update the object's position self.x = self.x + self.speed[0] self.y = self.y + self.speed[1] self.position = self.x, self.y # update the object's image self.image = pygame.transform.rotate(self.origImage, self.facingDir) # center the object's image in its new position self.rect = self.image.get_rect() self.rect.center = self.position |
Updating our in-game objects
The class definition above defines how a "GameObject" works,
but we still need to add code to our main routine to
create the actual game objects we want to see, and to
provide specific initial values for their speed, facing, position,
etc.
We'll revise our main routine to:
# ===================================================================== # MAIN GAME CONTROL ROUTINE # - sets up the game and runs the main game update loop # until instructed to quit def main(): # identify any global variables the main routine needs to access global gameScreen, backImage, scrRefreshRate # initialize pygame and the game's display screen gameSetup() # create an array of objects to add to the display, # giving each of them # an image, xcoord, ycoord, facing, and speed gameObjList = [ GameObject('ship.gif', 80, 60, 270, [1,1]), GameObject('ship.gif', 560, 60, 180, [-1,1]), GameObject('ship.gif', 560, 420, 90, [-1,-1]) ] # create a group for the game objects objGroup = pygame.sprite.RenderUpdates(*gameObjList) # run the main game loop keepPlaying = True while keepPlaying: # handle any pending events keepPlaying = processEvents() # update the display of the object groups objGroup.clear(gameScreen, backImage) # run the updates on each object in the group objGroup.update() # switch to the new display image pygame.display.flip() # pause before initiating the next loop cycle pygame.time.delay(scrRefreshRate) # the main game loop has completed, so exit the game sys.exit() |
Updating the display
We're getting closer, but the game display hasn't been told
how/when to redraw the portions of the screen that need to change
based on the movement of our game objects.
This involves two steps:
# ===================================================================== # MAIN GAME CONTROL ROUTINE # - sets up the game and runs the main game update loop # until instructed to quit def main(): # identify any global variables the main routine needs to access global gameScreen, backImage, scrRefreshRate # initialize pygame and the game's display screen gameSetup() # create the list of screen update sections (rectangles) updateSections = None # create an array of objects to add to the display, # giving each of them # an image, xcoord, ycoord, facing, and speed gameObjList = [ GameObject('ship.gif', 80, 60, 270, [1,1]), GameObject('ship.gif', 560, 60, 180, [-1,1]), GameObject('ship.gif', 560, 420, 90, [-1,-1]) ] # create a group for the game objects objGroup = pygame.sprite.RenderUpdates(*gameObjList) # run the main game loop keepPlaying = True while keepPlaying: # handle any pending events keepPlaying = processEvents() # update the display of the object groups objGroup.clear(gameScreen, backImage) # run the updates on each object in the group objGroup.update() # update the display rectangles updateSections = objGroup.draw(gameScreen) # update the buffered display pygame.display.update(updateSections) # switch to the new display image pygame.display.flip() # pause before initiating the next loop cycle pygame.time.delay(scrRefreshRate) # the main game loop has completed, so exit the game sys.exit() |
Complete version
Hopefully we can now see three ships moving towards the center
of the screen. (A copy of the complete code is included below.)
#! /usr/bin/python import sys, pygame, math # ===================================================================== # GLOBAL VARIABLES # - list all variables that need to be globally accessible # define the size of the pygame display window scrSize = scrWidth, scrHeight = 640,480 gameScreen = None # the main display screen backImage = None # the loaded background image scrRefreshRate = 40 # the pause (in milliseconds) between updates # ===================================================================== # SETUP ROUTINE # - initializes the pygame display screen and background image def gameSetup(): global gameScreen, scrSize, backImage pygame.init() gameScreen = pygame.display.set_mode(scrSize) backImage = pygame.image.load('backdrop.gif') gameScreen.blit(backImage, (0,0)) # ===================================================================== # GAME OBJECT # - controls the basic movable in-game objects (ships in this case) class GameObject(pygame.sprite.Sprite): # the constructor (initialization routine) for the # movable game objects def __init__(self, image, x, y, direction, speed): # initialize a pygame sprite for the object pygame.sprite.Sprite.__init__(self) # load the image for the object self.origImage = pygame.image.load(image) self.image = self.origImage # set up the initial position for the object self.position = self.x, self.y = x, y # set up the direction for the object self.facingDir = direction # set up the initial velocity of the object self.speed = speed # the update routine adjusts the object's current position and # image based on its speed and direction def update(self): # update the object's position self.x = self.x + self.speed[0] self.y = self.y + self.speed[1] self.position = self.x, self.y # update the object's image self.image = pygame.transform.rotate(self.origImage, self.facingDir) # center the object's image in its new position self.rect = self.image.get_rect() self.rect.center = self.position # ===================================================================== # EVENT HANDLING ROUTINE # - processes any pending in game events, # returns True if the game should keep playing, # returns False if the game should quit def processEvents(): # process each pending event for event in pygame.event.get(): # if the user closed the window send False back # to tell the game to quit playing if event.type == pygame.QUIT: return False # if this point is reached then no quit event was # encountered, so return True to keep playing return True # ===================================================================== # MAIN GAME CONTROL ROUTINE # - sets up the game and runs the main game update loop # until instructed to quit def main(): # identify any global variables the main routine needs to access global gameScreen, backImage, scrRefreshRate # initialize pygame and the game's display screen gameSetup() # create the list of screen update sections (rectangles) updateSections = None # create an array of objects to add to the display, # giving each of them # an image, xcoord, ycoord, facing, and speed gameObjList = [ GameObject('ship.gif', 80, 60, 270, [1,1]), GameObject('ship.gif', 560, 60, 180, [-1,1]), GameObject('ship.gif', 560, 420, 90, [-1,-1]) ] # create a group for the game objects objGroup = pygame.sprite.RenderUpdates(*gameObjList) # run the main game loop keepPlaying = True while keepPlaying: # handle any pending events keepPlaying = processEvents() # update the display of the object groups objGroup.clear(gameScreen, backImage) # run the updates on each object in the group objGroup.update() # update the display rectangles updateSections = objGroup.draw(gameScreen) # update the buffered display pygame.display.update(updateSections) # switch to the new display image pygame.display.flip() # pause before initiating the next loop cycle pygame.time.delay(scrRefreshRate) # the main game loop has completed, so exit the game sys.exit() # ===================================================================== # INITIATE THE GAME # - calls the main() routine if __name__ == "__main__": main() |