Skip to content

Instantly share code, notes, and snippets.

@mtliendo
Created August 26, 2013 05:12
Show Gist options
  • Save mtliendo/6338276 to your computer and use it in GitHub Desktop.
Save mtliendo/6338276 to your computer and use it in GitHub Desktop.
This is the final version of my Breakout game from the cs106a course that I'm currently studying, feel free to leave any feedback as I'm always continuing my learning!
/*
* File: Breakout.java
* -------------------
* Name:Michael Liendo
* @Author: Model3volution
*
* This file will eventually implement the game of Breakout.
* Total time spent creating the bricks: 2.5 hours.
* Total time spent creating a paddle w/in the bounds: 2 hours
* Total time spent testing all collsions (wall, paddle, bricks): 2.5 hours
*/
package graphicAnimation;
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
* Should not be used directly (use getWidth()/getHeight() instead).
* * */
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;
/** Total number of bricks */
private static final int TOTAL_BRICKS = NBRICKS_PER_ROW * NBRICK_ROWS;
/** 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;
/**Ball DELAY */
private static final int DELAY_START = 20;
/**The amount of times before the ball speeds up*/
private static final int SLOW_BALL = 5;
//////////////////////////////////////////////////* Method: run() */////////////////////////////////
/** Runs the Breakout program. */
//taken directly from the handout, presumably this assigns a sound to a variable of type AudioClip.
AudioClip bounceClip = MediaTools.loadAudioClip("bounce.au");
public void run() {
setup();
playGame();
}
/////////////////////////////////////////// SETUP ///////////////////////////////////
private void setup() {
createBricks();
addMouseListeners();
createPaddle();
}
private void createBricks() {
//Double for loop. the main loop goes through the rows, the inside loops spans across the columns.
//for everytime i<n_rows move to the correct row so that the columns can be built, for everytime i < n_columns, build the columns
//this loops essentially controls the y-axis of the bricks placement
for(int row = 0; row < NBRICK_ROWS; row++){
//this loop essentially controls the x-axis of the brick placement
//NOTE: SEEMS TO OFF BY A COUPLE PIXELS...DUE TO INTEGER DIVISION?
for(int putBrick = 0; putBrick < NBRICKS_PER_ROW; putBrick++){
/*int x: take the width, divide it by 2, subtract a row of bricks, including the sum of all the spaces,
* and each interval, increase x by the brick + it's spacing.
* Basically: the first line handles centering, the second line is simply spacing.
*/
int x = getWidth() / 2 - ((NBRICKS_PER_ROW * (BRICK_WIDTH + BRICK_SEP)) / 2) +
(putBrick * (BRICK_WIDTH + BRICK_SEP));
/*Start at 70 pixels down, and increase that by the brick height + the brick separation, all
* multiplied by the row that we're currently on.
*/
int y = BRICK_Y_OFFSET + (row * (BRICK_HEIGHT + BRICK_SEP));
//create a brick that's filled and has a color assigned by a method.
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
brick.setColor(assignColor(row));// this method returns a color for the bricks dependent on the row
add(brick);
//pause(50); <-------USED FOR DEBUGGING, THAT WAY I COULD SEE HOW THE BRICKS WERE BEING DISPLAYED.
}
}
}
//Takes in the row integer from above, and uses that to determine the color.
private Color assignColor(int row) {
if(row == 0 || row == 1) return Color.RED;
if(row == 2 || row == 3) return Color.ORANGE;
if(row == 4 || row == 5) return Color.YELLOW;
if(row == 6 || row == 7) return Color.GREEN;
/*remember, when using if-statements in a private method, you need a "catch all", so IF none
* of the other if's work, the catch all is the else statement below.
*/
else return Color.CYAN;
}
//this method simply creates a filled paddle at the appropriate location on the screen.
private void createPaddle(){
paddleStartX = (getWidth() - PADDLE_WIDTH) / 2;
paddleStartY = getHeight() - PADDLE_Y_OFFSET - PADDLE_HEIGHT;
paddle = new GRect(paddleStartX, paddleStartY, PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setFilled(true);
add(paddle);
}
/*This is my event that occurs whenever the mouse is moved.
* Essentially, move the mouse in the x direction so long as
* the entire paddle is visible.
* REMEMBER: Events happen in once instance, so using a while loop doesn't help.
*/
public void mouseMoved(MouseEvent e){
//keep track of the old position by setting the location to the middle of the paddle
double lastX = paddle.getX() + PADDLE_WIDTH / 2;
//set the constraints for the paddle: only move the paddle when the mouse is dragged in
//the screen MINUS half the width of the paddle. This way the entire paddle is in the screen.
if(e.getX() > 0 + (PADDLE_WIDTH / 2) && e.getX() < getWidth() - (PADDLE_WIDTH / 2)){
//Tricky Part: have the paddle follow the mouse
/*move the paddle to where the X location of the mouse is (the mouse is the event)
* by subtracting the difference of the event from the last X position
* lastly, assign the location of the event as the last position of the X location.
*/
paddle.move(e.getX() - lastX, 0);
lastX = e.getX();
}
}
/*This is the "play" part of the program.
* Start by creating a ball in the middle of the screen.
* Then create the initial movement for the ball (randomly)
*/
//////////////////////////////////////////////// PLAY GAME //////////////////////////////////////
private void playGame(){
//PLAY THE GAME UNTIL YOU RUN OUT OF TURNS OR ALL THE BRICKS ARE GONE.
while(numTries < NTURNS && bricksRemoved < TOTAL_BRICKS){
createBall();
startBall();
/*This is the core of the game, move the ball, check for collision
*/
while(bricksRemoved < TOTAL_BRICKS){
moveBall();
//CHECKS COLLISION ON WALLS
checkForCollision();
//if the ball hits the bottom, increase the tries by 1 and get out of *this* loop.
if(ball.getY() + BALL_RADIUS > getHeight()){
remove(ball);
numTries++;
break;
}
//CHECKS COLLISION ON OBJECTS (PADDLE/BRICKS)
GObject collider = getCollidingObject();
//ONLY DO NEXT STEP IF THERE WAS AN OBJECT
if(collider != null){
//EITHER BOUNCE OFF PADDLE OR BOUNCE AND REMOVE BRICK.
checkIfBrickOrPaddle(collider);
}
//pause the ball movement for a time determined by the DELAY method
pause(Delay(DELAY_START));
}
}
}
private void createBall() {
int x = getWidth() / 2 - (BALL_RADIUS / 2);
int y = getHeight() / 2 - (BALL_RADIUS / 2);
ball = new GOval(x, y, BALL_RADIUS, BALL_RADIUS);
ball.setFilled(true);
add(ball);
}
//THIS IS DONE SO THAT THERE'S A RANDOM SIDE AND ANGLE CHOSEN AT THE BEGINING.
private void startBall(){
vx = rgen.nextDouble(1.0, 3.0);
vy = 3;
if (rgen.nextBoolean(0.5)) vx = -vx;
}
private void moveBall(){
ball.move(vx, vy);
}
//check for a collision,lots of "if" scenarios
//(if hits wall/if hits paddle/ if hits brick/ if hits bottom)
private void checkForCollision(){
//condition for if the ball touches the bottom
if (ball.getY() + BALL_RADIUS >= getHeight()) vy = -vy;
//condition if the ball bounces off the left hand side
if(ball.getX() <= 0) vx = -vx;
//condition if the ball bounces off the top
if(ball.getY() <= 0) vy = -vy;
//condition if the ball bounces off the right hand side
if(ball.getX() + BALL_RADIUS >= getWidth()) vx = -vx;
}
//return an object at the specific location, if there is no object, return null.
private GObject getCollidingObject(){
/*SEE COMMENT IN THE IVARS SECTION*/
//IF THERE'S AN ELEMENT AT THE TOP LEFT LOCATION
if(getElementAt(ball.getX(), ball.getY()) != null)
return(getElementAt(ball.getX(), ball.getY()));
//IF THERE'S AN ELEMENT AT THE TOP RIGHT LOCATION
else if(getElementAt(ball.getX() + BALL_RADIUS, ball.getY()) != null)
return(getElementAt(ball.getX() + BALL_RADIUS, ball.getY()));
//IF THERE'S AN ELEMENT AT THE BOTTOM LEFT LOCATION
else if(getElementAt(ball.getX() + BALL_RADIUS, ball.getY() + BALL_RADIUS) != null)
return(getElementAt(ball.getX() + BALL_RADIUS, ball.getY() + BALL_RADIUS));
//IF THERE'S AN ELEMENT AT THE BOTTOM RIGHT LOCATION
else if(getElementAt(ball.getX(), ball.getY() + BALL_RADIUS) != null)
return(getElementAt(ball.getX(), ball.getY() + BALL_RADIUS));
//ELSE RETURN NO OBJECT
else return null;
}
//THIS METHOD ONLY GETS CALLED IF THERE IS AN OBJECT (IT'S ENCAPSULATED IN AN IF-STATEMENT
private void checkIfBrickOrPaddle(GObject collider){
//IF THE BALL HITS THE PADDLE, REVERSE IT'S Y DIRECTION
if(collider == paddle){
bounceClip.play();
numHits++;
vy = -vy;
}else if(collider == paddle) {
bounceClip.play();
numHits++;
vy = -vy;
}
//ONLY OTHER OBJECT IS THE BRICKS, IF THE BALL HITS ANY OTHER OBJECT BUT THE PADDLE, REMOVE IT AND CHANGE IT'S Y DIRECTION
else if (collider != paddle) {
bounceClip.play();
remove(collider);
bricksRemoved++;
vy = -vy;
}
}
//TAKES IN THE VALUE DELAY_START, AND USES THAT COPY TO SPEED UP THE BALL AFTER SLOW_BALL TIMES.
private int Delay(int delay){
if(numHits < SLOW_BALL){
return delay;
}else return delay / 2;
}
//////////////////////////////////////////////*private instance Variables*///////////////////////////////
/**create a ball*/
private GOval ball;
/**The next 4 IVARS define the 4 corners of the ball*/
/*FOR SOME REASON I COULDN'T USE A GPoint. SO I SWITCHED THE PARAMETERS ABOVE...DON'T KNOW WHY, WOULND'T
* INITIALIZE WHEN USING GPOINT, SAID THERE WAS A NULL EXCEPTION ERROR, all I did was take out the parameters and
* plug them in above to fix. The getElementAt() method supports a GPoint :-/
*/
// private GPoint topLeft = new GPoint();
// private GPoint topRight = new GPoint();
// private GPoint bottomRight = new GPoint();
// private GPoint bottomLeft = new GPoint();
/**Initialize the velocity of the ball*/
private double vx, vy;
/**create a random Generator to make the ball start off different each time*/
private RandomGenerator rgen = RandomGenerator.getInstance();
/**Create the paddle*/
private GRect paddle;
/**The starting X and Y location of the paddle*/
private int paddleStartX;
private int paddleStartY;
/**A counter for the bricks that have been removed*/
private int bricksRemoved = 0;
/** a counter for the number of tries the player has taken*/
private int numTries = 0;
/**a counter for the number of times the ball hits the paddle*/
private int numHits = 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment