Stanford CS106A Breakout Code


/ Published in: Java
Save to your folder(s)

This is my code for the "Breakout" assignment from Stanford's CS106A class, which is available free online.

I went a little above and beyond the original assignment, which has caused some issues.

The program works pretty well for the most part, but at higher velocities, the ball starts to "skip" some of the bricks. I have no idea how to fix this or if it is possible. The other bug I've noticed is when the ball hits the paddle in a way that causes it to reverse direction (see code). If this happens too close to the wall, the ball gets "stuck."


Copy this code and paste it in your HTML
  1. package graphicShit;
  2.  
  3. import acm.graphics.*;
  4. import acm.program.*;
  5. import acm.util.*;
  6. import java.applet.*;
  7. import java.awt.*;
  8. import java.awt.event.*;
  9.  
  10. public class Breakout extends GraphicsProgram{
  11.  
  12. /** Width and height of application window in pixels */
  13. public static final int APPLICATION_WIDTH = 400;
  14. public static final int APPLICATION_HEIGHT = 600;
  15. /** Dimensions of game board (usually the same) */
  16. private static final int WIDTH = APPLICATION_WIDTH;
  17. private static final int HEIGHT = APPLICATION_HEIGHT;
  18. /** Dimensions of the paddle */
  19. private static final int PADDLE_WIDTH = 60;
  20. private static final int PADDLE_HEIGHT = 10;
  21. /** Offset of the paddle up from the bottom */
  22. private static final int PADDLE_Y_OFFSET = 30;
  23. /** Number of bricks per row */
  24. private static final int NBRICKS_PER_ROW = 10;
  25. /** Number of rows of bricks */
  26. private static final int NBRICK_ROWS = 10;
  27. /** Separation between bricks */
  28. private static final int BRICK_SEP = 4;
  29. /** Width of a brick */
  30. private static final int BRICK_WIDTH =
  31. (WIDTH - (NBRICKS_PER_ROW - 1) * BRICK_SEP) / NBRICKS_PER_ROW;
  32. /** Height of a brick */
  33. private static final int BRICK_HEIGHT = 8;
  34. /** Radius of the ball in pixels */
  35. private static final int BALL_RADIUS = 10;
  36. /** Offset of the top brick row from the top */
  37. private static final int BRICK_Y_OFFSET = 70;
  38. /** Number of turns */
  39. private static final int NTURNS = 3;
  40. /** Animation delay or pause time between ball moves */
  41. private static final int DELAY = 50;
  42.  
  43. public void run() {
  44. numberOfTurns = NTURNS;
  45. while(numberOfTurns > 0) {
  46. setup();
  47. play();
  48. removeAll();
  49. numberOfTurns --;
  50. }
  51. GLabel gameOver = new GLabel("GAME OVER");
  52. gameOver.setLocation((WIDTH - gameOver.getWidth()) / 2, HEIGHT / 2);
  53. remove(youLose);
  54. remove(youWin);
  55. add(gameOver);
  56. }
  57.  
  58. private void setup() {
  59. addMouseListeners();
  60. createBricks();
  61. createPaddle();
  62. }
  63.  
  64. private void createBricks() {
  65. double xColumnOne = (WIDTH - ((NBRICKS_PER_ROW - 1) * BRICK_SEP + (NBRICKS_PER_ROW * BRICK_WIDTH)));
  66. double yRow = BRICK_Y_OFFSET;
  67. for (int i = 0; i < NBRICK_ROWS; i++) {
  68. for (int j = 0; j < NBRICKS_PER_ROW; j++) {
  69. GRect brick = new GRect (xColumnOne, yRow, BRICK_WIDTH, BRICK_HEIGHT);
  70. /* Sets row colors to the following standards: "The color of the bricks remain constant for two rows and run in the following
  71.   rainbow-like sequence: RED, ORANGE, YELLOW, GREEN, CYAN" */
  72. if (i == 0 || i == 1) {
  73. brick.setColor(Color.RED);
  74. } else if (i == 2 || i == 3) {
  75. brick.setColor(Color.ORANGE);
  76. } else if (i == 4 || i == 5) {
  77. brick.setColor(Color.YELLOW);
  78. } else if (i == 6 || i == 7) {
  79. brick.setColor(Color.GREEN);
  80. } else if (i == 8 || i == 9) {
  81. brick.setColor(Color.CYAN);
  82. }
  83. brick.setFilled(true);
  84. add(brick);
  85. xColumnOne += (BRICK_WIDTH + BRICK_SEP);
  86. }
  87. xColumnOne = (WIDTH - ((NBRICKS_PER_ROW - 1) * BRICK_SEP + (NBRICKS_PER_ROW * BRICK_WIDTH)));
  88. yRow += (BRICK_HEIGHT + BRICK_SEP);
  89. }
  90. }
  91. /* Creates paddle and sets starting location to center of canvas. */
  92. private void createPaddle() {
  93. paddle = new GRect ((WIDTH - PADDLE_WIDTH)/2, HEIGHT - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT);
  94. paddle.setFilled(true);
  95. add(paddle);
  96. }
  97. /* Sets paddle to move with mouse cursor, stopping before it exits the canvas. */
  98. public void mouseMoved(MouseEvent e) {
  99. double paddleX = e.getX();
  100. double paddleY = HEIGHT - PADDLE_Y_OFFSET;
  101. paddle.setLocation(paddleX, paddleY);
  102. if ((paddleX + PADDLE_WIDTH) > WIDTH) {
  103. paddle.setLocation((WIDTH - PADDLE_WIDTH), paddleY);
  104. }
  105. }
  106.  
  107. private void play() {
  108. createBall();
  109. waitForClick();
  110. /* Y-velocity of ball */
  111. vy = 5.0;
  112. /* X-velocity of ball */
  113. vx = rgen.nextDouble(1.0, 5.0);
  114. if (rgen.nextBoolean(0.5)) vx = -vx;
  115. int bricksRemaining = (NBRICK_ROWS * NBRICKS_PER_ROW);
  116. youWin = new GLabel("You Win!");
  117. youWin.setLocation((WIDTH - youWin.getWidth()) / 2, HEIGHT / 2);
  118. youLose = new GLabel("You Lose :(");
  119. youLose.setLocation((WIDTH - youLose.getWidth()) / 2, HEIGHT / 2);
  120. while (true) {
  121. moveBall();
  122. checkForWallCollision();
  123. GObject collider = getCollidingObject();
  124. /*Increases velocity in both directions by 0.15 each time the ball collides with the paddle. */
  125. if (collider != null && collider == paddle) {
  126. vy = vy + 0.15;
  127. vx = vx + 0.15;
  128. /* If the ball hits the first fifth of the paddle while traveling in a positive x-direction,
  129.   * or the last fifth of the paddle while traveling in a negative x-direction, x-velocity is doubled.
  130.   * If the inverse of either of these is true, the ball will bounce in the opposite x-direction as it was received,
  131.   * and x-velocity is halved.
  132.   */
  133. if (vx > 0 && ball.getX() < (paddle.getX() + (PADDLE_WIDTH * 0.2))) {
  134. vx = 0.5 * vx;
  135. vx = -vx;
  136. vy = -vy;
  137. } else if (vx < 0 && ball.getX() < (paddle.getX() + (PADDLE_WIDTH * 0.2))) {
  138. vx = 2 * vx;
  139. vy = -vy;
  140. } else if (vx < 0 && ball.getX() > (paddle.getX() + (PADDLE_WIDTH * 0.8))) {
  141. vx = 0.5 * vx;
  142. vx = -vx;
  143. vy = -vy;
  144. } else if (vx > 0 && ball.getX() > (paddle.getX() + (PADDLE_WIDTH * 0.8))) {
  145. vx = 2 * vx;
  146. vy = -vy;
  147. } else vy = -vy;
  148. }
  149. if (collider != null && collider != paddle) {
  150. remove(collider);
  151. vy = -vy;
  152. bricksRemaining --;
  153. }
  154. if (ball.getY() + 2 * BALL_RADIUS > HEIGHT) {
  155. add(youLose);
  156. remove(ball);
  157. break;
  158. }
  159. if (bricksRemaining == 0) {
  160. add(youWin);
  161. remove(ball);
  162. break;
  163. }
  164. pause(DELAY);
  165. }
  166. waitForClick();
  167. remove(youLose);
  168. remove(youWin);
  169. }
  170.  
  171. private void createBall(){
  172. ball = new GOval ((WIDTH - (2 * BALL_RADIUS)) / 2, (HEIGHT - (2 * BALL_RADIUS)) / 2, 2 * BALL_RADIUS, 2 * BALL_RADIUS);
  173. ball.setFilled(true);
  174. add(ball);
  175. }
  176.  
  177. private void moveBall() {
  178. ball.move(vx,vy);
  179. }
  180.  
  181. private void checkForWallCollision() {
  182. if (ball.getX() + 2 * BALL_RADIUS > WIDTH || ball.getX() < 0) {
  183. vx = -vx;
  184. }
  185. if (ball.getY() < 0) {
  186. vy = -vy;
  187. }
  188. }
  189.  
  190. /* Checks for ball collisions with objects other than the walls, using the four points of the surrounding rectangle as "check" points. */
  191. private GObject getCollidingObject() {
  192. if (getElementAt(ball.getX(), ball.getY()) != null) {
  193. collidingObject = getElementAt(ball.getX(), ball.getY());
  194. } else if (getElementAt(ball.getX() + (2 * BALL_RADIUS), ball.getY()) != null) {
  195. collidingObject = getElementAt(ball.getX() + (2 * BALL_RADIUS), ball.getY());
  196. } else if (getElementAt(ball.getX(), ball.getY() + (2 * BALL_RADIUS)) != null) {
  197. collidingObject = getElementAt(ball.getX(), ball.getY() + (2 * BALL_RADIUS));
  198. } else if (getElementAt(ball.getX() + (2 * BALL_RADIUS), ball.getY() + (2 * BALL_RADIUS)) != null) {
  199. collidingObject = getElementAt(ball.getX() + (2 * BALL_RADIUS), ball.getY() + (2 * BALL_RADIUS));
  200. } else {
  201. collidingObject = null;
  202. }
  203.  
  204. return collidingObject;
  205.  
  206. }
  207.  
  208. private GLabel youWin;
  209.  
  210. private GLabel youLose;
  211.  
  212. private int numberOfTurns;
  213.  
  214. private GObject collidingObject;
  215.  
  216. private GRect paddle;
  217.  
  218. private GOval ball;
  219.  
  220. private double vx, vy;
  221.  
  222. private RandomGenerator rgen = RandomGenerator.getInstance();
  223.  
  224. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.