CS21 Lab 11: Defining Classes

Due before midnight, Saturday April 23

This lab assignment requires you to write a program that uses multiple classes. As always, first run update21 to create the cs21/labs/11 directory.

flappy bird

This week's lab is to create a simple version of the oddly-addictive flappy bird game.


High-level Specifications:

To write this program, you will create two classes: a Pipe class (the obstacles the bird flies through) and a Bird class, as well as the main program that handles the graphics loop and game logic (when the game ends, scoring, etc). We will again use the Zelle graphics library. Here are the rules for the game:

Breaking things up into classes

This game may look hard at a first glance, but by breaking the program up into classes, and testing them incrementally, it will be relatively straight forward to implement.

Step 1. the Pipe class

Write and test the Pipe class that has the methods shown below. We suggest you test each method as you write them. For example, write the constructor (__init__), then write some test code to make a graphics window and create a Pipe() object. Make sure it's size and position look correct. Then add the move() method, and add more test code to move the pipe across the screen. And so on.

def __init__(self, win):
  • parameter: one GraphWin() object
  • creates and draws two Rectangle objects (with a randomly-placed gap)
  • stores both Rectangle objects in a list

def move(self, dx)
  • parameter: how far to move the pipes in the X direction
  • moves the pipe (both rectangles in the list) by dx in the X direction
Note: this move() method will call the Zelle graphics move() method to move each Rectangle object. It's OK that it has the same name (i.e., both are called move())

def offScreen(self)
  • has no parameters (except for self!)
  • returns True if the right-edge of the pipe has gone off the left-side of the window, False if not

pipe sides image

def getLeftX(self)
def getRightX(self)
def getTopY(self)
def getBottomY(self)
  • have no parameters (except for self!)
  • return the X or Y coordinate of a pipe side
These are needed to help determine if the bird has collided with the pipe. For the X coordinates, it doesn't matter which rectangle of the pipe (top or bottom) you use. For the Y coordinates, getTopY should return the Y coordinate of the bottom surface of the top rectangle, while getBottomY should return the Y coordinate of the top surface of the bottom rectangle.

Here's a sample test video of the pipe class. We create a pipe object to the right of the window, repeatedly move it to the left, and when it is off the screen to the left, create a new pipe and repeat the process.




Step 2. the Bird class

Write and test the Bird class that has the methods shown below. This class will create the bird object (a Circle graphics object for the body, plus at least a beak and an eye), and the methods needed to move the bird and decide if it has hit the pipes or the edge of the window.

def __init__(self, win, size)
  • parameters: the graphics window and the size of the bird
  • create and draw each part (body, eye, beak)
  • store all bird parts (graphics objects) in a list
The design of the bird (color, size of eye, beak, etc) is up to you, but the body of the bird should be a Circle object, to make it easier to determine if it collides with anything.

def move(self, dy)
  • parameter: how far to move the bird in the Y direction
  • moves all bird parts (graphics objects in the list) by dy in the Y direction
This is a good place to stop and test your bird object. Write some test code to create a graphics window, a bird object, and then add code to move the bird up a certain amount each time the Up arrow key is pressed. Remember to use the checkKey method of the GraphWin class.

def onScreen(self)
  • no parameters (except for self)
  • returns True as long as bird is totally on screen
  • returns False if bird goes off the top or bottom of the screen
Again, test your code to make sure this works!

def touching(self, pipeobj)
  • parameter: pipe object
  • returns True if bird collides with either rectangle in the pipe object, otherwise returns False
Note: this function will use the pipe object methods: getLeftX(), getRightX(), getTopY(), and getBottomY(). Hint: make a drawing of the bird (a circle) and the pipes (two rectangles with a gap), and figure out the conditions that have to be True if they are intersecting.

Again, test your code: add a pipe object to your bird test code and make sure it detects any collisions with the pipes.



Step 3. write main

Your main function should use the bird and pipe classes, and take care of the following:

Note: to allow for gravity, create a variable amount dy for the bird to move down each time, and set this initially to zero. Then, each time through the game loop, increase dy by an amount g which is set to a reasonable value that mimics the bird falling due to gravity (such as g=0.05). Finally, whenever the user hits Up key, reset dy back to zero.

Here is a sample of the finished game: Note that in this video the score is displayed in the graphics window. You can simply print it. Making it display on the window is one of the optional enhancements you may want to try.




Enhancements (optional)

Once you are done with the basic program, feel free to add enhancements to it (just for fun...no extra credit).


Submit

Once you are satisfied with your program, hand it in by typing handin21 at the unix prompt.