CS21 Lab 10: Classes

Due Monday, December 3, before midnight


As always, run update21 to create your cs21/labs/10 directory and create your programs for lab 10 in this directory. Since this is a graphics assignment, make use of the Zelle Graphics Documentation


Goals and Overview

So far in this course we have used many different types of objects (Circles, Points, lists, strings, etc), but we haven’t defined our own types. The goal of this lab is to practice writing your own classes (with constructors, instance variables, and methods).

In the file crossytoad.py, you will implement a game to guide a toad safely across lanes of traffic. If a car hits the toad, the game is over. If the toad makes it across to the grassy lane above, the player wins. The player can control the toad using the left/right/up/down keys on the keyboard. Cars should continuously wrap around to the other side of the screen.

To complete this assignment, you need to implement two classes: Car and Player. The game world consists of a series of lanes, where the first and last lanes are safe zones. The toad should start in the bottom-most lane. The players wins when the toad gets to the upper-most lane. Each lane should be 100 units high.

The core game logic should be inside main() and have the following structure:

Initialize the graphics window
Draw the backround
Create cars for each lane. To start, all cars can go in the same direction
Create the player

Game Loop:
  Update the player based on keyboard input
  Update the positions of the cars.  
    If a car overlaps with the player, the game is over
  Check if the player has reached the top lane. If yes, the game is over

Additionally, the game should support the following features:

Each of the sections below describes requirements and some suggestions for implementation.

Car Class

Your program should contain a Car class with following functions (implemented in Car.py). Each instance of Car represents a car that can drive across the screen and potentially collide with the toad.

constructor __init__

The constructor should initialize the graphics for the car. We have given you images to use for cars, which you can create using Zelle’s Image class. For example, to create a red car that drives from left to right, use “redCarR.png”. Then, to get its center point, width, and height, use the functions getAnchor(), getWidth(), getHeight()

img = Image(startPoint, "redCarR.png")
print(img.getAnchor()) # prints startPoint to the console
print(img.getWidth()) # prints the width of "redCarR.png"
print(img.getHeight()) # prints the height of "redCarR.png" 

Your constructor should take the following arguments in addition to self: starting point (Point) and the worldWidth (int) as arguments. The given point is needed to initialize your Image. The worldWidth will be needed for update (see below).

You should initialize and save a member variable that keeps track of the velocity that the car moves when updated.

draw

This method should take a parameter that represents the window, where window is of type GraphWin. This is similar to the draw method for all other graphics objects. Inside your draw method, you should draw the Image representing your car.

update

This method should move the car. If the car goes beyond the window, its position should map to the other side of the screen. Debugging tip: use the big fish from the aquarium assignment as a reference to help you animate the car.

getExtents

This method should return a list containing two points: the top left corner and the bottom right corner of the Image. These values will change as the car moves across the screen. These points will be used for testing whether the toad overlaps the car.

NOTE: Be careful! Extents need to be recomputed whenever then car moves!

# create two cars

onecar = Car(Point(width/2, height/2), width)
onecar.draw(win)

twocar = Car(Point(width/4, height/4), width)
twocar.draw(win)

Player Class

Your program should contain a Player class with following functions (implemented in Player.py). An instance of player represents the toad in the game and supports player controls and intersection testing with the cars.

constructor __init__

The constructor should initialize the graphics for the toad. We have given you an image for the toad (“toad.png”), which you can create using Zelle’s Image class.

Your constructor should take the following arguments in addition to self: starting point (Point) and the laneHeight (int) as arguments. The point is needed to initialize your Image. The laneHeight will be needed for moving the toad between lanes. (see below)

draw

This method should take a parameter representing the window, where window is of type GraphWin. This is similar to the draw method for all other graphics objects. Inside your draw method, you should draw the Image representing your toad

moveUp, moveDown, moveLeft, moveRight

These methods should move the car either up/down a lane or left and right.

intersects

This method should return True if the toad intersects a car and False otherwise. This method should take a list of two points, which correspond to the extents of the car. See below for hints and sample code showing how to check whether the toad is overlapping the car.

won

This method should return True if the toad is on the grassy lane at the top and False otherwise.


Here is some example test code to try in the main of the Car.py file:

# create the player

toad = Player(Point(width/2, height/2), width)
toad.draw(win)

Keyboard input

You will need to check whether the player presses a key to control the character. Getting keyboard input should be non-blocking. This means that we don’t want to wait for a keypress before continuing our loop.

Zelle gives us the call win.checkKey() to check for the input. This function returns None if no key is pressed. Otherwise, it returns a string:

Here is a template for using win.checkKey():

key = win.checkKey()
if key != None:
    # do something with the key

Checking for intersections

We will using bounding boxes to determine if two images overlap. For our images, the bounding box is centered at the anchor point and has matching width and height.

A bounding box has 4 points associated with it: top/left, top/right, bottom/left, and bottom/right. The top/left gives us the minimum values for x and y. The bottom/right gives us the maximum values for x and y.

Because the images do not rotate, the toad overlaps a car whenever one of its four corners is located inside the bounding box of the car.

If a point (x,y) is inside a bounding box, its x coordinate must be between the left and right bounding box points.

Similarly, if a point (x,y) is inside a bounding box, its y coordinate must be between the top and bottom bounding box points.

For a point to be in the bounding box both its x and y coordinates must be within the minimum/maximum bounds. Furthermore, the toad overlaps the car if any of its corners (x,y) is inside the car’s bounding box.

Below is sample code for checking whether the toad overlaps two cars

toad = Player(Point(0,0), 600)
toad.draw(win)

car1 = Car(Point(50,50), 100)
extents = car1.getExtents()
print("Car1 extents:", extents)
print("Toad intersects?", toad.intersects(extents))

car2 = Car(Point(300,300), 100)
extents = car2.getExtents()
print("Car2 extents:", extents)
print("Toad intersects?", toad.intersects(extents))

And here is sample output

Car1 extents: [Point(-61.0, 5.0), Point(161.0, 95.0)]
Toad intersects? True
Car2 extents: [Point(189.0, 255.0), Point(411.0, 345.0)]
Toad intersects? False

NOTE: Be careful! Extents need to be recomputed whenever then car moves!


Extensions

There are many ways to extend this assignment. A few suggestions are below, but feel free to come up with your own extension.

  1. Implement items which the player can collect. Item should be its own class and implement a getExtents() function similar to Car. Extend main to check for item/toad intersections

  2. Animate the car and toad when there’s a collision

  3. Randomize the car colors (load different images). Have opposing lanes of traffic

  4. Allow the player to replay. Make the game more difficult on each next level. The best way to implement this feature is with a Game class that contains a method for resetting the game components as well as an update method for updating the game elements and checking whether the player wins or loses.

  5. Personalize the game assets.

  6. Prevent the toad from moving beyond the extents of the screen

  7. Give the toad multiple lives.


Answer the Questionnaire

After you have turned in the full program, please edit the QUESTIONS-10.txt file in your cs21/labs/10 directory and answer the questions in that file.


Turning in your labs….

Don’t forget to run handin21 to turn in your lab files! You may run handin21 as many times as you want. Each time it will turn in any new work. We recommend running handin21 after you complete significant work on any part of the program.