Computer Science 120
Introduction to Programming
Spring 2011, Siena College
BetterBreakout Demo
A working demo of BetterBreakout will appear below. Click inside the applet to interact with it.
BetterBreakout BlueJ Project
Click here to download a BlueJ project for BetterBreakout.
BetterBreakout Source Code
The Java source code for BetterBreakout is below. Click on a file name to download it.
import objectdraw.*; import java.awt.*; /* $Id: Breakout.java 1593 2011-04-07 01:01:17Z terescoj $ */ /** * Example Breakout: play a simple breakout game * * @author Jim Teresco, Siena College, CSIS 120, Spring 2011 * */ public class Breakout extends WindowController { // position and dimensions of the court private static final double BORDER_WIDTH = 25; // dimensions of the paddle private static final double PADDLE_WIDTH = 50; private static final double PADDLE_HEIGHT = 20; // dimensions of the ball private static final double BALL_DIAMETER = 20; // numbers and sizes of bricks private static final int NUM_BRICKS_PER_ROW = 10; private static final int NUM_BRICK_ROWS = 6; private static final double BRICK_HEIGHT = 10; private static final double BRICK_TOP_OFFSET = 50; // number of turns per game private static final int NUM_TURNS = 3; private FilledRect paddle; private FramedRect boundary; // the boundary of the playing area. // last ball in play private BreakoutBall lastBall; // the collection of bricks to hit private BrickCollection bricks; // turns remaining private int turnsLeft; // array of FilledOvals representing turns remaining private FilledOval[] lives; /** * set up playing area and bricks. */ public void begin() { new FilledRect(BORDER_WIDTH, BORDER_WIDTH, canvas.getWidth() - BORDER_WIDTH*2, canvas.getHeight() - BORDER_WIDTH*2, canvas).setColor(Color.gray); // make the playing area boundary = new FramedRect(BORDER_WIDTH, BORDER_WIDTH, canvas.getWidth() - BORDER_WIDTH*2, canvas.getHeight() - BORDER_WIDTH*2, canvas); // make the paddle paddle = new FilledRect(canvas.getWidth()/2, canvas.getHeight() - BORDER_WIDTH - PADDLE_HEIGHT -1, PADDLE_WIDTH, PADDLE_HEIGHT, canvas); // set up the bricks bricks = new BrickCollection(NUM_BRICKS_PER_ROW, NUM_BRICK_ROWS, BRICK_HEIGHT, BRICK_TOP_OFFSET+BORDER_WIDTH, BORDER_WIDTH, boundary.getWidth(), canvas); // no ball in play lastBall = null; // initialize number of turns turnsLeft = NUM_TURNS; lives = new FilledOval[NUM_TURNS]; // create visual display of turns remaining double nextXBall = BORDER_WIDTH; double yBallPos = (BORDER_WIDTH - BALL_DIAMETER)/2; for (int ballNum = 0; ballNum < NUM_TURNS; ballNum++) { lives[ballNum] = new FilledOval(nextXBall, yBallPos, BALL_DIAMETER, BALL_DIAMETER, canvas); nextXBall = nextXBall + 2*BALL_DIAMETER; } } /** * create a ball if no ball is currently in play * * @param point not used */ public void onMousePress(Location point) { if ((turnsLeft > 0) && (lastBall == null || lastBall.outOfPlay())) { lastBall = new BreakoutBall(BALL_DIAMETER, paddle, bricks, boundary, canvas); turnsLeft--; lives[turnsLeft].removeFromCanvas(); } } /** * move the paddle to follow the mouse's x position * * @param point the current Location of the mouse pointer */ public void onMouseMove(Location point) { if (point.getX() < boundary.getX()) { // place paddle at left edge of the court paddle.moveTo(boundary.getX(), paddle.getY()); } else if (point.getX() + PADDLE_WIDTH > boundary.getX() + boundary.getWidth()) { // place paddle at right edge of the court paddle.moveTo(boundary.getX() + boundary.getWidth() - PADDLE_WIDTH, paddle.getY()); } else { // keep the edge of the paddle lined up with the mouse paddle.moveTo(point.getX(), paddle.getY()); } } }
import objectdraw.*; import java.awt.*; /* $Id: BreakoutBall.java 1593 2011-04-07 01:01:17Z terescoj $ */ /** * * A simple active object that controls a ball that bounces around the * canvas and interacts with a boundary, a pong paddle, and a set of * breakout bricks. * * @author Jim Teresco, Siena College, CSIS 120, Spring 2011 * */ public class BreakoutBall extends ActiveObject { // control speed of falling balls private static final double MIN_SPEED = 3; private static final double MAX_SPEED = 10; private static final int DELAY_TIME = 33; // the ball controlled by this instance private FilledOval ball; // the paddle with which we will interact private FilledRect paddle; // how far to fall before stopping and disappearing? private double yMax; // how far up to go before bouncing off the ceiling? private double yMin; // and what about the walls? private double xMin, xMax; // is this ball still in play? private boolean outOfPlay; // the brick collection with which we interact private BrickCollection bricks; /** * Draw a ball and start it moving around the playing area. * * @param diameter size of the ball to create * @param paddle the paddle with which to interact * @param bricks the collection of bricks with which the ball may collide * @param boundary the boundary of the playing area determining the * walls off which the ball will bounce * @param aCanvas the DrawingCanvas on which to place the ball */ public BreakoutBall(double diameter, FilledRect paddle, BrickCollection bricks, FramedRect boundary, DrawingCanvas aCanvas) { // compute/remember our boundaries (of the upper left corner of a ball) yMax = boundary.getY()+boundary.getHeight(); yMin = boundary.getY(); xMin = boundary.getX(); xMax = boundary.getX() + boundary.getWidth() - diameter; // random generator for the x-coordinate RandomDoubleGenerator posGen = new RandomDoubleGenerator(xMin, xMax); // draw the ball at its initial position, which is half way down // from the top of the playing area, at a randomly-selected x position ball = new FilledOval(posGen.nextValue(), (yMin + yMax)/2, diameter, diameter, aCanvas); ball.setColor(Color.white); // remember the paddle this.paddle = paddle; // remember the bricks this.bricks = bricks; // set the ball as in play outOfPlay = false; // activate! start(); } /** * move the ball repeatedly until it falls off screen, bouncing * off the paddle and walls and taking out bricks along the way */ public void run() { double xSpeed, ySpeed; // start by moving downward and some amount to the left or right RandomDoubleGenerator speedGen = new RandomDoubleGenerator(MIN_SPEED, MAX_SPEED); xSpeed = speedGen.nextValue(); double initYSpeed = speedGen.nextValue(); ySpeed = initYSpeed; // keep moving as long as we haven't fallen off the bottom of // the screen while (ball.getY() <= yMax) { // if we are above the top line, start moving down if (ball.getY() < yMin) { ySpeed = initYSpeed; } // if we are in contact with the paddle, start moving up if (ball.overlaps(paddle)) { if (ball.getY() < (paddle.getY() - ball.getHeight()/2)) { // hit a side, so change xSpeed instead of ySpeed xSpeed = -xSpeed; } else { ySpeed = -initYSpeed; } } // bounce off side walls if (ball.getX() < xMin || ball.getX() > xMax) { xSpeed = -xSpeed; } // check for contact with bricks if (bricks.hitBrick(ball)) { ySpeed = -ySpeed; } // move a little in the appropriate direction ball.move(xSpeed, ySpeed); pause(DELAY_TIME); } ball.removeFromCanvas(); outOfPlay = true; } /** * check if the ball has moved out of the playing area (turn over) * * @return whether the ball has moved out of the playing area */ public boolean outOfPlay() { return outOfPlay; } }
import objectdraw.*; import java.awt.*; /* * Example Breakout: the collection of bricks * * Jim Teresco, Siena College, CSIS 120, Spring 2011 * * $Id: BrickCollection.java 1593 2011-04-07 01:01:17Z terescoj $ */ public class BrickCollection { // dimensions of the array of bricks private int bricksPerRow; private int numRows; // the array private FilledRect[][] bricks; // some colors to use to define our rows of bricks private static final Color[] colors = { Color.red, Color.orange, Color.yellow, Color.green, Color.blue, Color.magenta }; /** * Construct a BrickCollection * * @param bricksPerRow the number of bricks per row * @param numRows the number of rows of bricks * @param brickHeight the height (in pixels) of a row of bricks * @param yPos the y-coordinate of the topmost row of bricks * @param xMin the x-coordinate of the leftmost brick * @param width the width of the playing area to be filled with bricks * @param canvas the DrawingCanvas on which to place the bricks */ public BrickCollection(int bricksPerRow, int numRows, double brickHeight, double yPos, double xMin, double width, DrawingCanvas canvas) { this.bricksPerRow = bricksPerRow; this.numRows = numRows; // construct the array bricks = new FilledRect[numRows][bricksPerRow]; double brickWidth = width/bricksPerRow; for (int row = 0; row < numRows; row++) { for (int col = 0; col < bricksPerRow; col++) { bricks[row][col] = new FilledRect(xMin + col*brickWidth, yPos + row*brickHeight, brickWidth, brickHeight, canvas); bricks[row][col].setColor(colors[row%colors.length]); } } } /** * Check if the given ball overlaps any brick, if so, remove it * and return true, otherwise return false. * * @param ball the FilledOval representing the ball to check for * overlap with the existing bricks * * @return whether the ball overlapped any brick */ public boolean hitBrick(FilledOval ball) { boolean hitOne = false; for (int row = 0; row < numRows; row++) { for (int col = 0; col < bricksPerRow; col++) { if (bricks[row][col] != null && bricks[row][col].overlaps(ball)) { bricks[row][col].removeFromCanvas(); bricks[row][col] = null; hitOne = true; } } } return hitOne; } }