Question

I have made a Tetris game using a bit of help from the internet and some books and it seems to work almost as I want it to. The only problem is that when I run it the screen flickers horribly when the blocks drop.

I am new to AWT and swing and hence I often tend to use trial and error (I know it is bad) to get what I want. This time I am quite unsure as to what part of the program is causing it.

Here is my code. Sorry but my code is a bit long and I seriously dont know where the problem is occuring. (But mostly it will be in the paint() method or in the Timer. At least that is what I feel.)

import java.awt.*;
import java.awt.event.*;    
import javax.swing.*;    
import java.applet.*;
import java.io.*;
import sun.audio.*;

public class MatrixBoard extends Frame implements ActionListener{

    int boardHeight = 20;
    int boardWidth  = 10;
    int score = 0;
    int curX = 0, curY = 0;
    int squareWidth;
    int squareHeight;
    Timer timer;
    int sleepTime = 300;
    Shape curPiece;
    Shape.Tetromino[][] board;
    boolean isFallingFinished = false;
    boolean isStarted = false;
    boolean isPaused = false;

    public static void main(String [] args){
        playAudio("C:\\doak.wav");
        MatrixBoard b = new MatrixBoard();
    }

    public static void playAudio(String filename)
    {
        InputStream in = null;
        AudioStream as = null;
        try{
            in = new FileInputStream(filename);
            as = new AudioStream(in);
        }
        catch(Exception e){
            System.out.println("Error!!!");
        }
        AudioPlayer.player.start(as);
    }

    MatrixBoard(){
        setTitle("TETRIS: NEW GAME");
        setFocusable(true);
        setSize(200, 400);
        setVisible(true);
        setBackground(Color.BLACK);
        timer = new Timer(400, this);
        timer.setInitialDelay(10);
        squareWidth = (getWidth())/ boardWidth;
        squareHeight = (getHeight()) / boardHeight;
        curPiece = new Shape();
        board = new Shape.Tetromino[boardWidth][boardHeight];
        addKeyListener(new KeyHandler());
        clearBoard();
        timer.start();
        JOptionPane.showMessageDialog(null, "Press Enter to Start!!!");
        start();
        System.out.println("MatrixBoard() Success!");
    }

    public void clearBoard(){
        for(int i = 0; i < boardWidth; ++i)
            for(int j = 0; j < boardHeight; ++j)
                board[i][j] = Shape.Tetromino.NoShape;
    }

    public void start()
    {
        if (isPaused)
            return;
        clearBoard();
        timer.start();
        timer = new Timer(400, this);
        timer.setInitialDelay(100);
        isStarted = true;
        isFallingFinished = false;
        score = 0;
        repaint();
        newPiece();
        System.out.println("START SUCCESS!");
    }

    private void newPiece(){
        if(!isStarted) return;
        curPiece.generateShape();
        curX = boardWidth / 2;
        curY = 1;

        if(!tryMove(curPiece, curX, curY)){
            curPiece.selectPiece(Shape.Tetromino.NoShape);
            isStarted = false;
            JOptionPane.showMessageDialog(null, "Game Over! Score : " + score);
            isStarted = false;
            int opt = JOptionPane.showConfirmDialog(null, "Try Again?", "Again?", JOptionPane.YES_NO_OPTION);
            if(opt == JOptionPane.YES_NO_OPTION){
                start();
            }
            dispose();
            System.exit(0);
            //new Tetris();
            return;
        }
        dropDown();
        System.out.println("NEW PIECE SUCCESS!");
    }

    private boolean tryMove(Shape newPiece, int newX, int newY){
        for(int i = 0; i < 4; i++){
            int x = newX + newPiece.coords[i][0]; 
            int y = newY + newPiece.coords[i][1]; 
            if(x < 0 || x >= boardWidth || y < 0 || y >= boardHeight){
                System.out.println("FALSE1");
                return false;
            }
            if(board[x][y] != Shape.Tetromino.NoShape){
                System.out.println("FALSE2");
                return false;
            }
        }
        curPiece = newPiece;
        curX = newX;
        curY = newY;
        System.out.println("curX = " + curX + " curY = " + curY);
        System.out.println("TRY MOVE SUCCESS!");
        return true;
    }

    private void dropDown(){
        int newY = curY;
        sleepTime = 300;
        System.out.println("newY = " + newY);
        while(newY < boardHeight){
            if(!tryMove(curPiece, curX, newY+1)){ break;}
            ++newY;
            repaint();
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        pieceDropped();
        System.out.println("DROPDOWN SUCCESS!");
    }

    private void pieceDropped(){

        for(int i = 0; i < 4; i++){
            int x = curX + curPiece.coords[i][0];
            int y = curY + curPiece.coords[i][1];
            board[x][y] = curPiece.retShape();
            System.out.println("PIECE: at X = " + x + " Y = " + y + "is " + curPiece.retShape().ordinal());
        }
        removeFullLines();
        if(!isFallingFinished) newPiece();
        System.out.println("PIECE DROPPED SUCCESS!");

    }

    public void paint(Graphics g){ 

        Dimension size = getSize();
        int boardTop = (int) size.getHeight() - boardHeight * squareHeight;

        for (int i = 0; i < boardWidth; ++i) {
            for (int j = 0; j < boardHeight; ++j) {
                Shape.Tetromino shape = board[i][j];
                if (shape != Shape.Tetromino.NoShape)
                    drawSquare(g, i * squareWidth,
                               boardTop + j * squareHeight, shape);
            }
        }

        if (curPiece.retShape() != Shape.Tetromino.NoShape) {
           for (int i = 0; i < 4; ++i) {
               int x = curX + curPiece.coords[i][0];
               int y = curY + curPiece.coords[i][1];
               drawSquare(g, x * squareWidth,
                          boardTop + (y - 1) * squareHeight,
                          curPiece.retShape());
           }
       }
   }

    private void drawSquare(Graphics g, int x, int y, Shape.Tetromino 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);

    }

    private void removeFullLines(){

        int numLines = 0;

        for(int i = 0; i < boardHeight; i++){
            boolean isLineFull = true;
            for(int j = 0; j < boardWidth; j++){
                System.out.println("i = " + i + " j = " + j);
                if(board[j][i] == Shape.Tetromino.NoShape){
                    System.out.println("Found No Shape here!");
                    isLineFull = false;
                    break;
                }
            }

            System.out.println("IIIIIIIIS LINE : " + isLineFull);

            if(isLineFull){
                numLines++;
                for(int k = i; k > 0; k--){
                    for(int j = 0; j < boardWidth; ++j){
                        board[j][k] = board[j][k-1]; 
                    }
                }
            }
        }

        if(numLines > 0){
            score += numLines;
            repaint();
            newPiece();
        }

    }

    class KeyHandler extends KeyAdapter{
        public void keyPressed(KeyEvent e){
            if(!isStarted || curPiece.retShape() == Shape.Tetromino.NoShape){
                return;
            }
            int keyCode = e.getKeyCode();

            switch(keyCode){
            case KeyEvent.VK_LEFT:
                tryMove(curPiece, curX - 1, curY);
                break;
            case KeyEvent.VK_RIGHT:
                tryMove(curPiece, curX + 1, curY);
                break;
            case KeyEvent.VK_DOWN:
                sleepTime = 10;
                break;
            case KeyEvent.VK_E:
                int opt = JOptionPane.showConfirmDialog(null,"Are you sure?", "Exit", JOptionPane.YES_NO_OPTION);
                if(opt == JOptionPane.YES_OPTION){
                    System.exit(0);
                }
                break;
            case KeyEvent.VK_SPACE:
                tryMove(curPiece.rotateLeft(), curX, curY);
                break;
            default:
                break;
            }
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (isFallingFinished) {
            isFallingFinished = false;
            newPiece();
        } 
    }

}

(I understand that my question may not be as per the required format. But I am new to this place and need some help. So I apologize for any errors in this question.)

EDIT:

THE PROBLEM WAS SOLVED. Following the link given in the comments section of the accepted answers I went on and added a function similar to the example given in the link: http://old.koalateam.com/jml/java/tricks/double-buffering.html

Also following the other suggestions I changed the inheritance to Panel instead of Frame, enabling me to implement double buffering.

Thanks a lot to everyone.

Was it helpful?

Solution

Typically, screen flickering is due to the drawing functions taking longer than it takes the screen to repaint. The basic solution is to do offscreen buffering where you draw on an offscreen image, then in the paint() method, you just paint that image onto the screen.

There are many posts available that talk about this. Here's one: https://today.java.net/pub/a/today/2006/02/23/smooth-moves-solutions.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top