
Today, we are going to learn how to build the classic Tetris Game in Java with Swing. The game requires the player to rotate and move falling Tetris pieces to an appropriate position so that the player can fill the entire row without a gap, once the row is filled it’s automatically cleared and the score increases. But, if the pieces reach the top, the game is over!
By creating this game we will learn the concepts like creating graphics with the help of Swing, game loops, and collision detection. Ultimately, we have a fully functional game with a beautiful UI.
Project Overview: Tetris Game in Java
Project Name: | Tetris Game in Java |
Abstract: | It’s a GUI-based project used with the swing library to organize all the elements that work under the Tetris Game. |
Language Used: | Java |
IDE: | VS Code |
Java version (Recommended): | Java SE 18.0. 2.1 |
Database: | None |
Type: | Desktop Application |
Recommended for: | Beginners of Java |
Time to build: | 1-2 hours |
What will you learn?
- Building the logic of the game with the help of conditionals and loops
- Learn the OOPs paradigm with the help of classes, objects, and inheritance
- Creating pleasing graphics with the help of Java Swing and Java AWT
Features:
- The player will be able to rotate the pieces left and right by pressing the up and down key
- The player will be able to move the pieces left and right by pressing the left and right key
- To quickly fall down a piece, the player can use the Space Bar key
- The score is updated when a row gets cleared
Complete Code for Tetris Game in Java
Create a folder for the project and a file in it named Tetris.java. Now, copy the below lines of code in the file. Comments are provided for explanation.
import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.util.*;import java.util.Timer;// this is class for all the shapes and their rotationsclass Shape {Tetrominoe pieceShape; // the shape of the pieceint coords[][]; // the coordinates of the pieceint[][][] coordsTable; // the coordinates of the piece in all its rotationspublic Shape() {initShape(); // initialize the shape}void initShape() {coords = new int[4][2]; // initialize the size of the piecesetShape(Tetrominoe.NoShape); // set the shape to NoShape}// set the shape of the piece to the given shape and set the coordinates of the piece to the coordinates of the given shapeprotected void setShape(Tetrominoe shape) {coordsTable = new int[][][] {{ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },{ { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } },{ { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } },{ { 0, -1 }, { 0, 0 }, { 0, 1 }, { 0, 2 } },{ { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } },{ { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } },{ { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } },{ { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }};for (int i = 0; i < 4 ; i++) { // for the no of rows in the shapefor (int j = 0; j < 2; ++j) { // for the no of columns in the shapecoords[i][j] = coordsTable[shape.ordinal()][i][j]; // set the coordinates of the piece to the coordinates of the given shape}}pieceShape = shape;}void setX(int index, int x) { coords[index][0] = x; } // set the x coordinate of the piecevoid setY(int index, int y) { coords[index][1] = y; } // set the y coordinate of the piecepublic int x(int index) { return coords[index][0]; } // get the x coordinate of the piecepublic int y(int index) { return coords[index][1]; } // get the y coordinate of the piecepublic Tetrominoe getShape() { return pieceShape; } // get the shape of the piece// set the shape of the piece to a random shape everytime a new piece is createdpublic void setRandomShape() {Random r = new Random(); // create a random objectint x = Math.abs(r.nextInt()) % 7 + 1; // get a random number between 1 and 7Tetrominoe[] values = Tetrominoe.values(); // get all the shapessetShape(values[x]); // set the shape of the piece to a random shape}// get the minimum x coordinate of the piecepublic int minX() {int m = coords[0][0];for (int i=0; i < 4; i++) { // for the no of rows in the shapem = Math.min(m, coords[i][0]); // get the minimum x coordinate of the piece}return m; // return the minimum x coordinate of the piece}// get the minimum y coordinate of the piecepublic int minY() {int m = coords[0][1];for (int i=0; i < 4; i++) { // for the no of rows in the shapem = Math.min(m, coords[i][1]); // get the minimum y coordinate of the piece}return m; // return the minimum y coordinate of the piece}// rotate the piece to the leftpublic Shape rotateLeft() {if (pieceShape == Tetrominoe.SquareShape) // if the shape is a square shapereturn this; // return the shape without rotationShape result = new Shape(); // create a new shaperesult.pieceShape = pieceShape; // set the shape of the new shape to the shape of the piecefor (int i = 0; i < 4; ++i) { // for the no of rows in the shaperesult.setX(i, y(i)); // set the x coordinate of the new shape to the y coordinate of the pieceresult.setY(i, -x(i)); // set the y coordinate of the new shape to the negative x coordinate of the piece}return result; // return the new shape}// rotate the piece to the rightpublic Shape rotateRight() {if (pieceShape == Tetrominoe.SquareShape) // if the shape is a square shapereturn this; // return the shape without rotationShape result = new Shape(); // create a new shaperesult.pieceShape = pieceShape; // set the shape of the new shape to the shape of the piecefor (int i = 0; i < 4; ++i) { // for the no of rows in the shaperesult.setX(i, -y(i)); // set the x coordinate of the new shape to the negative y coordinate of the pieceresult.setY(i, x(i)); // set the y coordinate of the new shape to the x coordinate of the piece}return result; // return the new shape}}enum Tetrominoe { NoShape, ZShape, SShape, LineShape,TShape, SquareShape, LShape, MirroredLShape };// this is the class for the boardclass Board extends JPanel {static final long serialVersionUID = 1L;final int BOARD_WIDTH = 10; // the width of the boardfinal int BOARD_HEIGHT = 22; // the height of the boardfinal int INITIAL_DELAY = 100; // the initial delay of the timerfinal int PERIOD_INTERVAL = 300; // the period interval of the timerTimer timer;boolean isFallingFinished = false; // check if the piece has finished fallingboolean isStarted = false; // check if the game has startedboolean isPaused = false; // check if the game is pausedint numLinesRemoved = 0; // the number of lines removedint curX = 0; // the current x coordinate of the pieceint curY = 0; // the current y coordinate of the pieceJLabel statusbar;Shape curPiece;Tetrominoe[] board;public Board(Tetris parent) {initBoard(parent);}// initialize the boardvoid initBoard(Tetris parent) {setFocusable(true);setBorder(BorderFactory.createLineBorder(Color.pink, 4));timer = new Timer();timer.scheduleAtFixedRate(new ScheduleTask(),INITIAL_DELAY, PERIOD_INTERVAL);curPiece = new Shape();statusbar = parent.getStatusBar();board = new Tetrominoe[BOARD_WIDTH * BOARD_HEIGHT];addKeyListener(new TAdapter());clearBoard();}int squareWidth() {return (int) getSize().getWidth() / BOARD_WIDTH;}int squareHeight() {return (int) getSize().getHeight() / BOARD_HEIGHT;}Tetrominoe shapeAt(int x, int y) {return board[(y * BOARD_WIDTH) + x];}public void start() {isStarted = true;clearBoard();newPiece();}void pause() {if (!isStarted) {return;}isPaused = !isPaused;if (isPaused) {statusbar.setText("Paused");} else {statusbar.setText(String.valueOf(numLinesRemoved));}}// draw the boardvoid doDrawing(Graphics g) {Dimension size = getSize();int boardTop = (int) size.getHeight() - BOARD_HEIGHT * squareHeight();for (int i = 0; i < BOARD_HEIGHT; ++i) {for (int j = 0; j < BOARD_WIDTH; ++j) {Tetrominoe shape = shapeAt(j, BOARD_HEIGHT - i - 1);if (shape != Tetrominoe.NoShape) {drawSquare(g, 0 + j * squareWidth(),boardTop + i * squareHeight(), shape);}}}if (curPiece.getShape() != Tetrominoe.NoShape) { // if the shape of the piece is not a no shapefor (int i = 0; i < 4; ++i) { // for the no of rows in the shapeint x = curX + curPiece.x(i); // get the x coordinate of the pieceint y = curY - curPiece.y(i); // get the y coordinate of the piecedrawSquare(g, 0 + x * squareWidth(),boardTop + (BOARD_HEIGHT - y - 1) * squareHeight(),curPiece.getShape()); // draw the piece on the board}}}// draw the square on the board@Overridepublic void paintComponent(Graphics g) {super.paintComponent(g);doDrawing(g);}// draw the new coordinates of the piece while falling downvoid dropDown() {int newY = curY; // set the new y coordinate to the current y coordinatewhile (newY > 0) { // while the new y coordinate is greater than 0if (!tryMove(curPiece, curX, newY - 1)) { // if the piece cannot move to the new coordinatesbreak; // break the loop}--newY; // decrement the new y coordinate}pieceDropped(); // call the piece dropped method}// draw the new coordinates of the piece while movingvoid oneLineDown() {if (!tryMove(curPiece, curX, curY - 1)) {pieceDropped();}}void clearBoard() {for (int i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; ++i) {board[i] = Tetrominoe.NoShape;}}void pieceDropped() {for (int i = 0; i < 4; ++i) {int x = curX + curPiece.x(i);int y = curY - curPiece.y(i);board[(y * BOARD_WIDTH) + x] = curPiece.getShape();}removeFullLines(); // remove the full linesif (!isFallingFinished) { // if the piece has not finished fallingnewPiece(); // create a new piece}}// create a new piecevoid newPiece() {curPiece.setRandomShape(); // set the shape of the piece to a random shapecurX = BOARD_WIDTH / 2 + 1; // set the x coordinate of the piece to the middle of the boardcurY = BOARD_HEIGHT - 1 + curPiece.minY();if (!tryMove(curPiece, curX, curY)) { // if the piece cannot move to the new coordinatescurPiece.setShape(Tetrominoe.NoShape); // set the shape of the piece to a no shapetimer.cancel();isStarted = false;statusbar.setText("GAME OVER!");}}// check if the piece can move to the new coordinatesboolean tryMove(Shape newPiece, int newX, int newY) {for (int i = 0; i < 4; ++i) {int x = newX + newPiece.x(i);int y = newY - newPiece.y(i);if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) { // if the new coordinates are out of the boardreturn false; // return false}if (shapeAt(x, y) != Tetrominoe.NoShape) { // if the new coordinates are not a no shapereturn false; // return false}}curPiece = newPiece; // set the current piece to the new piececurX = newX; // set the current x coordinate to the new x coordinatecurY = newY; // set the current y coordinate to the new y coordinaterepaint(); // repaint the board with the new coordinates of the piecereturn true; // return true}// remove the full line from the boardvoid removeFullLines() {int numFullLines = 0; // set the number of full lines to 0for (int i = BOARD_HEIGHT - 1; i >= 0; --i) { // for the no of rows in the boardboolean lineIsFull = true; // set the line is full to truefor (int j = 0; j < BOARD_WIDTH; ++j) { // for the no of columns in the boardif (shapeAt(j, i) == Tetrominoe.NoShape) { // if the shape at the coordinates is a no shapelineIsFull = false; // set the line is full to falsebreak; // break the loop}}if (lineIsFull) { // if the line is full++numFullLines; // increment the number of full linesfor (int k = i; k < BOARD_HEIGHT - 1; ++k) { // for the no of rows in the boardfor (int j = 0; j < BOARD_WIDTH; ++j) { // for the no of columns in the boardboard[(k * BOARD_WIDTH) + j] = shapeAt(j, k + 1); // set the shape at the coordinates to the shape at the coordinates below}}}}if (numFullLines > 0) { // if the number of full lines is greater than 0numLinesRemoved += numFullLines; // increment the number of lines removed by the number of full linesstatusbar.setText("Score: "+String.valueOf(numLinesRemoved)); // set the text of the score to the number of lines removedisFallingFinished = true; // set the piece has finished falling to truecurPiece.setShape(Tetrominoe.NoShape); // set the shape of the piece to a no shaperepaint();}}// draw the square on the boardvoid drawSquare(Graphics g, int x, int y,Tetrominoe shape) {Color colors[] = {new Color(0, 0, 0), new Color(204, 102, 102),new Color(102, 204, 102), new Color(102, 102, 204),new Color(204, 204, 102), new Color(204, 102, 204),new Color(102, 204, 204), new Color(218, 170, 0),};Color color = colors[shape.ordinal()];g.setColor(color);g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2);g.setColor(color.brighter());g.drawLine(x, y + squareHeight() - 1, x, y);g.drawLine(x, y, x + squareWidth() - 1, y);g.setColor(color.darker());g.drawLine(x + 1, y + squareHeight() - 1,x + squareWidth() - 1, y + squareHeight() - 1);g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,x + squareWidth() - 1, y + 1);}void doGameCycle() {update();repaint();}void update() {if (isPaused) { // if the game is pausedreturn; // return}if (isFallingFinished) { // if the piece has finished fallingisFallingFinished = false; // set the piece has finished falling to falsenewPiece(); // create a new piece} else {oneLineDown(); // move the piece down one line}}// used to check the pressed keysclass TAdapter extends KeyAdapter {@Overridepublic void keyPressed(KeyEvent e) {if (!isStarted || curPiece.getShape() == Tetrominoe.NoShape) { // if the game has not started or the shape of the piece is a no shapereturn; // return}int keycode = e.getKeyCode(); // get the key code of the pressed keyif (keycode == KeyEvent.VK_ENTER) { // if the pressed key is the enter keypause(); // pause the gamereturn; // return}if (isPaused) {return;}switch (keycode) { // switch the key code of the pressed keycase KeyEvent.VK_LEFT: // if the pressed key is the left arrowtryMove(curPiece, curX - 1, curY); // try to move the piece to the leftbreak;case KeyEvent.VK_RIGHT: // if the pressed key is the right arrowtryMove(curPiece, curX + 1, curY); // try to move the piece to the rightbreak;case KeyEvent.VK_DOWN: // if the pressed key is the down arrowtryMove(curPiece.rotateRight(), curX, curY); // try to rotate the piece to the rightbreak;case KeyEvent.VK_UP: // if the pressed key is the up arrowtryMove(curPiece.rotateLeft(), curX, curY); // try to rotate the piece to the leftbreak;case KeyEvent.VK_SPACE: // if the pressed key is the space bardropDown(); // drop the piece downbreak;case KeyEvent.VK_D: // if the pressed key is the d keyoneLineDown(); // move the piece down one linebreak;}}}class ScheduleTask extends TimerTask {@Overridepublic void run() {doGameCycle();}}}// main class of the gameclass Tetris extends JFrame {static final long serialVersionUID = 1L;JLabel statusbar;public Tetris() {initUI();}void initUI() {JPanel panel = new JPanel();panel.setBackground(new Color(0XF5EBE0));statusbar = new JLabel("Score: 0");statusbar.setFont(new Font("MV Boli", Font.BOLD, 30));panel.add(statusbar, BorderLayout.NORTH);Board board = new Board(this);add(panel, BorderLayout.NORTH);add(board);board.setBackground(new Color(0Xf0e2d3));board.start();setTitle("Tetris");setSize(400, 600);setDefaultCloseOperation(EXIT_ON_CLOSE);setResizable(false);setLocationRelativeTo(null);}public JLabel getStatusBar() {return statusbar;}// run the game from herepublic static void main(String[] args) {EventQueue.invokeLater(() -> {Tetris game = new Tetris();game.setVisible(true);});}}
Output:
Image output:

Video output:
Congratulations!! Hope you enjoyed building this cool project Tetris Game in Java.
Also Read:
- Dino Game in Java
- Java Games Code | Copy And Paste
- Supply Chain Management System in Java
- Survey Management System In Java
- Phone Book in Java
- Email Application in Java
- Inventory Management System Project in Java
- Blood Bank Management System Project in Java
- Electricity Bill Management System Project in Java
- CGPA Calculator App In Java
- Chat Application in Java
- 100+ Java Projects for Beginners 2023
- Airline Reservation System Project in Java
- Password and Notes Manager in Java
- GUI Number Guessing Game in Java
- How to create Notepad in Java?
- Memory Game in Java
- Simple Car Race Game in Java
- ATM program in Java
- Drawing Application In Java
- Tetris Game in Java
- Pong Game in Java
- Hospital Management System Project in Java
- Ludo Game in Java
- Restaurant Management System Project in Java
- Flappy Bird Game in Java
- ATM Simulator In Java
- Brick Breaker Game in Java
- Best Java Roadmap for Beginners 2023
- Snake Game in Java