T-Th 9:05
or
T-Th 11:15
in Phillips 101

CS 1110: Introduction to Computing Using Java

Spring 2012

Breakout

Due to CMS by Saturday, May 5th

This assignment, including much of the wording of this document, is taken from an assignment from Stanford University, by Professor Eric Roberts. Your task is to write the classic arcade game Breakout. You can download our solution as a jar-file application and get a real taste for the game.

Please keep track of the time you spent on this assignment. We will ask for it when it is time to submit.

This assignment is easily within your grasp. You just have to start early, break the problem up into manageable pieces, and program/test incrementally. Below, we discuss stages of implementation and give suggestions for staying on top of the project. If you follow our advice and test each piece thoroughly before proceeding to the next, you should be successful. Our solution is ~335 lines long and has 11-12 methods.


Table of Contents


Before You Get Started

Academic Integrity

This assignment is similar to one given in previous offerings of CS1110. It is a violation of the Code of Academic Integrity for you to be in possession of or to look at code (in any format) for this assignment of anyone in this class or in previous classes of CS 1110. You should also not show another student your program (this includes publicly posting your code on Piazza). Please do not violate the Code; penalties for this will be severe. We will try hard to give you all the help you need for this assignment.

As with the last assignment, it is highly unlikely that your code for this assignment will look exactly like someone else's. Once gain, we will actively be using Moss to check for instances of cheating and plagiarism. Anyone caught using files that are obviously from a previous semester will be prosecuted, with the end result perhaps being to fail the course.


Collaboration Policy

You may do this assignment with one other person. If you are going to work together, then form your group on CMS as soon as possible. If you do this assignment with another person, you must work together. It is against the rules for one person to do some programming on this assignment without the other person sitting nearby and helping.

With the exception of your CMS-registered partner, you may not look at anyone else's code or show your code to anyone else, in any form what-so-ever.


Necessary Files

The first thing to do in this assignment is to download the zip file A7.zip from this link. You should unzip it and put the contents in a new directory. This zip file contains the following:

Breakout.java
This is the source code that you will modify, and the only file that you submit for a grade.
Breakout.jar
This is a double-clickable Jar file with a solution. It gives you something to compare against.
acm.jar
This is the jar file from Assignment A5.
docs
These are the specifications of all the classes in package acm.
sound files
These are some sample sound files should you wish to add sound effects as part of your extensions.

The only source code that you have access to in this assignment is the calass Breakout. A partial skeleton of this class appears to the right. The class includes:

Import statements for the various packages
Recall from class that a GUI application needs to access a lot of packages to function properly; this class imports them all.
Constants that control the game parameters
These are mnemonic constants for things like the height of the game paddle. Your code should use these constants internally so that changing them in your file changes the behavior of your program accordingly.
A static main method
This is a method that starts the application pro- gram, fixes bricks, and sets the window to the appropriate size. Run the program by clicking button Run or by executing this in the interactions pane:
run Breakout

You will notice that by cramming everything into the Breakout class, this application is in clear violation of the model-view-controller pattern. We chose to do it this way for two reasons. First, our hands are somewhat tied by the ACM Graphics package which is primarily designed for beginners, and so trades poor design for simplified presentation. Second, this assignment has always been distributed this way, so we did not want to introduce a lot of bugs by rewriting the program.

You should use the instructions from Assignment A5 to add the JAR file to DrJava. As before, if you need more detailed specifications than the ones we give, they are available from the official site.

Class Breakout is a subclass of class GraphicsProgram, which is associated with the window on which the graphics takes place. Method main creates an instance of the class and then calls method start of the instance, which is inherited from GraphicsProgram.


Assignment Scope

GraphicsProgram also declares procedure run, which is overridden in class Breakout. Execution of start(sizeArgs) constructs the basic GUI window and then calls procedure run. You have to write the body of run (and any other methods you need) to initialize the GUI with bricks, paddle, and ball and play the game. You also have to write class Brick.

An important part of your programming will be to develop new methods whenever you need them, in order to keep each method small and manageable. Your grade will depend partly on the design of your program. As one guideline, points will be deducted for methods that are more than 50 lines long (including the specification).

You must provide a class invariant (for the fields). You must specify each method precisely and thoroughly. You need not write loop invariants, although doing so will help you. If you show your code to an instructor, TA, or consultant and they see a method that is not specified, they will ask you to go away, fix it, and come back at another time.


Pacing Yourself

You should start as soon as possible. If you wait until the day before this assignment is due, you will have a hard time completing it. If you do one part of it every 2-3 days, you will enjoy it and get it done on time. The hard part may be "finishing up": designing the final reorganization in order to incorporate three balls in a game.

Implement the program in stages, as described in this handout. Do not try to get everything working all at once. Make sure that each stage is working before moving on to the next stage.

Set up a schedule. We have suggested some milestones, but make up your own schedule. Leave time for learning things and asking questions. You may have to learn some points about package acm.graphics by yourself.

Finally, do not try to extend the program until you get the basic functionality working. If you add extensions too early, debugging may get very difficult.


Getting Help

We have tried to give you as much guidance in this document as we can. However, if your are still lost, please see someone immediately. Like the last assignment, this is a fairly involved project, and you should get started early. To get help, you may talk to the course instructor, a TA, or a consultant. See the staff page for more information.

In addition, you should always check Piazza for student questions as the assignment progresses. We may also periodically post announcements regard this assignment on the course website.


Breakout

The initial configuration of the game Breakout is shown in the left-most picture below. The colored rectangles in the top part of the screen are bricks, and the slightly larger rectangle at the bottom is the paddle. The paddle is in a fixed position in the vertical dimension; it moves back and forth horizontally across the screen along with the mouse — unless the mouse goes past the edge of the window.


Starting Position
       
Hitting a Brick

A complete game consists of three turns. On each turn, a ball is launched from the center of the window toward the bottom of the screen at a random angle. The ball bounces off the paddle and the walls of the world, in accordance with the physical principle generally expressed as "the angle of incidence equals the angle of reflection" (it is easy to implement). The start of a possible trajectory, bouncing off the paddle and then off the right wall, is shown to the right. The dotted line is there only to show the ball's path and will no actually appear on the screen.

In the second diagram above, the ball is about to collide with a brick on the bottom row. When that happens, the ball bounces just as it does on any other collision, but the brick disappears. The left-most diagram below shows the game after that collision and after the player has moved the paddle to put it in line with the oncoming ball.


Intercepting the Ball
       
Breaking Out

The play on a turn continues in this way until one of two conditions occurs:

  1. The ball hits the lower wall, which means that the player missed it with the paddle. In this case, the turn ends. If the player has a turn left, the next ball is served; otherwise, the game ends in a loss for the player.
  2. The last brick is eliminated. In this case the player wins, and the game ends.

Clearing all the bricks in a particular column opens a path to the top wall. When this delightful situation occurs, the ball may bounce back and forth several times between the top wall and the upper line of bricks without the user having to worry about hitting the ball with the paddle. This condition, a reward for "breaking out", gives meaning to the name of the game. The last diagram above shows the situation shortly after the first ball has broken through the wall. The ball goes on to clear several more bricks before it comes back down the open channel.

Breaking out is an exciting part of the game, but you do not have to do anything in your program to make it happen. The game is operating by the same rules it always applies: bouncing off walls, clearing bricks, and obeying the "laws of physics".


The Basic Game

We have divided these instructions into two parts. The first part covers the basic things that you must implement just to get the game running. Once you do that, the assignment gets more interesting. You should try to finish everything in this part of the assignment by Wednesday, May 2 (during the last week of class). If you do that, you will be in good shape to finish the rest of the assignment.


Set up the Bricks

The first step is to write the code that puts the various pieces on the playing board. Thus, it probably makes sense to implement procedure run (which drives the game) as two method calls: one that sets up the game and one that plays it. An important part of the setup consists of creating rows of bricks at the top of the game, which are shown to the right.

The number, dimensions, and spacing of the bricks, as well as the distance from the top of the window to the first line of bricks, are specified using named constants given in class Breakout. The only value you need to compute is the x coordinate of the first column, which should be chosen so that the bricks are centered in the window, with the leftover space divided equally on the left and right sides (Hint: the leftmost brick should be placed at x-coordinate BRICK_SEP_H/2). The colors of the bricks remain constant for two rows and run in the following sequence: RED, ORANGE, YELLOW, GREEN, CYAN. If there are more than 10 rows, your start over with RED, and do the sequence again.

All objects placed on the playing board are instances of subclasses of abstract class GObject. The bricks and paddle are objects of subclass GRect. You should complete subclass Brick of GRect (given at the end of Breakout.java) and use objects of class Brick for the bricks.

Class Brick has no fields and needs only two methods:

  • a constructor for a new brick of a given width and height and
  • a constructor for a new brick at a point (x,y) and of a given width and height.
Class GRect has similar constructors, if you wish to compare. Class Brick can stay inside Breakout.java, you do not need to have it in a separate file (notice the lack of public before class).

To add a GRect object r to the playing board, call inherited procedure add(r) inside the class Breakout. To begin, you might want to create a single Brick object of some position and size and add it to the playing board, just to see what happens. Then think about how you can place the 8 rows of bricks.

You need to fill a Brick (which is a GRect) with a particular color; you should also to set its color so that its outline is the same color (instead of black). Look for GRect methods to do this. In order to allow the object to be filled with a color, you need to use a procedure call like rect.setFilled(true);.

Important Considerations

Do not use an array or Vector to keep track of the bricks. Just add the bricks to the playing board as they are created. Do not be concerned about keeping track of them. Later, you will see why you do not need to keep them in an array or Vector.

In addition, make sure your creation of the rows of bricks works with any number of rows and any number bricks in each row (e.g. 1, 2, ..., 10, and perhaps more).

Try to finish this part by April 22. All you need to do is to produce the brick diagram shown above. This will give you considerable confidence that you can get the rest done.


Write Method fixBricks

While you are working with the program, you should play with just 3-4 bricks er row and 1-2 r ows of bricks. This will save time and let you more quickly see whether the program works correctly when the ball breaks out (gets to the top of the canvas). It will also allow you to test when someone wins or loses.

Unforunately, this would require changing the values of the static constants that give the number of rows and number of bricks in a row. This is undesirable (you might forget to change them back) Instead of that, we can use the fact that this is an application and give the application some values when it starts out. Use the following command in the interactions pane.

java Breakout 3 2
This is equivalent to writing Breakout.main(new String[]{"3","2"}; That is, it calls method main with the given String array as argument.

Look at method Breakout.main. Its first statement calls fixBricks. Your task is to write the body of fixBricks, following its specification (and the hint given as a comment) carefully. You will then be able to start the program as shown above and have just 3 bricks per row and 2 rows.

Complete this part by April 24.


Create the Paddle

Next you need to create the black paddle. You will need to reference the paddle often, so declare it as a private field of type GRect. A public static variable of class Breakout gives its vertical position relative to the bottom of the window.

Making the paddle move in response to mouse movements means that something has to be registered as a "listener" to the mouse. We talked about how to do this in general in lecture, but the ACM classes handle this a bit differently (sigh). Read the ACM documentation to find out how to make the "appropriate something" in the ACM-package framework listen to the mouse. The whole window associated with the GraphicsProgram should listen to mouse movement, not the paddle.

Once you have done that, here iss how to make the paddle track the mouse (horizontally only). Declare a procedure like this:

   /** Move the horizontal middle of the paddle to the 
    *  x-coordinate of the mouse position, but keep the 
    *  paddle completely on the board. */
   public void mouseMoved(MouseEvent e) { 
      GPoint p= new GPoint(e.getPoint());
      ... 
   }
Thus you are overriding inherited procedure mouseMoved. This procedure is called whenever the mouse is moved. Parameter e has a function getPoint, and the assignment statement we give you in the body stores in p the coordinates of the point where the mouse currently is. Replace the three dots above by code that changes the x-coordinate of the paddle as indicated in the specification, using the x-coordinate of point p.

Be careful that the paddle stays completely on the board even if the mouse moves off the board. Our code for this is 3 lines long; it uses function Math.min and Math.max.

Complete this part by April 26.


Create a Ball and Make it Bounce

You are now past the "setup" phase and into the "play" phase of the game. In this phase, a ball is created and moves and bounces appropriately. A ball is just a filled GOval. To start, create a ball and put it in the center of the window. Again, you probably want a private field ball of type GOval to contain the ball, since you will have to refer to it often. Keep in mind that the coordinates of the GOval specify the upper left corner and not the center of the ball.

The play phase of the game should contain a loop, each iteration of which does the following:

  • moves the ball a bit
  • changes direction if it hits a wall
  • pauses for 10 milliseconds, using pause(10);
The program needs to keep track of the velocity of the ball, which consists of its horizontal (x) component and its vertical (y) component. You should declare them as fields like this:
   private double vx, vy; // give their meanings in a comment!
These velocity components represent the change in position that occurs on each time step (each loop iteration).

Initially, the ball should head downward, so you should use a starting velocity of +3.0 for vy. The game would be boring if every ball took the same course, so you should choose component vx randomly. You can read about random numbers in the text, but for now simply do the following:

  1. Declare a field rgen, which will serve as a random-number generator:
       private RandomGenerator rgen= new RandomGenerator();
    
  2. Initialize variable vx as follows:
       vx = rgen.nextDouble(1.0, 3.0);
       if (!rgen.nextBoolean(0.5)) { vx = -vx };
    
This code sets vx to be a random double in the range 1.0 to 3.0 and then makes it negative half the time.

Your next challenge is to get the ball to bounce off the walls of the playing board, ignoring entirely the paddle and the bricks. Once you have moved the ball one step — by (vx,vy) — do the following: Suppose the ball is going up. Then, if any part of the ball has a y-coordinate less than or equal to 0, the ball has reached the top and its direction has to be changed so that it goes down. So this by setting vy to -vy. Check the other three sides in the same fashion. When you have finished this, the ball will bounce around the playing board forever — until you stop it.

You have to figure out whether the ball has reached (or gone over) the top. Remember that the location of a GOval is the top-left corner of its bounding box.

Complete this part by April 30.


Check for Collisions

Now comes the interesting part. In order to make Breakout into a real game, you have to be able to tell when the ball collides with another object in the window. As scientists often do, we make a simplifying assumption and then relax the assumption later. Suppose the ball were a single point (x,y) rather than a circle. The call

   getElementAt(x,y)
of inherited function getElementAt does the follow:
  • return null if no graphical object covers point (x,y)
  • return (the name on the tab of) the GObject that covers point (x,y), if some GObject actually covers the point.
If several GObjects cover the point, the one that appears to be in front on the display is returned.

Suppose getElementAt returns (the name on the tab of) a GObject gob. If gob == paddle, you know the paddle has collided with the single-point ball at (x,y). If gob is an instance of Brick, then gob must be a brick. So, we have just explained how to test whether a single-point ball has collided with the paddle or a brick.

However, the ball is not a single point. It occupies physical area, so it may collide with something on the screen even though its center does not. The easiest thing to do — which is typical of the simplifying assumptions made in real computer games is to check a few carefully chosen points on the outside of the ball and see whether any of those points has collided with anything. As soon as you find something at one of those points (other than the ball of course) you can declare that the ball has collided with that object.

One of the easiest ways to come up with these "carefully chosen points" is to treat everything in the game as rectangles. A GOval is defined in terms of is bounding rectangle (e.g. the rectangle in which it is inscribed). Therefore the upper left corner of the ball is at the point (x,y) and the other corners are at the locations shown in the diagram to the right (r is the radius of the ball). These points have the advantage of being outside the ball, so that getElementAt cannot return the ball itself. But they are close enough to make it appear that a collision has occurred.

Write a function

   GObject getCollidingObject()
that checks the four corners, one at a time. If one of them collides with the paddle or a brick, stop the checking immediately and return the object involved in the collision. Return null if no collision occurred.

Next you need to modify the main game loop, discussed above. After moving the ball, call getCollidingObject to check for a collision. If the ball going up collides with the paddle, do nt do anything. If the ball going down collides with the paddle, negate the vertical direction of the ball. If the ball (going in either vertical direction) collides with a brick, remove the brick from the board and negate the vertical direction.

The inherited function remove(gob) can be used to remove object gob (such as a brick). You can check whether the object is a brick by asking whether it is an instance of class Brick.

Try to finish this part by May 2.


Expanding the Game

If you have followed our suggested timeline, you now have three days to take care of some minor additional details. You are also welcome to extend the game and try to make it more fun. In doing this, you might find yourself reorganizing run. That is okay. However, we highly suggest that you save a copy of the basic game in a separate folder before you start to make major changes. That way you have something to revert to if you really screw up the program.


Mandatory Details

While many of the extensions to the game are optional, the ones below are not. They must be completed for you to get full credit on this assignment.

Player lives

You need to care of the case that the ball hits the bottom wall. Right now, the ball just bounces off this wall like all the others, but hitting the bottom means that the ball is gone. In a game, the player should get three balls before losing. If the player can have another ball, put a message on the board somewhere (use a GLabel object), telling the player that another ball is coming in 3 seconds, pause for 3 seconds, remove the message, and continue with a new ball.

Win condition

You need to check for hitting the last brick, in which case the player wins. An easy way to do this is to keep track of how many bricks are left on the board. When there are none, the game ends and the player has won.

Ending message

When a game ends, you should place a message somewhere on the window. Use a GLabel object to do this.

Tune the game

Once you have finished everything else, experiment with the settings that control the speed of your program. How long should you pause in the loop that updates the ball? Do you need to change the velocity values to get better play action?


Possible Extensions

There are many ways — and fun ways — to extend this assignment. Add more and we will be a bit more lenient with grading than if you implement the bare minimum. But note that a submission that does not have a good class invariant and good specifications for the methods will not be looked at kindly under any circumstances.

Here are some ways to extend the game.

Improve user control over bounces

The program gets rather boring if the only thing the player has to do is hit the ball. Let the player control the ball by hitting it with different parts of the paddle. For example, suppose the ball is coming down toward the right (or left). If it hits the left (or right) 1/4 the paddle, the ball goes back the way it came (both vx and vy are negated).

Implement "Try Again"

Let the player play as many games as they want. The player could click the mouse button to start a new game. A call on inherited procedure waitForClick() will wait (or pause) until the mouse is clicked.

Implement sound effects

It is an easy extension to add appropriate sounds for game events. We have provided several audio files in A7.zip. You are not restricted by those; you can easily find lots more (but it is a violation of the Academic Integrity Policy to use copyrighted material in your assignment).

You can load an audio file (e.g. bounce.au) by writing the following command

   AudioClip bounceClip= MediaTools.loadAudioClip("bounce.au");
Once it is loaded, you can play it whenever you want (such as when the ball hits something) by calling bounceClip.play();. The sound might get monotonous after a while, so make the sounds vary, and figure out a way to let the user turn sound off (and on).

Add the kicker

The arcade version of Breakout lures you in by starting off slowly. But as soon as you think you are getting the hang of things, the ball speeds up, making life more exciting. Implement this in some fashion, perhaps by doubling the horizontal velocity of the ball on the seventh time it hits the paddle.

Keep score

Design some way to score the player performance. This could be as simple as the number of bricks destroyed. However, you may want to make the bricks in the higher rows more valuable.

It is best to display the score underneath the paddle. However, remember that GLabel is an object, and the ball hitting it should have no effect.

Use your imagination

What else have you always wanted a game like this to do? At some point your game might stop being like Breakout and be more like Arkanoid.


Create a Jar File (Optional)

You might want people to be able to play your game without having to have DrJava installed. To do this, create a jar file Breakout.jar, checking that you can run it as a stand-alone application (e.g. by double-clicking on its icon). You can use the procedure outlined in the lecture notes during the last week of class (also covered in appendix I.4 in the text). Alternatively, you can use DrJava's project facilities.

You do not have to turn anything in for this step. Just send your jar file to your friends and let them amuse themselves for a couple of hours destroying innocent bricks that never did them any harm, while you study for the final.


Completing the Assignment

Before submitting anything, test your program to see that it works. Play for a while and make sure that as many parts of it as you can check are working. If you think everything is working, try this: just before the ball is going to pass the paddle level, move the paddle quickly so that the paddle collides with the ball rather than vice-versa. Does everything still work, or does your ball seem to get "glued" to the paddle? If you get this error, try to understand why it occurs and how you might fix it.

When you are done, you should put a non-JavaDoc comment (/* */, not /** */) in Breakout.java with the following information:

  • Your name(s) and netid(s);
  • The time you spent on this assignment.
  • A brief description of your extensions.
  • Your opinion of this assignment and how we could improve it.

As always, you should make sure that class Breakout is indented properly. Do this by hitting tab on every line in DrJava, so that it will auto-indent for you.

Turning it In

You should upload the file Breakout.java onto CMS by the due date: Saturday, May 5th at 11:59PM. If you have extra files to submit (e.g. custom sound or art files), put all your files in a zip file called everything.zip and submit this instead. Do not give us .class files and other stuff; just zip up Breakout.java and other files that your program needs.