Coders Packet

Brick Breaker game using Java

By KHYATI MADDALI

In this guide, we will learn to develop a simple and fun- Brick Breaker game in Java.

As we know, Java is one of the most widely used programming languages for making games due to the versatility and the huge amount of open-source material made available to a programmer when using this language.

To complete this brick breaker game, let's make 2 components: The methods used in the main class and the execution of gameplay. let’s start developing our main class! First, let’s import the libraries we need. 

import javax.swing.JFrame;

then, let’s create a class named ‘Main_class’ since this will be the main class in our source code. After creating the main class, you also see that the main method like public static void main(String[] args)are available. We'll define an object that will have the properties of JFrame. And later, we have set the properties of this frame using methods such as setBounds(), setTitle(), setResizable() etc. we've added the methods and functions of the Gameplay class in the lines below:

Gameplay game = new Gameplay();
obj.add(game);

now, let’s see the whole code for the main class!

Main class code:

import javax.swing.JFrame; 
public class Main_class {

  public static void main(String[] args) {
    JFrame obj = new JFrame();
    Gameplay game = new Gameplay();
    obj.setBounds(10,10,700,600);
    obj.setTitle("BRICK BREAKER");
    obj.setResizable(false);
    obj.setVisible(true);
    obj.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    obj.add(game);
    
    
  }

}

When you run the code as of now, you'll see only a frame with the title in it. Like so:

let’s move onto making the game class! we've created a class named ‘Gameplay’. We've imported the following libraries:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.Timer; 
import javax.swing.JPanel;

since we need a container that can store and organize different components, let's use JPanel which is found in the Java Swing Package. We've ensured that this class inherits the attributes of the JPanel container by using the keyword ‘extends’. We've also ensured to implement the KeyListener and ActionListener interfaces by using the ‘implements’ keyword. KeyListener in Java takes care of all the operations or any actions related to the keyboard such as pressing the left button to move and so on. The ActionListener interface in Java is automatically notified when you click or interact with a defined object within the bounds of the frame.

After all, this we have initialized and defined all the parameters which will be required to play and create this game.

public class Gameplay extends JPanel implements KeyListener, ActionListener {
  private boolean play = false;
  private int score =0;
  private int totalbricks = 21;
  private Timer time;
  private int delay = 5;
  private int player1= 310;
  private int ballposx = 120;
  private int ballposy = 350;
  private int ballxdir = -1;
  private int ballydir = -2;
  
  private mapbrick map; 
}

we've used the setFocusable() method from the component class in Swing package to set certain components on focus. The setFocusTraversalKeysEnabled() helps you to decide whether or not to focus the traversal keys, and it only accepts Boolean parameters. Since we require a timer, we've also designed a code to create the timer.

time = new Timer(delay, this);
time.start();

the Timer class can be obtained from the javax.swing.Timer package. The public void paint(Graphics g)is a class that contains the paint interface from the Graphics class. it contains and describes the graphic environment in which the game is running. In this method, we have set the graphics of objects such as the background, ball, paddle, etc. Here, we've set the color of the background as black and we've made the frame rectangular. The fillRect() method requires 4 arguments (one x, one y coordinate, height, and width as arguments)

//background
    g.setColor(Color.black);
    g.fillRect(1,1, 692, 592);

We have enabled the draw() method to invoke the Graphics object.

//draw
    map.draw((Graphics2D)g);

next, we've set the color of the borders as well as the bounds of the rectangle that will be reachable to the ball to intercept when we actually play the game.

//borders
    g.setColor(Color.yellow);
    g.fillRect(0, 0, 3, 592);
    g.fillRect(0,0 , 692, 3);
    g.fillRect(691,0,3, 592);

here, we've set the font style, font size as well as color of the font. And we've used the drawString() method to display the score in the window.

//scores
    g.setColor(Color.white);
    g.setFont(new Font("Verdana", Font.BOLD, 25));
    g.drawString(""+score, 590, 30);

we've created the paddle as an object and we set the color of it. The paddle is a rectangular bar that intersects the ball when the player moves the keys accordingly. That is why we are using the fillRect() method.

//paddle
    g.setColor(Color.green);
    g.fillRect(player1, 550, 100,8);

lastly, we've designated the ball in the shape of the oval using the fillOval method. And we've set the color of the ball.

//ball 
    g.setColor(Color.yellow);
    g.fillOval(ballposx,ballposy, 20, 20);

in the game, we need to ensure that if all the bricks are broken and none are left, the game should terminate. So, how will do that? Let’s take a look at this:

if(totalbricks <=0) {
      play = false;
      ballxdir = 0;
      ballydir = 0;
      g.setColor(Color.red);
      g.setFont(new Font("Verdana", Font.BOLD, 35));
      g.drawString("YOU WON!", 190, 300);
      
      g.setFont(new Font("Verdana", Font.BOLD, 30));
      g.drawString("Press ENTER to restart",230, 350);
    }
    
    if (ballposy > 570) {
      play = false;
      ballxdir = 0;
      ballydir = 0;
      g.setColor(Color.red);
      g.setFont(new Font("Verdana", Font.BOLD, 35));
      g.drawString("GAME OVER!", 190, 300);
      
      g.setFont(new Font("Verdana", Font.BOLD, 30));
      g.drawString("Press ENTER to restart",230, 350);
      
    }

As you can see, we're using the if-else construct to find whether the player has lost or won the game. We've also included the option to press enter to restart the game. Next, let’s use the override method which allows the child class to facilitate a unique implementation of a method that is available in the parent classes. We are using the override the actionPerformed() method specifically invoke the method when you click on a specific object. To detect the intersection of 2 objects within the window of the game, we've created a structure using if -else and for loop to find the course of action according to the position and direction of the ball. We've used the labeling method with the break statement to exit the loop once a condition has been met. After breaking out of the loop, we used the repaint() method to redraw all the components again.

@Override
  public void actionPerformed(ActionEvent e) {
    time.start();
    if(play) {
      //detecting interstion of 2 objs
      if (new Rectangle(ballposx,ballposy, 20, 20).intersects(new Rectangle(player1, 550, 100, 8))) {
        ballydir = -ballydir; 
      }
      
      B: for (int i=0; i< map.map.length; i++) {
        for (int j=0; j<map.map[0].length; j++) {
          if (map.map[i][j]>0) {
            int brickx = j*map.brickwidth + 80;
            int bricky = i* map.brickheight + 50; 
            int brickwidth = map.brickwidth;
            int brickheight = map.brickheight;
            
            Rectangle rect = new Rectangle(brickx, bricky, brickwidth, brickheight);
            Rectangle ballrect = new Rectangle(ballposx, ballposy, 20, 20);
            Rectangle brickrect = rect; 
            
            if(ballrect.intersects(brickrect)) {
              map.setBrickval(0,i,j);
              totalbricks--;
              score += 10;
              
              if(ballposx + 19 <= brickrect.x || ballposx + 1 >= brickrect.x + brickrect.width) {
                ballxdir = - ballxdir;
              } else {
                ballydir = -ballydir; 
              }
              
              break B;
            }
          }
        }
      }
      ballposx += ballxdir;
      ballposy += ballydir;
      if (ballposx < 0) { 
        ballxdir = - ballxdir;
      }
      if (ballposy < 0) { 
        ballydir = - ballydir;
      }
      if (ballposx > 670) { 
        ballxdir = - ballxdir;
      }
      
    }
    repaint();
  }

We can keep these override methods as it is since we don’t have any functions that will require this implementation. However, removing will cause an error due to disruption of the method structure.

@Override
  public void keyTyped(KeyEvent e) {}
  
@Override
  public void keyReleased(KeyEvent e) {}

then, let’s construct the events and methods in the keypressed method using override. here, we have created an if-else loop to detect the use of the key that is used to enable motion. In the structure, it's ensured that if the keyPressed method detects that right key is being pressed, then the object moves to the right. Same for the left key. if enter is being pressed, the game will restart itself. we'll generate a new map and hence the use of repaint method.

@Override
  public void keyPressed(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
      if(player1 >= 600) {
        player1 = 600;
      } else {
        moveright();
      }
    }
    if(e.getKeyCode() == KeyEvent.VK_LEFT) {
      if(player1 <10) {
        player1 = 10;
      } else {
        moveleft();
      }
    }
    
    if(e.getKeyCode() == KeyEvent.VK_ENTER) {
      if (!play) {
        play = true;
        ballposx = 120;
        ballposy = 350;
        ballxdir = -1;
        ballydir = -2;
        player1 = 310;
        score = 0;
        totalbricks = 21;
        map = new mapbrick(3,7);
        
        repaint();
      };
      
    }
  }

  public void moveright() {
    play = true;
    player1+=20;
  }
  public void moveleft() {
    play = true;
    player1 -=20;
  }
  
}

let’s take a look at the complete code for the Gameplay class!

Game Play code:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.Timer; 
import javax.swing.JPanel;

public class Gameplay extends JPanel implements KeyListener, ActionListener {
  private boolean play = false;
  private int score =0;
  private int totalbricks = 21;
  private Timer time;
  private int delay = 5;
  private int player1= 310;
  private int ballposx = 120;
  private int ballposy = 350;
  private int ballxdir = -1;
  private int ballydir = -2;
  
  private mapbrick map; 
  
  public Gameplay() {
    map = new mapbrick (3,7);
    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);
    time = new Timer(delay, this);
    time.start();
  }
  
  public void paint(Graphics g) {
    //bg
    g.setColor(Color.black);
    g.fillRect(1,1, 692, 592);
    
    //draw
    map.draw((Graphics2D)g);
    
    //borders
    g.setColor(Color.yellow);
    g.fillRect(0, 0, 3, 592);
    g.fillRect(0,0 , 692, 3);
    g.fillRect(691,0,3, 592);
    
    //scores
    g.setColor(Color.white);
    g.setFont(new Font("Verdana", Font.BOLD, 25));
    g.drawString(""+score, 590, 30);
    //paddle
    g.setColor(Color.green);
    g.fillRect(player1, 550, 100,8);
    
    //ball 
    g.setColor(Color.yellow);
    g.fillOval(ballposx,ballposy, 20, 20);
    
    if(totalbricks <=0) {
      play = false;
      ballxdir = 0;
      ballydir = 0;
      g.setColor(Color.red);
      g.setFont(new Font("Verdana", Font.BOLD, 35));
      g.drawString("YOU WON!", 190, 300);
      
      g.setFont(new Font("Verdana", Font.BOLD, 30));
      g.drawString("Press ENTER to restart",230, 350);
    }
    
    if (ballposy > 570) {
      play = false;
      ballxdir = 0;
      ballydir = 0;
      g.setColor(Color.red);
      g.setFont(new Font("Verdana", Font.BOLD, 35));
      g.drawString("GAME OVER!", 190, 300);
      
      g.setFont(new Font("Verdana", Font.BOLD, 30));
      g.drawString("Press ENTER to restart",230, 350);
      
    }
    g.dispose();
  }
  @Override
  public void actionPerformed(ActionEvent e) {
    time.start();
    if(play) {
      //detecting interstion of 2 objs
      if (new Rectangle(ballposx,ballposy, 20, 20).intersects(new Rectangle(player1, 550, 100, 8))) {
        ballydir = -ballydir; 
      }
      
      B: for (int i=0; i< map.map.length; i++) {
        for (int j=0; j<map.map[0].length; j++) {
          if (map.map[i][j]>0) {
            int brickx = j*map.brickwidth + 80;
            int bricky = i* map.brickheight + 50; 
            int brickwidth = map.brickwidth;
            int brickheight = map.brickheight;
            
            Rectangle rect = new Rectangle(brickx, bricky, brickwidth, brickheight);
            Rectangle ballrect = new Rectangle(ballposx, ballposy, 20, 20);
            Rectangle brickrect = rect; 
            
            if(ballrect.intersects(brickrect)) {
              map.setBrickval(0,i,j);
              totalbricks--;
              score += 10;
              
              if(ballposx + 19 <= brickrect.x || ballposx + 1 >= brickrect.x + brickrect.width) {
                ballxdir = - ballxdir;
              } else {
                ballydir = -ballydir; 
              }
              
              break B;
            }
          }
        }
      }
      ballposx += ballxdir;
      ballposy += ballydir;
      if (ballposx < 0) { 
        ballxdir = - ballxdir;
      }
      if (ballposy < 0) { 
        ballydir = - ballydir;
      }
      if (ballposx > 670) { 
        ballxdir = - ballxdir;
      }
      
    }
    repaint();
  }

  @Override
  public void keyTyped(KeyEvent e) {}
  
  @Override
  public void keyReleased(KeyEvent e) {}

  @Override
  public void keyPressed(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
      if(player1 >= 600) {
        player1 = 600;
      } else {
        moveright();
      }
    }
    if(e.getKeyCode() == KeyEvent.VK_LEFT) {
      if(player1 <10) {
        player1 = 10;
      } else {
        moveleft();
      }
    }
    
    if(e.getKeyCode() == KeyEvent.VK_ENTER) {
      if (!play) {
        play = true;
        ballposx = 120;
        ballposy = 350;
        ballxdir = -1;
        ballydir = -2;
        player1 = 310;
        score = 0;
        totalbricks = 21;
        map = new mapbrick(3,7);
        
        repaint();
      };
      
    }
  }

  public void moveright() {
    play = true;
    player1+=20;
  }
  public void moveleft() {
    play = true;
    player1 -=20;
  }
  
}

Last, but it's time to develop the bricks! Like always, let’s first import the libraries or packages we need.

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;

Here, we’re also using the BasicStroke class in the awt package that is used to produce certain attributes along with the Graphics2D class such as dash, join cap, etc. let’s create a class named ‘mapbrick’. We'll be developing this class to contain objects and attributes such as brick width, brick height etc. we've also declared an array named ‘map’, which will contain all the bricks.

public int map[][];
public int brickwidth;
public int brickheight; 

so, inside the constructor named ‘mapbrick, we will receive the number of rows and columns that notify and help us understand about how many rows and columns should be generated in order to accommodate a given number of bricks.

public mapbrick (int row, int col) {
    map = new int [row][col];

    for (int i=0; i< map.length; i++) {
      for (int j=0; j<map[0].length ; j++) {
        map[i][j] = 1;
      }
    }
}

We have instantiated a 2D array with values of ‘row’ and ‘col’

map = new int [row][col];

after this, we have created a for loop to iterate through the number of rows and columns. Now, pay close attention to this line:

map[i][j] = 1;

the logic behind adding 1 inside each element of the 2D array is that it will notify us when the ball has not intersected with brick and it will show up in the panel. 

and now, we have set the width and height of the bricks that will appear on the screen with respect to the array elements.

brickwidth = 540/col;
brickheight = 150/row; 

since we have understood the logic behind the 2D array and the for loop, let’s bunch them together.

public mapbrick (int row, int col) {
    map = new int [row][col];

    for (int i=0; i< map.length; i++) {
      for (int j=0; j<map[0].length ; j++) {
        map[i][j] = 1;
      }
    }
    
    brickwidth = 540/col;
    brickheight = 150/row; 
  }

Once the constructor is called, we need to have a function for drawing those particular bricks. We will do that by using the draw() method. Also, note that the draw function should contain the Graphics2D object. When the draw function with Graphics objects is called the bricks will be drawn on the particular positions where there’s a value of 1 identified on the array.

To accomplish this, we will use the for loop to iterate once again. Within the for loop, we will need to check if the particular value is greater than not. if it is greater than zero, it creates the bricks. We have also set the color of the bricks and used the fillRect method to fill the rectangle at the particular positions.

public void draw(Graphics2D g) {
    for (int i=0; i< map.length; i++) {
      for (int j=0; j<map[0].length; j++) {
        if (map[i][j]> 0) {
          g.setColor(Color.white);
          g.fillRect(j*brickwidth + 80,  i*brickheight + 50,  brickwidth,  brickheight);
          
          //map border of bricks
          g.setStroke(new BasicStroke(3));
          g.setColor(Color.black);
          g.drawRect(j*brickwidth +80, i*brickheight + 50, brickwidth, brickheight);
          
        }
      }
    }
  }

Finally, we need to create a function that will give us a value when the ball intersects the brick. We know that the total number of bricks is 21. When the function value attains zero, the game will terminate and display a ‘ you won!’ message. The following code ensures that the value is accordingly interpreted by the 2D array too.

public void setBrickval(int val, int row, int col) {
    map[row][col] = val;
  }

now let’s put together all the bits of this class and look at the code:

Map-bricks code:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;

public class mapbrick {
  public int map[][];
  public int brickwidth;
  public int brickheight; 
  public mapbrick (int row, int col) {
    map = new int [row][col];
    for (int i=0; i< map.length; i++) {
      for (int j=0; j<map[0].length ; j++) {
        map[i][j] = 1;
      }
    }
    
    brickwidth = 540/col;
    brickheight = 150/row; 
  }
  
  public void draw(Graphics2D g) {
    for (int i=0; i< map.length; i++) {
      for (int j=0; j<map[0].length; j++) {
        if (map[i][j]> 0) {
          g.setColor(Color.white);
          g.fillRect(j*brickwidth + 80,  i*brickheight + 50,  brickwidth,  brickheight);
          
          //map border of bricks
          g.setStroke(new BasicStroke(3));
          g.setColor(Color.black);
          g.drawRect(j*brickwidth +80, i*brickheight + 50, brickwidth, brickheight);
          
        }
      }
    }
  }
  
  public void setBrickval(int val, int row, int col) {
    map[row][col] = val;
  }
}

And done! developing mini-projects in Java will help you build fluency in Java, and it tests your knowledge. All in all, Implementing and making games in Java is rewarding as well as a way to exercise your skills.

 

Download project

Reviews Report

Submitted by KHYATI MADDALI (khyatimaddali)

Download packets of source code on Coders Packet