Homework #10 CMSC 131
Due May 6th, 2003 Object-Oriented Programming I
  Spring 2004

 

Breakout

This homework will give you experience implementing a complex application that uses inheritance.

You will implement a version of the decades-old Breakout game.  If you are not familiar with Breakout, you can see an online version (screenshot below).

Your game will support the following features.  Note that for this homework, we have attempted to keep the rules as simple as possible, but you will implement everything.  A framework will be supplied, but it is minimal.

You will build your application based on the supplied framework.  This framework provides:

For this project, we are giving you the object design with the essential semantics of each object, and it is up to you to implement our design.  You should start with the implementation of the Breakout class that we give you which implements the BreakoutMVC interface and sets up the game window by creating an instance of BreakoutFrame which supplies the above-mentioned features.  The version of Breakout we give you just has a placeholder for each method you must implement which are described within the comments of Breakout.java

To successfully complete this project, you must implement the three elements of a standard model-view-controller system.  The "model" of the game should contain a list of blocks, a ball, and a paddle. The "view" should provide a mechanism to paint the board based on the model.  The "controller" should allow you to update the model which includes moving the paddle left and right, updating the position of the ball, and bouncing the ball off of walls, paddles, and blocks - possibly modifying the model in the process by removing a block if the ball bounces off it.

The blocks, ball and paddle have a significant amount of shared code as they all appear on the screen with a simple rectangle.  In fact, they all are rectangles.  Thus, the simplest way to implement them is to inherit from a rectangle class - one of which is defined by java, in java.awt.geom.Rectangle2D.Double.  The objects also must have a color and the ability to paint themselves on the screen at their appropriate position.  You have to implement that part.  We have designed a class hierarchy to support sharing of code that you must follow as follows:

MovableShape must contain one instance variable to represent the color of the object along with methods for manipulating the color and painting the rectangle.  Since some of the objects move, we have found it useful to define setSize() and setPosition() methods which manipulate the underlying rectangle.  To understand how this all works, we will take a quick look at rendering with Java.  The java.awt and java.awt.geom packages provide sophisticated support for 2D rendering of shapes, text and images with colors, patterns, transparency, anti-aliasing, rotation, magnification, etc.  For this project, we are going to use just a tiny portion of these two packages.  We will use Rectangle2D.Double from java.awt.geom, and the Color and Graphics2D classes from java.awt as follows.

java.awt.geom.Rectangle2D.Double represents a rectangle via it's top-left corner (x and y) and its dimensions (width and height) as doubles, and provides methods of getting its values - getX(), getY(), getWidth() and getHeight() as well as setting all the dimensions of the rectangle at once - setRect().

java.awt.Color represents a color to be used for drawing.  You can create your own color value from red, green and blue components, or more simply, you can use predefined constants for many common colors - such as Color.RED.

java.awt.Graphics2D represents the core class for rendering onto a window.  It has many methods, but for this assignment, you need to use only two: setColor() and fill().  An instance of the Graphics2D class is passed in to your paint() method, and you use it to paint onto the window. If 'color' is a variable of type Color, then you can call g.setColor(color) which tells Graphics2D that the next things that are drawn should be drawn in the specified color.  Then, if 'rect' is a variable of type Rectangle2D.Double, then you can call g.fill(rect) which will actually paint a rectangle on the screen within the coordinates specified by rect in the current color as previously specified by g.setColor().  However, for this assignment, you don't have an explicit instance of Rectangle2D.Double.  Instead, your MovableShape object has a paint method.  But, since MovableShape extends Rectangle2D.Double, you can use it as the rectangle you pass in to fill(). And since you will be calling fill() from within the paint() method of MovableShape, you will pass in a pointer to itself - and use 'this' do to do.  Thus, the code you will use to paint with will be g.fill(this).

Given the above, you should be able to figure out how to implement MovableShape to support the management and rendering of a rectangle.  Then, you must implement the Block, Ball and Paddle subclasses to support the special features of those three objects.  Block is very simple and actually doesn't have any special features, so its implementation is trivial just consisting of a constructor to set its size and color (which are up to you). Paddle is just slightly more complex and must contain methods for moving it to the left and right (exactly how much it should move left and right with each key press is up to you).

Finally, you must implement the Ball object which is slightly more complicated.  It must support the ability to move in a given direction (one small amount per call to step()), the ability to bounce off any of the four walls, and to bounce off the paddle or a block.  Note that since the paddle and block are both inherited from MovableShape, it is sufficient to support the ability to bounce off a MovableShape, and then use that feature in your implementation of Breakout.step(). Since the goal of this project is for you to gain experience using inheritance and multiple objects - and not to master the mathematics of ball bouncing, we describe here how you can manage the bouncing of the ball.

First of all, your Ball class must maintain its current movement direction - so it can move a bit in that direction at each call to step().  It turns out to be easiest to represent that direction with two variables to represent the amount the ball should move in the X and Y directions at each step:

double dx;
double dy;

Then, whenever you want to manually set the angle the ball is moving, you can set dx and dy as follows (where 'theta' is an input variable in degrees with 0 degrees representing the direction to the right and degrees increasing clockwise, and MOVE_DIST represents a constant meaning how many pixels the ball should move each step):

double rad = Math.PI * theta / 180;
dx = MOVE_DIST * Math.cos(rad);
dy = MOVE_DIST * Math.sin(rad);

Given dx and dy, you can then move the ball MOVE_DIST pixels in that direction by adding dx to the current X position of the ball and dy to the current Y position of the ball.

Finally, here are two utility methods for bouncing the ball off of things. They assume that dx and dy are double variables as defined above, that there are getX(), getY(), and setPosition() methods available, that there are WIDTH and HEIGHT constants defining the dimensions of the ball, and that frameWidth and frameHeight are double variables defining the dimensions of the breakout game window.  It is up to you to define your classes so that all of these assumptions are met.

/*
* This bounces the ball off any of the four walls of the game board.
*/
public void bounceWithinFrame() {
  double x = getX();
  double y = getY();

  if ((x < 0) || ((x + WIDTH) > frameWidth)) {
    dx *= -1;
    x += dx;
  }

  if ((y < 0) || ((y + HEIGHT) > frameHeight)) {
    dy *= -1;
    y += dy;
  }
  setPosition(x, y);
}

/* 
* This determines if the ball currently intersects the specified MovableShape 
* object, and if it does, it bounces it off the shape. It returns true if
* the ball bounced off the shape, or false if it did not intersect the shape.
*
* This method has a small bug where it is possible to get the ball to bounce
* inside of an object, such as the paddle.  This doesn't happen very frequently,
* and it is a fun bug, so we have left it in.
*/ 
public boolean bounceOffBounds(MovableShape movableShape) {
  boolean bounce = false;
  double x1 = movableShape.getX();
  double y1 = movableShape.getY();
  double x2 = x1 + movableShape.getWidth();
  double y2 = y1 + movableShape.getHeight();

  double x = getX();
  double y = getY();

  // First check ball intersects shape
  if (intersects(movableShape)) {
    bounce = true;

    // Then determine whether it should bounce vertically or horizontally
    // Decide this based on whether it is closer to a vertical or horizontal edge
    double horDist = Math.min(Math.abs(x - x2), Math.abs((x+WIDTH) - x1));
    double verDist = Math.min(Math.abs(y - y2), Math.abs((y+HEIGHT) - y1));
    if (horDist < verDist) {
      dx *= -1;
      x += dx;
    } else {
      dy *= -1;
      y += dy;
    }
    setPosition(x, y);
  }

  return bounce;
}

Note that it is an error to modify an ArrayList as you iterate over it.  So, for example, in your step method, if you attempt to remove a block from the list of blocks (due to a block being hit by the ball) as you iterate over it, you will get a ConcurrentModificationException.  A simple mechanism to avoid this problem (for this homework) is to simply stop iterating over the blocks whenever one is removed.  This is ok because we only expect the ball to hit one block at a time.  So, you might write code that follows this pseudo-code pattern:

for each block in the list of blocks {
    if the ball hit this block {
        bounce the ball off the block
        remove the block
        exit this loop
    }
}

Getting the Code Distribution

We will be using CVS with AutoCVS as we have for previous assignments.  You can access the assignment code by checking it out from CVS with Eclipse (it is called 'hw10'). You can read the API documentation here.

Your Assignment

You must implement the complete Breakout game as defined above, following the specified object design.  We recommend that you implement your project in the following order:

To set your expectations of how much code is involved for a complete solution to this assignment, our solution (including comments) is a total of about 290 lines broken down as follows:

In comparison, our solution to Tetris was about 120 lines.  Subtracting the ball movement and bouncing code we gave you, this project will require about twice as much code as Tetris.  So, please do not wait until a few days before it is due or you will not have enough time to complete it.

Submitting Your Assignment

Submit your program by right-clicking on your project and select "Submit Project". Note that this option will only appear if you have successfully installed AutoCVS as described previously.  If you decide that you would like to make a change after you have submitted your project, you can resubmit as many times as you like before the extended deadline.  We will grade the last project submitted.

/* Name
 * Student ID
 * Homework #10
 */

<your code goes here>

Closed Assignment

This is a "closed" assignment.  You must do this assignment entirely by yourself.  You may not look at other people's code or show your code to others (except for the class instructors and TAs).  Any violations will be considered academic dishonesty and will be handled as such.

Challenge Problem

For an extra challenge, you can include support for the following features in your program: