消除自制俄罗斯方块游戏中的闪烁

时间:2014-02-15 08:54:28

标签: java swing awt paint game-engine

我已经使用互联网和一些书籍的一些帮助制作了俄罗斯方块游戏,它似乎几乎按照我的要求工作。唯一的问题是,当我运行它时,屏幕会在块下降时闪烁。

我是AWT的新手,因此我经常倾向于使用反复试验(我知道这很糟糕)来得到我想要的东西。这次我不确定程序的哪个部分导致它。

这是我的代码。对不起,但我的代码有点长,我真的不知道问题出在哪里。 (但主要是在paint()方法或Timer中。至少这是我的感受。)

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();
        } 
    }

}

(我知道我的问题可能不符合要求的格式。但我是这个地方的新手,需要一些帮助。所以我为这个问题中的任何错误道歉。)

编辑:

问题已得到解决。 按照接受的答案的评论部分中给出的链接,我继续添加了一个类似于链接中给出的示例的函数:http://old.koalateam.com/jml/java/tricks/double-buffering.html

另外遵循其他建议我将继承更改为Panel而不是Frame,使我能够实现双缓冲。

非常感谢大家。

1 个答案:

答案 0 :(得分:2)

通常,屏幕闪烁是由于绘图功能所花费的时间比屏幕重绘时间长。基本的解决方案是在屏幕外的图像上进行屏幕外缓冲,然后在paint()方法中,将图像绘制到屏幕上。

有很多帖子可以讨论这个问题。这是一个:https://today.java.net/pub/a/today/2006/02/23/smooth-moves-solutions.html