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 lab4 cd lab4Next, copy across some files for this week's lab: star.gif, ship.gif, backdrop.gif, game3a.py, game3b.py.
Run the ls command to check that the files were successfully copied.
Finally, make the .py files executable:
chmod u+x *.py
Mouse events
The game3a.py file is essentially the game version
we created in lab 3 two weeks ago,
with a moving star whose speed and direction are controlled
through the arrow keys.
Open the file using gedit game3a.py &
Now we'll give the player the ability to click on the screen and have the star "jump" directly to the spot they clicked on.
This involves modifying the event-handling loop (the for loop) to add a test to see if a mouse button has been pushed down:
if event.type == pygame.MOUSEBUTTONDOWN: starBox.center = event.pos # put the star where the player clicked print "Event: mouse button press at (%d, %d)" % event.pos |
Try to identify where in the for loop the above code snippet should be added, then try running the revised program, i.e. ./game3a.py
If successful, whenever you click on the screen the star should jump to the chosen location.
When the star is reacting correctly to mouse clicks, close the editor and move on to the next exercise.
Handling multiple objects
File game3b.py contains a revised version of the program
to have two moving objects: the star and a ship.
At the moment, the player has no control over the ship, and the star only "jumps" in response to mouse clicks.
Open the game3b.py file in the editor, i.e.
gedit game3b.py &
Observe that several key additions have been made to allow us to draw and move the ship:
Run the game for awhile to ensure it behaves the way you expect, i.e.
./game3b.py
Now move on to the next stage, where we'll start the star spinning as it flies.
Image rotation
To create a rotated image, we take the image we originally
loaded and specify the direction it should be
facing as a number between 0 and 360 (like a compass heading).
The best way to do this is to use one image to hold the "original", and a new image to represent the rotated version we're actually going to display.
To make a spinning star, we need to replace the line
starImage = pygame.image.load("star.gif")
with something that
starFacing = 0 facingAdjustment = 4 starOriginal = pygame.image.load("star.gif") starImage = pygame.transform.rotate(starOriginal, starFacing)That creates the initial image, but now as the game progresses we want to adjust the facing every turn, and update the starImage with an appropriately rotated version.
To do so, we can add the following within the main while loop:
# calculate the new facing, in the range 0..355 starFacing = starFacing + facingAdjustment if starFacing > 355: starFacing = starFacing - 360 # rotate the original image to the specified facing starImage = pygame.transform.rotate(starOriginal, starFacing)
Run the game for awhile to ensure the star is spinning.
Now move on to the next stage, where we'll get the game to detect and react to collisions between the star and the ship.
Collision detection
Collision detection is handled using the rectangles (boxes)
around the star and the ship.
Fortunately, a built-in routine called colliderect is provided to check if one box overlaps another.
Within our while loop, we can use this to check for collisions between the star and ship, e.g.
# check for collisions between the star and the ship if (starBox.colliderect(shipBox)): print "THE STAR AND SHIP HAVE COLLIDED!"Note that the collision will be detected on every game loop while the two enclosing boxes overlap one another...
Run the game for awhile to ensure you understand how/when the game is detecting collisions.
Now let's revise things so that every time there is a collision a giant red X is painted across the screen and the game pauses for one second. To do so, we rewrite the colliderect section we just added:
# check for collisions between the star and the ship if (starBox.colliderect(shipBox)): red = 255, 0, 0 # the 3 numbers are the Red, Green, Blue values pygame.draw.line(display, red, (0, 0), (screenWidth, screenHeight), 5) pygame.draw.line(display, red, (0, screenHeight), (screenWidth, 0), 5) pygame.display.flip() pygame.time.delay(1000)Again, try out the revised program with ./game3b.py
Note the use of the draw line routine. To use this routine, give it
Finally, after each collision let's restore the ship and star to their original positions. The new collision check might look like:
# check for collisions between the star and the ship if (starBox.colliderect(shipBox)): red = 255, 0, 0 pygame.draw.line(display, red, (0, 0), (screenWidth, screenHeight), 5) pygame.draw.line(display, red, (0, screenHeight), (screenWidth, 0), 5) pygame.display.flip() pygame.time.delay(1000) starBox.x = screenWidth / 2 starBox.y = screenHeight / 2 shipBox.x = 16 shipBox.y = 16
Try out this final version with ./game3b.py
The complete final code should look something like this:
#! /usr/bin/python # import and initialize the pygame module import pygame pygame.init() # print an initialization message on the control console print "Starting game console" # set our desired screen width and height (in pixels) screenSize = screenWidth,screenHeight = 480,320 # open a display window of the specified size display = pygame.display.set_mode(screenSize) # load images for the display background and a moving star and ship background = pygame.image.load("backdrop.gif") starOriginal = pygame.image.load("star.gif") starFacing = 0 facingAdjustment = 4 starImage = pygame.transform.rotate(starOriginal, starFacing) shipImage = pygame.image.load("ship.gif") # set up boxes to track the positions of the star and ship starBox = starImage.get_rect() shipBox = shipImage.get_rect() # start the star out in the center of the screen # by setting its x,y coordinates starBox.x = screenWidth / 2 starBox.y = screenHeight / 2 # start the ship out in the upper left of the screen # by setting its x,y coordinates shipBox.x = 16 shipBox.y = 16 # establish an initial speed for the star and ship # speeds are listed using [horizontal, vertical] baseSpeed = 4 starSpeed = [baseSpeed,baseSpeed] shipSpeed = [baseSpeed,-baseSpeed] # establish the colour red red = 255, 0, 0 # the keepPlaying variable allows us to continue playing # as long as it is set to True keepPlaying = True # keep track of the total number of collisions numCollisions = 0 while keepPlaying: # start of the keepPlaying while loop # adjust the star's facing and rotate the image of the star starFacing = starFacing + facingAdjustment if starFacing > 355: starFacing = starFacing - 360 starImage = pygame.transform.rotate(starOriginal, starFacing) # adjust the position of the star and ship # based on their current speed starBox = starBox.move(starSpeed) shipBox = shipBox.move(shipSpeed) # if the star hits an edge have it "bounce" if starBox.left < 0 or starBox.right > screenWidth: starSpeed[0] = - starSpeed[0] if starBox.top < 0 or starBox.bottom > screenHeight: starSpeed[1] = - starSpeed[1] # if the ship hits an edge have it "wrap around" if shipBox.left > screenWidth: shipBox.right = 1 if shipBox.bottom < 0: shipBox.top = screenHeight - 1 # check for collisions between the star and the ship if (starBox.colliderect(shipBox)): numCollisions = numCollisions + 1 print "Collision ", numCollisions pygame.draw.line(display, red, (0, 0), (screenWidth, screenHeight), 5) pygame.draw.line(display, red, (0, screenHeight), (screenWidth, 0), 5) pygame.display.flip() pygame.time.delay(1000) starBox.x = screenWidth / 2 starBox.y = screenHeight / 2 shipBox.x = 16 shipBox.y = 16 # redraw the background picture to the screen buffer display.blit(background, (0,0)) # redraw the ship first then the star overtop of it display.blit(shipImage, shipBox) display.blit(starImage, starBox) # update the visible display from the screen buffer pygame.display.flip() # pause for 20 milliseconds before continuing pygame.time.delay(30) # check for any events that have taken place during # this turn (keypresses, mouseclicks, etc) for event in pygame.event.get(): # start of the events for loop # check to see if the player clicked the window close box if event.type == pygame.QUIT: keepPlaying = False print "Player command: close window" # if the player clicked on the screen # have the star "jump" to the new location if event.type == pygame.MOUSEBUTTONDOWN: starBox.center = event.pos # check to see if the player pressed any keys if event.type == pygame.KEYDOWN: # treat the Q and escape keys as quit commands if event.key == pygame.K_q: keepPlaying = False print "Player command: quit (q)" elif event.key == pygame.K_ESCAPE: keepPlaying = False print "Player command: quit (escape)" # end of the events for loop # end of the keepPlaying while loop # print a termination message on the control console print "shutting down the game" |