Breakout – Part 2

Breakout – Part 2, I have broken it down into 2 parts because of the size of the source code and also the post in general, the first is the problem and solution and this is the solution in code and images.

Here is the screen output of the start of the game.

Breakout  - the start
Breakout - the start

Here is the screen output of the extra life coming down the screen, (the one in grey!!) and also the ball as well, can you catch them both ?

Breakout - extra life coming down
Breakout - extra life coming down

And here is the end of the game with the display information about how much you have scored.

Breakout - the end
Breakout - the end

Here is the full source code from the break out assignment 3, I have included it in the zip file above and also the PDF of the assignment.

/*
 * File: Breakout.java
 * -------------------
 * Name:
 * Section Leader:
 * 
 * This file will eventually implement the game of Breakout.
 */
 
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
 
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
 
public class Breakout extends GraphicsProgram implements MouseListener {
 
/** Width and height of application window in pixels */
	public static final int APPLICATION_WIDTH = 400;
	public static final int APPLICATION_HEIGHT = 600;
 
/** Dimensions of game board (usually the same) */
	private static final int WIDTH = APPLICATION_WIDTH;
	private static final int HEIGHT = APPLICATION_HEIGHT;
 
/** Dimensions of the paddle */
	private static final int PADDLE_WIDTH = 60;
	private static final int PADDLE_HEIGHT = 10;
 
/** Offset of the paddle up from the bottom */
	private static final int PADDLE_Y_OFFSET = 30;
 
/** Number of bricks per row */
	private static final int NBRICKS_PER_ROW = 10;
 
/** Number of rows of bricks */
	private static final int NBRICK_ROWS = 10;
 
/** Separation between bricks */
	private static final int BRICK_SEP = 4;
 
/** Width of a brick */
	private static final int BRICK_WIDTH =
	  (WIDTH - (NBRICKS_PER_ROW - 1) * BRICK_SEP) / NBRICKS_PER_ROW;
 
/** Height of a brick */
	private static final int BRICK_HEIGHT = 8;
 
/** Radius of the ball in pixels */
	private static final int BALL_RADIUS = 10;
 
/** Offset of the top brick row from the top */
	private static final int BRICK_Y_OFFSET = 70;
 
/** Number of turns */
	private static final int NTURNS = 3;
 
	/** the starting pause delay between paddle hits **/ 
	private static final int PAUSE_DELAY = 20;
	/** text size of the display text */
	private static final int TEXT_SIZE = 5;
	/** ball speed */
	private static final double BALL_SPEED = 3.0;
	/** extras - if you catch the extra score, this is the extra value */
	private static final int EXTRA_SCORE_VALUE = 100;
	/** extras speed coming down the screen */
	private static final double EXTRAS_SPEED = 3.0;
 
	public void mouseMoved(MouseEvent e)
	{
		Point movePos = e.getPoint();
		if (movePos.x < (WIDTH- PADDLE_WIDTH))
			paddleObject.setLocation(movePos.x,HEIGHT - (PADDLE_Y_OFFSET + PADDLE_HEIGHT));
	}
 
	// setup the bricks, with different colours
	private void CreateBricks()
	{
// if there is any more colours, rows or less then this could be a problem of creating the list of colours.
		colours[0] = Color.RED;
		colours[1] = Color.RED;
		colours[2] = Color.ORANGE;
		colours[3] = Color.ORANGE;
		colours[4] = Color.YELLOW;
		colours[5] = Color.YELLOW;
		colours[6] = Color.GREEN;
		colours[7] = Color.GREEN;
		colours[8] = Color.CYAN;
		colours[9] = Color.CYAN;
 
		int startX, startY = BRICK_Y_OFFSET;
		GRect bricks;
		// iRow = going downwards
		for (int iRow =0; iRow < NBRICK_ROWS; iRow++)
		{
			startX = BRICK_SEP;
			// iAisle = going across
			for (int iAisle = 0; iAisle < NBRICKS_PER_ROW; iAisle++)
			{
				bricks = new GRect(startX,startY, BRICK_WIDTH,BRICK_HEIGHT);
				bricks.setFillColor(colours[iRow]);
				bricks.setFilled(true);
				bricks.setColor(colours[iRow]);
				add(bricks);
				startX += BRICK_WIDTH + BRICK_SEP;
			}
			startY += BRICK_HEIGHT + BRICK_SEP;
		}
	}
 
	private void addPaddle()
	{
		paddleObject = new GRect((WIDTH/2) - (PADDLE_WIDTH/2), HEIGHT - (PADDLE_Y_OFFSET + PADDLE_HEIGHT),
								PADDLE_WIDTH, PADDLE_HEIGHT);
		paddleObject.setColor(Color.BLACK);
		paddleObject.setFillColor(Color.BLACK);
		paddleObject.setFilled(true);
		add(paddleObject);
	}
 
	private void setUpBall()
	{
		// middle !!
		ballX = WIDTH / 2;
		ballY = HEIGHT / 2;
		// ballVX / VY are the velocity of movement 
		ballObject = new GOval(ballX, ballY, BALL_RADIUS, BALL_RADIUS);
		ballObject.setFillColor(Color.BLACK);
		ballObject.setFilled(true);
		add(ballObject);
		// setup the ball to move a random direction
		ballVX = rgen.nextDouble(1.0, 3.0);
		if (rgen.nextBoolean(0.5)) ballVX -= ballVX;
		ballVY = BALL_SPEED;
	}
 
	// move the ball and check for collision around the screen, if gone to the bottom return true else false
	private Boolean moveBall()
	{
		GPoint ballPoint = ballObject.getLocation();
		if (ballPoint.getX() > WIDTH) ballVX = -ballVX;
		if (ballPoint.getX() <= 0) ballVX = -ballVX;
		if (ballPoint.getY() <= 0) ballVY = -ballVY;
		if (ballPoint.getY() > HEIGHT) return true;//ballVY = -ballVY; // basically lost
		ballObject.move(ballVX,ballVY);
		return false;
	}
 
	// get any Graphics Object surrounding the ball Object, be it the bat and ball, or bat and extras
	private GObject getCollidingObject(GObject ballObj)
	{
		if (!ballObj.isVisible()) return null;
		GPoint ballPoint = ballObj.getLocation();
		GObject coll;
		Point addingPoints[] = new Point[4];
		addingPoints[0]= new Point(0,0);
		addingPoints[1]= new Point(0,BALL_RADIUS);
		addingPoints[2]= new Point(BALL_RADIUS,BALL_RADIUS);
		addingPoints[3]= new Point(BALL_RADIUS,0);
		for (int i =0; i< 4; i++)
		{
			coll = getElementAt(ballPoint.getX()+addingPoints[i].x, ballPoint.getY()+addingPoints[i].y);
			if (coll != null)
					return coll;
		}
		return null;
	}
 
	// display the text on the screen to give some information.
	private void displayText(String text)
	{
		textDisplay = new GLabel(text,(WIDTH/2 - (text.length() * TEXT_SIZE)), HEIGHT/2);
		textDisplay.setFont(Font.SANS_SERIF);
		add(textDisplay);
	}
 
	// display the status , score and also lives left
	private void displayStatus()
	{
		if (status != null) remove(status);
		status = new GLabel("Score : "+score +" : Lives : "+lifesLeft,WIDTH/2, (BRICK_HEIGHT*2));
		add(status);
	}
 
	// difference between two points.
	private GPoint differenceBetween(GPoint a, GPoint b)
	{
		return new GPoint(a.getX() - b.getX(), a.getY() - b.getY());
	}
 
	// type = true - extra life, false - extra score (+100) 
	private void generateExtra(Boolean type, GObject startingPoint)
	{
		if (type)
		{
			// create a extraLife object if not one already
			extraLife.setLocation(startingPoint.getX(), startingPoint.getY());
			extraLife.setColor(Color.MAGENTA);
			extraLife.setFilled(true);
			extraLife.setVisible(true);
			add(extraLife);
		}
		else
		{
			// create a extraScore object if not one already
			extraScore.setLocation(startingPoint.getX(), startingPoint.getY());
			extraScore.setColor(Color.GRAY);
			extraScore.setFilled(true);
			extraScore.setVisible(true);
			add(extraScore);
		}
	}
 
	/* move the extraLife/Score if they are visible.*/
	private void moveExtras()
	{
		if (extraLife.isVisible())
		{
			extraLife.move(0, EXTRAS_SPEED);
			if (extraLife.getLocation().getY() > HEIGHT) remove(extraLife);
		}
		if (extraScore.isVisible())
		{
			extraScore.move(0,EXTRAS_SPEED);
			if (extraScore.getLocation().getY() > HEIGHT) remove(extraScore);
		}
	}
/* Method: run() */
/** Runs the Breakout program. */
	public void run() {
		/* You fill this in, along with any subsidiary methods */
		getGCanvas().setSize(WIDTH, HEIGHT);
		GObject colliding;
		AudioClip bounceSound = MediaTools.loadAudioClip("bounce.au");
		Boolean bottomHit, emptyBricks = true;
		int bricksLeft=0, paddleHit, randomExtra;
 
		extraLife = new GOval(0, 0, BALL_RADIUS,BALL_RADIUS);
		extraScore = new GOval(0,0, BALL_RADIUS, BALL_RADIUS);
		score = 0;
		lifesLeft = NTURNS;
		pauseDelay = PAUSE_DELAY;
		// loop through for the amount of lives
		while (lifesLeft > 0)
		{
			/* setup the display and then ask the user to click to start */
 
			if (emptyBricks)
			{
				/* clear the screen and rebuild the bricks, if come to the end of that level, or just starting*/
				removeAll();
				bricksLeft = NBRICKS_PER_ROW * NBRICK_ROWS;
				CreateBricks();
				addPaddle();
				addMouseListeners();
				emptyBricks = false;
			}
			displayText("Click to start");
			displayStatus();
			waitForClick();
			remove(textDisplay);
			setUpBall();
			paddleHit =0;
			bottomHit =false;
			randomExtra = rgen.nextInt(5);
			/* end of setup display*/
			while (true)
			{
				// moveBall returns true if the bottom has been hit.
				if (moveBall()) {
					bottomHit = true;
					break;
				}
				moveExtras();
				colliding = getCollidingObject(ballObject);
				// if the ball has collided with anything
				if (colliding != null)
				{
					if (!colliding.equals(paddleObject))
					{
						if (!colliding.equals(status))
						{
							if (randomExtra <=0)
							{
								randomExtra = rgen.nextInt(5);
								// create a extra life if next random extra is even else extra score
								if ((randomExtra % 2)==0)
									generateExtra(true,colliding);
								else
									generateExtra(false,colliding);
							}
							// delete the object that we just collided with, e.g. the brick
							if (colliding.getClass().getSimpleName().equalsIgnoreCase("GRect"))
							{
								// decrement the randomExtra 
								randomExtra--;
								// increment the score by finding out the colour of brick that we hit.
								Color collidColor = colliding.getColor();
								for (int i = (NBRICK_ROWS-1); i > 0; i--)
								{
									score++;
									if (colours[i] == collidColor) break;
								}
								displayStatus();
								remove(colliding);
								/* if no more bricks.. exit */
								bricksLeft--;
								if (bricksLeft == 0) 
								{ 
									emptyBricks = true; 
									break;
								}
								/*//another way of checking to see if there is any GRects left.!!.
								emptyBricks = true;
								for (int i =0; i < getElementCount(); i++)
								{
									if (getElement(i).getClass().getSimpleName().equalsIgnoreCase("GRect"))
									{
										if (!getElement(i).getClass().equals(paddleObject))
										{
											emptyBricks= false;
											break;
										}	
									}
								}
								if (emptyBricks) break;
								 */
								ballVY = -ballVY;
							}
						}
					}
					else
					{
						// handle the increase of speed (less delay)
						paddleHit++;
						if ((paddleHit % 7) ==0) pauseDelay--;
 
						bounceSound.play();
						GPoint difBetween = differenceBetween(ballObject.getLocation(), colliding.getLocation());
						ballVX = (difBetween.getX() - (PADDLE_WIDTH/2))/2;
						ballVY = -(difBetween.getY()/BALL_SPEED);
						// for when the ball may try and bounce of the side of the bat and then the wall!!.
						if (ballVY == 0.0) ballVY = 1.0;
						// move in the opposite direction
						ballVY = -ballVY;
					}
				}
				/* extra items */
				colliding = getCollidingObject(extraLife);
				if (colliding != null)
				{
					if (colliding.equals(paddleObject))
					{
						extraLife.setVisible(false);
						lifesLeft++;
						displayStatus();
					}
				}
				colliding = getCollidingObject(extraScore);
				if (colliding != null)
				{
					if (colliding.equals(paddleObject))
					{
						extraScore.setVisible(false);
						score+=EXTRA_SCORE_VALUE;
						displayStatus();
					}
				}
				pause(pauseDelay);
			}
			if (bottomHit) 
			{
				lifesLeft--;
				displayStatus();
				if (lifesLeft > 0)
				{
					displayText("Bottom hit : lives left = " + lifesLeft);
				}
				else 
					displayText("End of Game : your score " + score);
			}
			if (emptyBricks)
				displayText("Yeppy you do it!! click to start again");
			waitForClick();
			remove(textDisplay);
		}
	}
 
	private GRect paddleObject;
	private GOval ballObject, extraScore, extraLife;
	private GLabel textDisplay, status;
	private double ballVX, ballVY, ballX, ballY;
	private int lifesLeft, score, pauseDelay;
	private RandomGenerator rgen = RandomGenerator.getInstance();
	private Color colours[] = new Color[NBRICK_ROWS];
}

Leave a Reply

Your email address will not be published. Required fields are marked *