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:
You start with the bird ("Faby") in a graphics window (300x500 or
400x600 are good sizes)
You control the bird by pressing the Up arrow
key. Note: the GraphWin class has a method
called checkKey that will tell you what the last key pressed
was.
If the bird doesn't fly, gravity will quickly end the game.
The game ends if the bird flies too high or too low and hits the
top or bottom of the graphics window, or hits one of the pipes.
The pipes move from right to left across the window.
Each set of pipes (top and bottom) has a gap in them for the bird
to fly through.
The gap size is always the same, but it's position changes
(up/down) randomly for each pipe.
At any time, only one set of pipes is moving across the
window.
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
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:
create a graphics window with a bird in it
create the first pipe object on the right side of the window (or
off the screen to the right)
create a score variable and initialize it to 0
enter a game loop that does the following, until the game ends:
checks for a key press
if user presses Up key, move bird up a bit (try windowHeight/20)
if user doesn't press Up key, bird drops due to gravity
(see below)
if bird has gone off the screen (top or bottom), then end game
if bird has hit a pipe, then end game
move pipe to the left
if pipe has gone off the left edge, update score and create new pipe
sleep for a brief time (such as 0.01 seconds) to allow
the user to see the animation
after the game ends, report the score
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).
Make messages display in the graphics window, such as the score,
and a large "GAME OVER" at the end.
Make the graphics window wider, and allow for more than one set
of pipes to be seen at once (like the real game...you can see what's
coming).
Add levels, making the game harder with faster pipes or smaller
gaps at higher levels.
Add a High Score leader-board. If the user achieves a high score,
ask for their name and add them to the leader-board.
Have the pipes moving up and down as they also move to the left
across the screen.
Submit
Once you are satisfied with your program, hand it in by typing
handin21 at the unix prompt.