CS100M Spring 2007
Project 5
Due Thursday, April 12 at 6 pm


Important! Turn off the file backup feature in DrJava—it causes problems on some system configurations! Go to menu item Edit, then to Preferences, choose the last category Miscellaneous, and uncheck the box that says "Keep Emacs-style Backup Files."

Academic Integrity: You may work on your own or with one partner. You may discuss background issues and general solution strategies with others, but the project you submit must be the work of just you (and your partner). If you work with a partner, you and your partner must register as a group in CMS and submit your work as a pair.


Objectives and background

In this project, you will learn how to use classes and objects, and how to develop and test code incrementally—one class (or even method) at a time. You will write the classes and methods to run a simplified version of a little game called BoomShine.

The objective of this game is to make as many of the bouncing balls explode as you can. The player gets to start one explosion and any ball that comes into contact with an exploding ball in turn explodes. In other words, the player chooses the location of the exploding ball in order to make a chain reaction last as long as possible. In our simplified game, there will be two balls, and the goal is to explode both of them.

Included with the assignment files is one possible complete implementation of the game that you can play with to get an idea of what the final product should be like. (See README.txt on how to run the example game.) Note that you are free to experiment a bit with your solution, and the result doesn't have to look exactly like the sample given, as long as it achieves the stated requirements.


Files and classes

This project will involve implementing the functionality of five classes: Point, Velocity, Ball, Walls, and BoomShine. Skeleton files are provided on the Projects page to get you started. In addition, we are providing the complete GameFrame class that takes care of all the graphics and window functionality of the game.

The skeleton files have comments before each class and method that specify what the methods are required to do. These are their specifications, and you should implement the functionality as specified. Do not change any of the given method headers or the given variable declarations. You can, however, implement additional private methods if you like.

Do not use arrays in this project. We'll use arrays in the next project to create a more interesting game!


Getting started

Basically, the game goes as follows. First there will be two balls (class Ball) moving at some velocity. They will bounce when they hit the wall, but they will pass over one another (the balls do not bounce off one another). They can explode, but they only do so when they hit another ball that is exploding. The explosion is a chain reaction initiated by a mouse click, which essentially creates another ball (ball3). ball3 does not move and it starts to explode once created. An explosion will eventually collapse and stop touching off other explosions. The following diagram illustrates the game.

Game illustration

First, let's determine the relationship between the classes. A ball can be represented as a point with a radius. The radius is a simple numeric value, but a point on the Cartesian plane has two numeric values, the x- and y-coordinates. We can "package" the x- and y-coordinates into one type, called the Point class. Look at the skeleton Point class now, you will see that it has two fields, x and y. Now look at the Ball class. It has several fields, including the center, which is a point so it is of type Point, and the radius (type double). Another property, or state, of a Ball is the velocity. A velocity vector in our 2-dimensional game has two components: x and y. Therefore, we have also created a Velocity class with the fields x and y. Did you notice the similarity (redundancy), between classes Point and Velocity? We'll work with the redundant code for now. Later, in Project 6, we will look at another design.

The Walls class defines the 2-dimensional space within which the Balls move. The Walls has the properties height and width, and in this class we will define how a Ball will bounce off the Walls.

The BoomShine class simulates the game. In the main method you will create the necessary Walls and Balls objects and implement the rules of the game.

So where do you start?? Choose the most independent class, one that doesn't depend on other classes! That would be class Point or Velocity. Below are more information on implementing the individual classes. Be sure to follow the specifications here and in the comments. Watch for the comments that start with the marker TODO. These comments describe what to do in (each section of) the methods.

Before you write or change any code, compile all six classes that you have downloaded. The given and skeleton classes should compile successfully (even though you cannot execute the game yet). As you implement the classes, compile and test your code often. Since we have given you code that compiles to begin with, we expect that your submitted project will at least compile, even if it doesn't execute flawlessly.


Details of the classes

The Velocity class (Velocity.java)

You can start by implementing the class Velocity, as it is the simplest. Velocity contains only data, so you only need to implement the constructor, getters, and setters. Test your code before you move on to another class! For example, you may want to type the following statements in DrJava's Interactions Pane to make sure that your Velocity class is correct:

Velocity v= new Velocity(3,1.6);
System.out.println(v.getX());  //expect to see 3  
v.setX(7.1);
System.out.println(v.getX());  //expect to see 7.1

You can write similar code to test the methods related to the y component.

The Point class (Point.java)

The Point contains mostly data, except the method distance which calculates the distance to another Point. You will need this method later when you check for collisions. Again, test your code after you implement the methods. Use similar test code as that shown above for testing the x and y components. To test the distance method, create two Points and calculate the distance between them:

Point p1= new Point(0,0);
Point p2= new Point(1,3);
System.out.println(p1.distance(p2));  //expect to see 3.16...

Always test your code before moving on to another class! It may be tempting to skip the testing, but that will burn more time in the end since you may end up with a large number of errors in multiple classes. Debugging one class at a time, with a small number of errors and dependencies, is far easier than debugging multiple classes with numerous confounding errors all at the same time.

The Ball class (Ball.java)

The center of a ball is of class Point. It absolutely determines the location of the ball. A ball has a radius, which should be changed when it explodes. A ball has a velocity (class Velocity), that can be changed by the walls (method checkBounce in Walls). You will need the velocity to calculate the location of the ball over time.

Implement the constructor, all the getters and setters specified in Ball.java, and two essential methods: update and checkCollision. Method update has the signature

public void update(double secsElapsed)

It will be passed the number of seconds (can be fractional) elapsed since the last call to this method. Take a look in class BoomShine.java for an example method call. The update to be done depends on whether the ball is exploding. If the ball is not exploding (based on the value in the field isExploding), then it is moving according to its current velocity, so you need to change its location over time by changing its center. Don't change the value of isExploding in this method, as it will be determined elsewhere. Similarly, whether the ball is at the walls will be checked in another method (in class Walls). If the ball is exploding, then it doesn't move and its radius will grow over time to a maximum before shrinking to zero. You have the freedom to decide how the explosion will look. For example, you can have the radius grow linearly with time or have certain acceleration as the ball grows. You can change its color and decide when to shrink. However, keep in mind that the explosion and eventual shrinkage should look gradual, i.e., they happen over a number of calls to update. Hint: the field explodingTime will be handy. Also, the explosion should not grow so big that it covers most of the game frame (the game would be too easy then!).

Testing: Create a Ball object and use simple print statements to show the field values. A few examples are shown below, but you should do additional tests!

//Create a Ball at (19,7) with radius 10, velocity 100 in the  
//  negative x direction, and color code 4:
Ball b1= new Ball(new Point(19,7), 10, new Velocity(-100,0), 4);
//Several example calls to the Ball's getter methods:
System.out.println(b1.getRadius());          //expect to see 10.0
System.out.println(b1.getCenter().getY());   //expect to see 7.0
System.out.println(b1.getVelocity().getX()); //expect to see -100.0

Notice in the last few statements that you can chain up your method calls. For example, b1 refers to a Ball, which has a getCenter() method. This getCenter() method returns something of type Point, which has a getY() method. This is why the entire expression to access the y-coordinate, which is in a Point, which is in a Ball, is written as b1.getCenter().getY().

Below is an example test of the update method:
//Update the ball's position after 0.1 seconds (it isn't exploding)
b1.update(0.1);
System.out.println(b1.getCenter().getX());  //expect to see 9.0
System.out.println(b1.getCenter().getY());  //expect to see 7.0

You get to decide on how the explosion works, so you should create some tests for that case as well.

At this point, not only can you compile the skeleton classes, you can run the program as well! (Run the main method in class BoomShine). We have provided enough example code in classes Walls.java and BoomShine.java to show a ball moving to the left, but the ball can only bounce back from the left wall and will move into the other walls. You will fix this later (in class Walls). Now close the graphics window and go back to programming!

Complete the checkCollision method to detect whether a ball is colliding with another. As stated in the specifications, do not change the state of either ball. You will need to use the method distance from class Point.

The Walls class (Walls.java)

The walls are actually a rectangle with a certain width and a certain height. Implement or modify the getters and setters as specified in order to work with walls of a different size. The method checkBounce in the skeleton only checks whether the ball hits the left wall. You need to modify this method to have the other walls bounce the ball back as well. Use the code for the left wall as an example.

Testing: Below are some tests for bouncing off the left wall. Test your code for the other walls as well! Below, we have created a ball that is just a little "in" the left wall. This can happen during the simulation since we update the position of a ball just once in an iteration—the positions over time are discrete values, not a continuous function. However, since each iteration takes only fractions of a second, the portion of the ball that gets in the wall is acceptably small and is generally not visible.

Walls walls= new Walls(800, 600);
Ball b2= new Ball(new Point(9.9,70), 10, new Velocity(-200,0), 4);
walls.checkBounce(b2);
System.out.println(b2.getCenter().getX());    //Expect to see 10.1
System.out.println(b2.getVelocity().getX());  //Expect to see 200.0

The BoomShine class (BoomShine.java)

Congratulations! You now have all the mechanisms to implement the game. All the careful testing that you have done up to this point should pay off by allowing you to concentrate on the logic of the simulation, not errors in the previous classes! The given skeleton only creates one ball moving to the left. You need to modify the code so that there are two balls, each starting from a random position, with a random radius, and moving at a random velocity. Use reasonable ranges for your random numbers. A suggested range for each velocity component is 150 to 400 (both positive and negative).

In the game loop (the infinite loop in the skeleton), you need to add code for all the actions that have to be taken during each iteration of the game. What do you see when the game starts? The two balls move around while the game appears to be waiting for the user to make one mouse click. The given skeleton code shows you how to use method update to move the ball in each iteration, but how does the "waiting" work? The program actually doesn't wait! At each iteration, it checks to see if a mouse click has been made. The given GameFrame class provides the method getClick to accept a mouse click and return the clicked location as a Point. If a user has not made a mouse click, method getClick returns the value null. Properly calling getClick and dealing with the returned value gives the appearance of the program "waiting" for a single mouse click. The skeleton creates the GameFrame object and calls it frame. The syntax for calling the getClick() method is therefore frame.getClick().

Your code needs to create a third ball (ball3) at the mouse click and set it to explode.

The other actions in the game loop include updating the position or radius of the balls (method update), checking for bounces against the walls, and checking for collisions. Recall that a ball that collides with an exploding ball will start to explode. If both ball1 and ball2 have exploded, send out a message using the setMessage method defined in class GameFrame to let the user know he or she has won. The syntax is frame.setMessage("your message").

The GameFrame class (GameFrame.java)

This is the class provided with the project. Do not modify it. Two methods defined in this class that you will call directly are getClick and setMessage. Method getClick reads the location of a mouse click and returns the location as a Point. It has the signature

public Point getClick()

Method setMessage displays a message in the graphics window and has the signature

public void setMessage(String message)

Submission instructions

Submit to CMS the .java files for your 5 classes: Point, Velocity, Walls, Ball, and BoomShine. Make sure that you submit your *.java files, and not your *.class files. Double check this!