Domanda

I have built a tetris game. Now in that I have used a JPanel for displaying content and the blocks (using the paintComponents() method).

The problem is whenever I try to call the tetris program from another JFrame it does not paint at all.

The code for my tetris main menu is:

import javax.swing.*;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
import java.awt.*;
import java.awt.event.*;
import java.io.FileInputStream;
import java.io.InputStream;

@SuppressWarnings("serial")
public class Tetris_MainMenu extends JFrame implements ActionListener {

    @SuppressWarnings("unused")
    public static void main(String[] args) {

        Tetris_MainMenu tmm = new Tetris_MainMenu();
        playAudio("doak.wav");

    }

    JPanel logo = new JPanel();
    JPanel buttonPanel = new JPanel(new GridLayout(4, 1));

    JButton start = new JButton("START NEW GAME");
    JButton help = new JButton("INSTRUCTIONS");
    JButton about = new JButton("ABOUT THIS GAME");
    JButton exit = new JButton("EXIT");

    Tetris_MainMenu(){

        setTitle("JAG's TETRIS");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocation(300, 100);
        setSize(200, 400);
        setEnabled(true);
        setFocusable(true);
        setVisible(true);

        //adding a logo to the logo panel

        //adding buttons to the buttonPanel
        buttonPanel.add(start);
        buttonPanel.add(help);
        buttonPanel.add(about);
        buttonPanel.add(exit);

        //add panels to window
        setLayout(new GridLayout(2, 1));
        add(logo);
        add(buttonPanel);

        //make buttons listen to actions
        start.addActionListener(this);
        help.addActionListener(this);
        about.addActionListener(this);
        exit.addActionListener(this);

    }

    @SuppressWarnings("restriction")
    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);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if(e.getSource() == start){
            this.dispose();
            MatrixBoard b = new MatrixBoard();
            b.setRequestFocusEnabled(true);
        }
        else if(e.getSource() == help){
            JOptionPane.showMessageDialog(null, "Controls:\n"
                    + "LEFT and RIGHT ARROWS: For moving blocks left and right\n"
                    + "DOWN ARROW: For dropping block immediately\n"
                    + "SPACEBAR: For rotating block\n"
                    + "e: To exit to main menu");
        }
        else if(e.getSource() == about){
            JOptionPane.showMessageDialog(null, "Designed by: JAG."
                    + "\nIf you want you can use it for your own purposes."
                    + "\nBut give credit where it is due.");
        }
        else if(e.getSource() == exit){
            int opt = JOptionPane.showConfirmDialog(null, "Are you sure?", "Confirm Exit", JOptionPane.YES_NO_OPTION);
            if(opt == JOptionPane.YES_OPTION){
                System.exit(0);
            }
        }
    }


}

The tetris game starts in a new window when the constructor for MatrixBoard is called. However the blocks are not visible on the screen. The code for MatrixBoard is:

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

@SuppressWarnings("serial")
public class MatrixBoard extends JPanel 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;
    JFrame f;

    @SuppressWarnings("unused")
    public static void main(String [] args){
        MatrixBoard b = new MatrixBoard();      
    }

    public void update(Graphics g) {

        Graphics offgc;
        Image offscreen = null;
        Dimension d = getSize();
        // create the offscreen buffer and associated Graphics
        offscreen = createImage(d.width, d.height);
        offgc = offscreen.getGraphics();
        // clear the exposed area
        offgc.setColor(getBackground());
        offgc.fillRect(0, 0, d.width, d.height);
        offgc.setColor(getForeground());
        // do normal redraw
        paint(offgc);
        // transfer offscreen to window
        g.drawImage(offscreen, 0, 0, this);
    }

    @SuppressWarnings("restriction")
    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(){
        f = new JFrame("JAG's TETRIS");
        f.setVisible(true);
        f.setSize(205, 400);
        f.setFocusable(true);
        f.setBackground(Color.GREEN);
        f.add(this);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200, 400);
        setVisible(true);
        setFocusable(true);
        requestFocusInWindow();
        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!!!");
        f.setEnabled(true);
        f.setResizable(false);
        //setEnabled(true);
        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_OPTION){
                start();
            }
            else if (opt == JOptionPane.NO_OPTION){
                System.exit(0);
            }
            //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;
            System.out.println("calling f.update()");
            repaint();
            System.out.println("called f.update()");
            try {
                Thread.sleep(sleepTime);
            } catch (Exception e) {
                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 paintComponent(Graphics g){ 

        System.out.println("PAINTED\n\n\n\n\nPAINTED!!!!");

        super.paintComponent(g);

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

        System.out.println("PAINTED\n\n\n\n\nPAINTED!!!!");

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

}

Please help. I have a doubt that the problem is in the repaint mechanism but I am not sure.

È stato utile?

Soluzione

Your problem appears to be a Swing threading issue as you appear to be calling Thread.sleep(...) on the Swing event thread. Doing this will lock the entire thread, freezing your application. Don't do this, but use a Swing Timer instead.

As an aside, you don't want to override the update() method for Swing GUI's as that's mainly for AWT GUI's, unless you're changing the application's Look and Feel in mid run.


Edit
You ask:

I understand that calling Thread.sleep() is undesirable and it is better to use timers. But the thing is that when I run the MatrixBoard individually, it works perfectly well. The thread issue is not faced here. Please explain if possible.

When you call it by itself, the main application is not being run on the Swing thread (which is not something that should be done -- all Swing apps should be run on the Swing event thread), and so your code seems to work. Once you force it to run on the Swing event thread, it freezes.

All Swing GUI's should be queued onto the event thread using SwingUtilities.invokeLater(new Runnable() {...});


Edit 2

Watch what happens when you change this:

public static void main(String [] args){
    MatrixBoard b = new MatrixBoard();      
}

to the more appropriate code:

public static void main(String[] args){
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        MatrixBoard b = new MatrixBoard();      
      }
    });
}

I'll bet your MatrixBoard will not freeze (although I haven't tested it).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top