Smash It!
Brick Breaker
Bounce a ball off your paddle to smash a wall of bricks. You will learn 2D arrays, bounce physics, mouse-free paddle control and lives, the classic arcade formula, in pure Java.
Bounce a ball off your paddle to smash a wall of bricks. You will learn 2D arrays, bounce physics, mouse-free paddle control and lives, the classic arcade formula, in pure Java.
Create a fresh project with the game-loop pattern from Episode 1 ready to go.
public class GamePanel extends JPanel
implements ActionListener, KeyListener {
Timer timer = new Timer(16, this);
public GamePanel() {
setPreferredSize(new Dimension(700, 520));
setBackground(new Color(15, 18, 32));
addKeyListener(this);
setFocusable(true);
timer.start();
}
public void actionPerformed(ActionEvent e) { update(); repaint(); }
void update() {}
}public class GamePanel extends JPanel
implements ActionListener, KeyListener {
Timer timer = new Timer(16, this);
public GamePanel() {
setPreferredSize(new Dimension(700, 520));
setBackground(new Color(15, 18, 32));
addKeyListener(this);
setFocusable(true);
timer.start();
}
public void actionPerformed(ActionEvent e) { update(); repaint(); }
void update() {}
}
Draw a paddle that moves smoothly left/right and cannot leave the screen.
int padX = 295, padW = 110, padH = 14;
boolean leftHeld, rightHeld;
void update() {
if (leftHeld) padX -= 7;
if (rightHeld) padX += 7;
padX = Math.max(0, Math.min(700 - padW, padX));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(255, 119, 0));
g.fillRoundRect(padX, 470, padW, padH, 8, 8);
} int padX = 295, padW = 110, padH = 14;
boolean leftHeld, rightHeld;
void update() {
if (leftHeld) padX -= 7;
if (rightHeld) padX += 7;
padX = Math.max(0, Math.min(700 - padW, padX));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(255, 119, 0));
g.fillRoundRect(padX, 470, padW, padH, 8, 8);
}
padX = Math.padX = Math.(0, Math.(0, Math.(700 - padW, padX));(700 - padW, padX));
Get a ball bouncing around the arena and rebounding off your paddle.
int bx = 340, by = 300, bs = 14;
int bvx = 4, bvy = -4;
void update() {
// ... paddle code ...
bx += bvx; by += bvy;
if (bx <= 0 || bx + bs >= 700) bvx = -bvx; // side walls
if (by <= 0) bvy = -bvy; // ceiling
Rectangle ball = new Rectangle(bx, by, bs, bs);
Rectangle pad = new Rectangle(padX, 470, padW, padH);
if (ball.intersects(pad) && bvy > 0) {
bvy = -bvy;
int hit = (bx + bs/2) - (padX + padW/2);
bvx = hit / 12; // steer with the paddle!
if (bvx == 0) bvx = 1;
}
} int bx = 340, by = 300, bs = 14;
int bvx = 4, bvy = -4;
void update() {
// ... paddle code ...
bx += bvx; by += bvy;
if (bx <= 0 || bx + bs >= 700) bvx = -bvx; // side walls
if (by <= 0) bvy = -bvy; // ceiling
Rectangle ball = new Rectangle(bx, by, bs, bs);
Rectangle pad = new Rectangle(padX, 470, padW, padH);
if (ball.intersects(pad) && bvy > 0) {
bvy = -bvy;
int hit = (bx + bs/2) - (padX + padW/2);
bvx = hit / 12; // steer with the paddle!
if (bvx == 0) bvx = 1;
}
}
Fill the top of the screen with a colourful wall of bricks stored in a 2D array.
boolean[][] bricks = new boolean[5][10];
Color[] rowColors = { new Color(248,113,113), new Color(251,146,60),
new Color(250,204,21), new Color(74,222,128), new Color(96,165,250) };
// in constructor:
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
bricks[r][c] = true;
// in paintComponent:
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
if (bricks[r][c]) {
g.setColor(rowColors[r]);
g.fillRect(c*68 + 12, r*26 + 50, 64, 22);
} boolean[][] bricks = new boolean[5][10];
Color[] rowColors = { new Color(248,113,113), new Color(251,146,60),
new Color(250,204,21), new Color(74,222,128), new Color(96,165,250) };
// in constructor:
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
bricks[r][c] = true;
// in paintComponent:
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
if (bricks[r][c]) {
g.setColor(rowColors[r]);
g.fillRect(c*68 + 12, r*26 + 50, 64, 22);
}
for (int r = 0; r < for (int r = 0; r < ; r++)
for (int c = 0; c < ; r++)
for (int c = 0; c < ; c++)
bricks[r][c] = ; c++)
bricks[r][c] = ;;
Make the ball destroy bricks it touches, bounce off them, and rack up score.
int score = 0;
outer:
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
if (bricks[r][c]) {
Rectangle brick = new Rectangle(c*68 + 12, r*26 + 50, 64, 22);
if (ball.intersects(brick)) {
bricks[r][c] = false;
score += (5 - r) * 10;
bvy = -bvy;
break outer; // one brick per frame
}
} int score = 0;
outer:
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
if (bricks[r][c]) {
Rectangle brick = new Rectangle(c*68 + 12, r*26 + 50, 64, 22);
if (ball.intersects(brick)) {
bricks[r][c] = false;
score += (5 - r) * 10;
bvy = -bvy;
break outer; // one brick per frame
}
}
Finish the game: lose a life when the ball drops, win when every brick is gone.
int lives = 3;
boolean gameOver = false, won = false;
void update() {
if (gameOver || won) return; // freeze the game
// ... all previous code ...
if (by > 520) { // ball lost!
lives--;
bx = 340; by = 300; bvx = 4; bvy = -4;
if (lives == 0) gameOver = true;
}
int alive = 0;
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
if (bricks[r][c]) alive++;
if (alive == 0) won = true;
} int lives = 3;
boolean gameOver = false, won = false;
void update() {
if (gameOver || won) return; // freeze the game
// ... all previous code ...
if (by > 520) { // ball lost!
lives--;
bx = 340; by = 300; bvx = 4; bvy = -4;
if (lives == 0) gameOver = true;
}
int alive = 0;
for (int r = 0; r < 5; r++)
for (int c = 0; c < 10; c++)
if (bricks[r][c]) alive++;
if (alive == 0) won = true;
}
if (by > 520) {
livesif (by > 520) {
lives;
bx = 340; by = 300;
if (lives == ;
bx = 340; by = 300;
if (lives == ) gameOver = ) gameOver = ;
};
}
A clamped paddle, a bouncing ball with paddle-spin steering, a colour-banded brick wall, break-and-score, three lives and win/lose screens , one runnable file. Save as Main.javaMain.java, run javac Main.java && java Mainjavac Main.java && java Main, and play. Stuck? Compare your file against this.
// Complete Java Breakout. Run with: javac Main.java then java Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Brick Breaker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class GamePanel extends JPanel implements ActionListener, KeyListener {
Timer timer = new Timer(16, this);
Color[] rowColours = {
new Color(248,113,113), new Color(251,146,60), new Color(250,204,21),
new Color(74,222,128), new Color(96,165,250)
};
int padX = 295, padW = 110, padH = 14;
int bx = 340, by = 300, bs = 14, bvx = 4, bvy = -4;
boolean leftHeld, rightHeld;
int score = 0, lives = 3;
String state = "PLAY"; // PLAY / WIN / LOSE
List<Rectangle> bricks = new ArrayList<>();
public GamePanel() {
setPreferredSize(new Dimension(700, 520));
setBackground(new Color(15, 18, 32));
addKeyListener(this);
setFocusable(true);
buildBricks();
timer.start();
}
void buildBricks() {
bricks.clear();
for (int row = 0; row < 5; row++)
for (int col = 0; col < 10; col++)
bricks.add(new Rectangle(col*68 + 12, row*26 + 50, 64, 22));
}
int rowOf(Rectangle b) { return (b.y - 50) / 26; }
void serve() { bx = 343; by = 300; bvx = 4; bvy = -4; }
void reset() {
padX = 295; score = 0; lives = 3; state = "PLAY";
buildBricks(); serve();
}
public void actionPerformed(ActionEvent e) { update(); repaint(); }
void update() {
if (!state.equals("PLAY")) return;
if (leftHeld) padX -= 7;
if (rightHeld) padX += 7;
padX = Math.max(0, Math.min(700 - padW, padX));
bx += bvx; by += bvy;
if (bx <= 0 || bx + bs >= 700) bvx = -bvx;
if (by <= 0) bvy = -bvy;
Rectangle ball = new Rectangle(bx, by, bs, bs);
Rectangle pad = new Rectangle(padX, 470, padW, padH);
if (ball.intersects(pad) && bvy > 0) {
bvy = -bvy;
int hit = (bx + bs/2) - (padX + padW/2);
bvx = hit / 12;
if (bvx == 0) bvx = 1;
}
for (int i = 0; i < bricks.size(); i++) {
Rectangle brick = bricks.get(i);
if (ball.intersects(brick)) {
score += (5 - rowOf(brick)) * 10;
bvy = -bvy;
bricks.remove(i);
break;
}
}
if (by - bs > 520) {
lives--;
if (lives <= 0) state = "LOSE";
else serve();
}
if (bricks.isEmpty()) state = "WIN";
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Rectangle b : bricks) {
g.setColor(rowColours[rowOf(b) % rowColours.length]);
g.fillRect(b.x, b.y, b.width, b.height);
}
g.setColor(new Color(255, 119, 0));
g.fillRoundRect(padX, 470, padW, padH, 8, 8);
g.setColor(Color.WHITE);
g.fillOval(bx, by, bs, bs);
g.setFont(new Font("Arial", Font.BOLD, 18));
g.drawString("Score: " + score, 16, 26);
g.drawString("Lives: " + Math.max(0, lives), 590, 26);
if (!state.equals("PLAY")) {
g.setFont(new Font("Arial", Font.BOLD, 48));
g.drawString(state.equals("WIN") ? "YOU WIN!" : "GAME OVER", 200, 250);
g.setFont(new Font("Arial", Font.PLAIN, 18));
g.drawString("Press R to play again", 270, 290);
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) leftHeld = true;
if (e.getKeyCode() == KeyEvent.VK_RIGHT) rightHeld = true;
if (e.getKeyCode() == KeyEvent.VK_R && !state.equals("PLAY")) reset();
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) leftHeld = false;
if (e.getKeyCode() == KeyEvent.VK_RIGHT) rightHeld = false;
}
public void keyTyped(KeyEvent e) {}
}// Complete Java Breakout. Run with: javac Main.java then java Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Brick Breaker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GamePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class GamePanel extends JPanel implements ActionListener, KeyListener {
Timer timer = new Timer(16, this);
Color[] rowColours = {
new Color(248,113,113), new Color(251,146,60), new Color(250,204,21),
new Color(74,222,128), new Color(96,165,250)
};
int padX = 295, padW = 110, padH = 14;
int bx = 340, by = 300, bs = 14, bvx = 4, bvy = -4;
boolean leftHeld, rightHeld;
int score = 0, lives = 3;
String state = "PLAY"; // PLAY / WIN / LOSE
List<Rectangle> bricks = new ArrayList<>();
public GamePanel() {
setPreferredSize(new Dimension(700, 520));
setBackground(new Color(15, 18, 32));
addKeyListener(this);
setFocusable(true);
buildBricks();
timer.start();
}
void buildBricks() {
bricks.clear();
for (int row = 0; row < 5; row++)
for (int col = 0; col < 10; col++)
bricks.add(new Rectangle(col*68 + 12, row*26 + 50, 64, 22));
}
int rowOf(Rectangle b) { return (b.y - 50) / 26; }
void serve() { bx = 343; by = 300; bvx = 4; bvy = -4; }
void reset() {
padX = 295; score = 0; lives = 3; state = "PLAY";
buildBricks(); serve();
}
public void actionPerformed(ActionEvent e) { update(); repaint(); }
void update() {
if (!state.equals("PLAY")) return;
if (leftHeld) padX -= 7;
if (rightHeld) padX += 7;
padX = Math.max(0, Math.min(700 - padW, padX));
bx += bvx; by += bvy;
if (bx <= 0 || bx + bs >= 700) bvx = -bvx;
if (by <= 0) bvy = -bvy;
Rectangle ball = new Rectangle(bx, by, bs, bs);
Rectangle pad = new Rectangle(padX, 470, padW, padH);
if (ball.intersects(pad) && bvy > 0) {
bvy = -bvy;
int hit = (bx + bs/2) - (padX + padW/2);
bvx = hit / 12;
if (bvx == 0) bvx = 1;
}
for (int i = 0; i < bricks.size(); i++) {
Rectangle brick = bricks.get(i);
if (ball.intersects(brick)) {
score += (5 - rowOf(brick)) * 10;
bvy = -bvy;
bricks.remove(i);
break;
}
}
if (by - bs > 520) {
lives--;
if (lives <= 0) state = "LOSE";
else serve();
}
if (bricks.isEmpty()) state = "WIN";
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Rectangle b : bricks) {
g.setColor(rowColours[rowOf(b) % rowColours.length]);
g.fillRect(b.x, b.y, b.width, b.height);
}
g.setColor(new Color(255, 119, 0));
g.fillRoundRect(padX, 470, padW, padH, 8, 8);
g.setColor(Color.WHITE);
g.fillOval(bx, by, bs, bs);
g.setFont(new Font("Arial", Font.BOLD, 18));
g.drawString("Score: " + score, 16, 26);
g.drawString("Lives: " + Math.max(0, lives), 590, 26);
if (!state.equals("PLAY")) {
g.setFont(new Font("Arial", Font.BOLD, 48));
g.drawString(state.equals("WIN") ? "YOU WIN!" : "GAME OVER", 200, 250);
g.setFont(new Font("Arial", Font.PLAIN, 18));
g.drawString("Press R to play again", 270, 290);
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) leftHeld = true;
if (e.getKeyCode() == KeyEvent.VK_RIGHT) rightHeld = true;
if (e.getKeyCode() == KeyEvent.VK_R && !state.equals("PLAY")) reset();
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) leftHeld = false;
if (e.getKeyCode() == KeyEvent.VK_RIGHT) rightHeld = false;
}
public void keyTyped(KeyEvent e) {}
}