Lab 4: Handling mouse events and collisions between objects

In this lab we'll focus on four new abilities:

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 lab4
Next, 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:

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

  1. establishes an initial facing for the star (e.g. 0 degrees)
  2. decides how much the star will rotate each turn (e.g. 4 degrees per turn)
  3. loads and stores the original copy of the image (e.g. starOriginal)
  4. puts a rotated version of the original in starImage
These four steps can be done with the following:
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: