Skip to content

Instantly share code, notes, and snippets.

@kenechiokolo
Created April 3, 2015 14:18
Show Gist options
  • Save kenechiokolo/4c205eeb9889b9e188bb to your computer and use it in GitHub Desktop.
Save kenechiokolo/4c205eeb9889b9e188bb to your computer and use it in GitHub Desktop.
CS106A Assignment 3
/*
* 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 {
/** 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;
private static final int NLIVES = 3;
private static final double DELAY = 1;
private static final int COUNTDOWN = 3;
private static final int COUNTDOWN_DELAY = 1000;
private static final int NLEVELS = 1;
/* Method: run() */
/** Runs the Breakout program. */
public void run() {
setUp();
welcomeScreen();
gamePlay();
}
// sets up the game
private void setUp() {
drawBricks();
createPaddle();
addMouseListeners();
}
// displays a welcome screen with instructions
private void welcomeScreen() {
GLabel welcome = new GLabel ("Welcome to Kenechi's version of Breakout");
add(welcome);
welcome.move(10, 30);
pause(COUNTDOWN_DELAY*3);
welcome.setLabel("Click on the paddle to select it.");
pause(COUNTDOWN_DELAY*3);
welcome.setLabel("Once selected, you control it by moving the mouse left or right...");
pause(COUNTDOWN_DELAY*3);
welcome.setLabel("The aim of the game is to bounce the ball back up at the bricks...");
pause(COUNTDOWN_DELAY*3);
welcome.setLabel("You win if you destroy all the bricks.");
pause(COUNTDOWN_DELAY*3);
welcome.setLabel("You lose if the ball falls beneath the paddle");
pause(COUNTDOWN_DELAY*3);
welcome.setLabel("You have "+NLIVES+" lives before the game is over");
pause(COUNTDOWN_DELAY*3);
remove(welcome);
}
// initiates the gameplay
private void gamePlay() {
levelOne();
if (currentLevel == NLEVELS) {
endOfGameMessage();
} else gameover();
}
// displays message if you complete the game
private void endOfGameMessage() {
GLabel endgame = new GLabel ("Congratulations! You have completed the game.");
add(endgame);
endgame.move(appCentreX - endgame.getWidth() / 2, appCentreY);
pause(COUNTDOWN_DELAY*5);
// endgame.setLabel("Press 1 if you would like to play again.");
// int playAgain = readInt("");
// if (playAgain == 1) {
// gamePlay();
// }
remove(endgame);
}
//gameplay for level one (structure needs to be rewritten so brick formation is dependent on levels)
private void levelOne() {
int initTotalBricks = NBRICK_ROWS * NBRICKS_PER_ROW;
bricksLeft = initTotalBricks;
countdown();
for (int i=0; i < NLIVES; i++) {
createBall();
setBallTrajectory();
while (ball.getY() < APPLICATION_HEIGHT && bricksLeft > 0) {
moveBall();
checkBoundaryCollision();
checkGameCollision();
pause(DELAY);
}
if (bricksLeft == 0) {
i = NLIVES;
}
}
if (bricksLeft == 0) {
levelComplete();
currentLevel = currentLevel + 1;
} else {
gameover();
}
}
// displays a gameover message if player loses
private void gameover() {
remove(paddle);
GLabel gameover = new GLabel ("Game Over...you lose :(");
add(gameover);
gameover.move(appCentreX - gameover.getWidth() / 2, appCentreY);
// endgame.setLabel("Press 1 if you would like to play again.");
// int playAgain = readInt("");
// if (playAgain == 1) {
// gamePlay();
// }
}
// countdown before the games starts
private void countdown() {
for (int i=COUNTDOWN; i > 0; i--) {
GLabel countdown = new GLabel (""+i+"");
countdown.move(appCentreX - (countdown.getWidth() / 2), appCentreY);
add(countdown);
pause(COUNTDOWN_DELAY);
remove(countdown);
}
}
// sets initial trajectory of ball (including velocity)
private void setBallTrajectory() {
vx = rgen.nextDouble(0.07, 0.2);
if (rgen.nextBoolean(0.5)) vx = -vx;
vy = 0.2;
}
// moves the ball in between collisions, dependent upon velocity & DELAY
private void moveBall() {
ball.move(vx, vy);
}
// checks for collision with application boundary
private void checkBoundaryCollision() {
if (ball.getX() + ballDiameter > APPLICATION_WIDTH) {
ball.move(-ball.getX() + APPLICATION_WIDTH - ballDiameter, 0);
vx = -vx;
}
if (ball.getX() < 0) {
ball.move(0 - ball.getX(), 0);
vx = -vx;
}
if (ball.getY() < 0) {
ball.move(0, 0 - ball.getY());
vy = -vy;
}
}
// checks for collisions between bricks & paddle
private void checkGameCollision() {
if (collision() == paddle) { // bounces ball if it collides with paddle
// if ball collides with right half of paddle while travelling right, vx increases based on % of distance from centre of paddle
if ((ball.getX() > paddle.getX() + (PADDLE_WIDTH / 2) && vx > 0)) {
vx = vx * 1 + ((ball.getX() - paddle.getX() + (PADDLE_WIDTH / 2)) / PADDLE_WIDTH) / 5;
}
// if ball collides with left half of paddle while travelling left, vx increases based on % of distance from centre of paddle
if ((ball.getX() < paddle.getX() + (PADDLE_WIDTH / 2) && vx < 0)) {
vx = vx * 1 + ((ball.getX() - paddle.getX() + (PADDLE_WIDTH / 2)) / PADDLE_WIDTH) / 5;
}
// if ball collides with right half of paddle while travelling left, vx reverses and increases based on % of distance from centre of paddle
if ((ball.getX() > paddle.getX() + (PADDLE_WIDTH / 2) && vx < 0)) {
vx = -vx * 1 + ((ball.getX() - paddle.getX() + (PADDLE_WIDTH / 2)) / PADDLE_WIDTH) / 5;
}
// if ball collides with left half of paddle while travelling right, vx reverses and increases based on % of distance from centre of paddle
if ((ball.getX() < paddle.getX() + (PADDLE_WIDTH / 2) && vx > 0)) {
vx = -vx * 1 + ((ball.getX() - paddle.getX() + (PADDLE_WIDTH / 2)) / PADDLE_WIDTH) / 5;
}
vy = -vy;
paddleHits++;
collider = null;
} else if (collision() !=null && ball.getY() < (APPLICATION_HEIGHT - ballDiameter)) { // removes brick and bounces ball (y direction only) if ball collides with brick
remove(collider);
vy = -vy;
bricksLeft--;
collider = null; // resets collider to null
}
// accelerates vy based on NDiffChange
if (paddleHits > NDiffChange) {
vy = vy * acceleration;
paddleHits = 0;
NDiffChange = NDiffChange * NDiffChangeDecay;
}
}
// displays a message if you complete the level
private void levelComplete() {
remove(ball);
GLabel levelComplete = new GLabel("Congratulations! You've completed this level!");
add(levelComplete);
levelComplete.move(appCentreX - levelComplete.getWidth() / 2, appCentreY);
pause(COUNTDOWN_DELAY*5);
remove(levelComplete);
}
// determines if ball collides with bricks or paddle
// if collision is present, assigns collider value of whatever
// ball collides with, returns this as value of collision()
private GObject collision() {
double ballX = ball.getX();
double ballY = ball.getY();
if (getElementAt(ballX, ballY) != null) {
collider = getElementAt(ballX, ballY);
}
if (getElementAt(ballX + ballDiameter, ballY) != null) {
collider = getElementAt(ballX + ballDiameter, ballY);
}
if (getElementAt(ballX, ballY + ballDiameter) != null) {
collider = getElementAt(ballX, ballY + ballDiameter);
}
if (getElementAt(ballX + ballDiameter, ballY + ballDiameter) != null) {
collider = getElementAt(ballX + ballDiameter, ballY + ballDiameter);
}
return collider;
}
// creates and adds a ball to the game. The ball is blue.
private void createBall() {
ball = new GOval(appCentreX - BALL_RADIUS, appCentreY - BALL_RADIUS, ballDiameter, ballDiameter);
ball.setFilled(true);
ball.setFillColor(Color.blue);
ball.setColor(Color.blue);
add(ball);
}
// draws the bricks and adds them to the gamespace
private void drawBricks() {
redRows();
orangeRows();
yellowRows();
greenRows();
cyanRows();
}
// creates and adds a paddle to the game
private void createPaddle() {
paddle = new GRect (getWidth() / 2 - PADDLE_WIDTH / 2, getHeight() - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setFilled(true);
add(paddle);
}
// Called on mouse press to record the coordinates of the click
public void mouseClicked(MouseEvent e) {
last = new GPoint(e.getPoint());
gobj = getElementAt(last);
}
// Called on mouse drag to reposition the object
public void mouseMoved(MouseEvent e) {
if (gobj == paddle) {
gobj.move(e.getX() - last.getX(), 0);
last = new GPoint(e.getPoint());
}
// checks to see if paddle (gobj) is within right wall, if not, moves it back
if (gobj.getX() > APPLICATION_WIDTH - PADDLE_WIDTH) {
gobj.move(-(gobj.getX() - APPLICATION_WIDTH + PADDLE_WIDTH) , 0);
}
// checks to see if paddle (gobj) is within left wall, if not, moves it back
if (gobj.getX() < 0) {
gobj.move(0 - gobj.getX() , 0);
}
}
// draws the red rows (descending order) and adds to canvas
private void redRows() {
for (int i=0; i<2; i++) {
redRow();
resetX();
yShift();
}
}
// draws a single red row and adds to canvas
private void redRow() {
for (int i=0; i < NBRICKS_PER_ROW; i++) {
add(redBrick());
// x determines bricks positioning, this shifts positioning along for each new brick
x = x + (BRICK_WIDTH + BRICK_SEP);
}
}
// draws a single red brick in top left corner (first brick, does not add)
private GRect redBrick() {
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
brick.setFillColor(Color.red);
brick.setColor(Color.red);
return brick;
}
// x determines positioning of bricks, this method resets it to first column (think typewriter)
private void resetX() {
x = BRICK_SEP / 2;
}
// y determines positioning of bricks, this shifts it to next row
private void yShift() {
y = y + (BRICK_HEIGHT + BRICK_SEP);
}
// draws orange rows
private void orangeRows() {
for (int i=0; i<2; i++) {
orangeRow();
resetX();
yShift();
}
}
// draws a single orange row
private void orangeRow() {
for (int i=0; i < NBRICKS_PER_ROW; i++) {
add(orangeBrick());
x = x + (BRICK_WIDTH + BRICK_SEP);
}
}
// creates a single orange brick (does not add to canvas)
private GRect orangeBrick() {
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
brick.setFillColor(Color.orange);
brick.setColor(Color.orange);
return brick;
}
// draws two yellow rows of bricks (descending) and adds them to the canvas
private void yellowRows() {
for (int i=0; i<2; i++) {
yellowRow();
resetX();
yShift();
}
}
// adds a single yellow row of bricks to the canvas, starting from the left
private void yellowRow() {
for (int i=0; i < NBRICKS_PER_ROW; i++) {
add(yellowBrick());
x = x + (BRICK_WIDTH + BRICK_SEP);
}
}
// draws a single yellow brick, does not add to canvas
private GRect yellowBrick() {
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
brick.setFillColor(Color.yellow);
brick.setColor(Color.yellow);
return brick;
}
// draws two green rows of bricks (descending) and adds to canvas
private void greenRows() {
for (int i=0; i<2; i++) {
greenRow();
resetX();
yShift();
}
}
// draws single green row of bricks (from left to right) and adds to the canvas
private void greenRow() {
for (int i=0; i < NBRICKS_PER_ROW; i++) {
add(greenBrick());
x = x + (BRICK_WIDTH + BRICK_SEP);
}
}
// draws a single green brick but does not add to canvas (x, y)
private GRect greenBrick() {
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
brick.setFillColor(Color.green);
brick.setColor(Color.green);
return brick;
}
// draws two cyan rows and adds to canvas (descending)
private void cyanRows() {
for (int i=0; i<2; i++) {
cyanRow();
resetX();
yShift();
}
}
// draws single cyan row and adds to canvas (from left to right)
private void cyanRow() {
for (int i=0; i < NBRICKS_PER_ROW; i++) {
add(cyanBrick());
x = x + (BRICK_WIDTH + BRICK_SEP);
}
}
// draws single cyan brick, does not add to canvas, position (x, y)
private GRect cyanBrick() {
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
brick.setFillColor(Color.cyan);
brick.setColor(Color.cyan);
return brick;
}
// x position of first brick
double x = BRICK_SEP / 2;
// y position of first brick
double y = BRICK_Y_OFFSET;
GRect paddle; // the paddle used for gameplay
private GOval ball; // the ball used for gameplay
private double NDiffChange = 10; // number of paddle hits until vy increases
private double acceleration = 1.3; // acceleration of vy
private double NDiffChangeDecay = 0.9; // decay of NDiffChange (means exponential increase of vx)
private GPoint last; // last position of mouse when pressed
private GObject gobj; // generic g object
private double ballDiameter = BALL_RADIUS*2;
private double appCentreX = APPLICATION_WIDTH / 2; // application centre point on x-axis
private double appCentreY = APPLICATION_HEIGHT / 2; // application centre point on y-axis
private double vx, vy;
private RandomGenerator rgen = RandomGenerator.getInstance();
private GObject collider; // object with which the ball collided
private int bricksLeft;
private int paddleHits = 1;
private int currentLevel = 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment