首页 > 解决方案 > Swing GUI 在使用线程时冻结

问题描述

我正在写一个突破性的游戏,除了一件小事,一切都很好。

我有一个从 thread 和 RepainterThread 扩展的球对象,它可以实现 Runnable 或扩展 Thread 调用paint方法,该方法用砖块、桨和球重新渲染游戏场

我有一个连接所有事物的 Singelton GameController。

我有一个带有 isActive 布尔值的 GameState 来决定是否应该暂停游戏。

我开始游戏,我可以玩了,程序运行正常。当球击中一切正常时,框架被绘制,球移动砖块。

然后我通过一个我将 Active 设置为 false 的按钮暂停游戏。球按原样停止。然后我点击继续按钮,isActive 再次为真。球对象再次开始运行,Repainter Thread 的运行方法也被触发,但 Swing Frame 完全冻结。

我尝试了各种方法,这些是我最近的方法。

我花了几天时间请帮忙

public class Ball extends MovingObject {

    private double hSpeed; // Horizontal velocity
    private double vSpeed; // Vertical velocity

    public Ball() {
        this.color = Color.MAGENTA;
        this.height = GameSettings.ballSize;
        this.width = GameSettings.ballSize;
        this.position = new Point2D.Double(GameSettings.defaultBallX, GameSettings.defaultBallY);
        this.hSpeed = GameSettings.ballHSpeed;
        this.vSpeed = GameSettings.ballYSpeed;
    }

    public Ball(Ball ball) {
        color = ball.color;
        height = ball.height;
        width = ball.width;
        position = ball.position;
        hSpeed = ball.hSpeed;
        vSpeed = ball.vSpeed;
    }

    public double getHSpeed() {
        return this.hSpeed;
    }

    public double getVSpeed() {
        return this.vSpeed;
    }


    public void run() {

        try {
            while (GameController.getInstance().getGameState().isActive()) {
                System.out.println("Ich run im Ball");
                Thread.sleep(10);
                this.meetingWall();
                this.meetingBrick();
                this.position.setLocation(this.getPosition().getX() + this.hSpeed,
                        this.getPosition().getY() + this.vSpeed);
                if (this.meetingPaddle()) {
                    this.newDirection();
                }
                if (this.out()) {
                    GameController.getInstance().stopGame();
                    this.stopThread();
                }

                this.position = this.getPosition();

            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void stopThread() {
        GameController.getInstance().getGameState().setActive(false);
    }

    public void startThreadAgain() {
        this.run();
    }

    @Override
    public synchronized void start() {
        this.position.setLocation(GameSettings.defaultBallX, GameSettings.defaultBallY);
        super.start();
    }


    class GamePanel extends JPanel {
        private static final long serialVersionUID = 1L;
        private final Color backgroundColor = Color.BLACK;
        GameState gameState = GameController.getInstance().getGameState();

        public GamePanel() {
            super();

            this.addKeyListener(new KeyListener() {
                // Dieser KeyListener soll auf Inputs der Pfeiltasten nach links
                // <- und rechts -> hoeren und eine entsprechende Bewegung des
                // Schlaegers erwirken, aber nur, wenn das Spiel nicht
                // pausiert/gestoppt ist.
                public void keyPressed(KeyEvent keyEvent) {
                    if (gameState.isActive()) {// gameState.isActive()
                        if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
                            gameState.getPaddle().setPositionRigth();
                        }

                        if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
                            gameState.getPaddle().setPositionLeft();
                        }
                    }

                }

                public void keyReleased(KeyEvent keyEvent) {
                    // TODO
                }

                public void keyTyped(KeyEvent arg0) {
                }
            });
        }


        public void paint(Graphics g) {
            Graphics2D graphics2D = (Graphics2D) g;
            graphics2D.setColor(this.backgroundColor);
            graphics2D.fillRect(0, 0, this.getWidth(), this.getHeight());

            for (int i = 0; i < gameState.getBricks().length; i++) {
                for (int j = 0; j < gameState.getBricks()[i].length; j++) {
                    if ((gameState.getBricks()[i][j] != null)) {
                        graphics2D.setColor(gameState.getBricks()[i][j].getColor());

                        graphics2D.fillRect(
                                (i * GameSettings.brickWidth) + (i + 1) * (GameSettings.spaceAroundBrick + 1),
                                (j * GameSettings.brickHeight) + (j + 1) * GameSettings.spaceAroundBrick,
                                GameSettings.brickWidth, GameSettings.brickHeight);
                    }
                    scoreLabel.setText(this.gameState.getScore() + "");

                    gameState.getPaddle().draw(graphics2D);
                    gameState.getBall().draw(graphics2D);
                }
            }

        }
    }


// First Approach


    private class RepainterThread implements Runnable {

        public RepainterThread() {
            System.out.println("RepainThreadCOntructor");
        }

        private void updateGUI() {
            System.out.println("before invoke later");

            System.out.println("repaint");
            getGamePanel().requestFocus();
            getGamePanel().repaint();

        }

        @Override
        public void run() {
            System.out.println("Repainter run");
            System.out.println(GameController.getInstance().getGameState().isActive());

            while (GameController.getInstance().getGameState().isActive()) {
                System.out.println("inside while");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                updateGUI();

            }

        }

    }

// Second Approach

        private class RepainterThread implements Runnable {


            public RepainterThread() {
                System.out.println("RepainThreadCOntructor");
            }

            private void updateGUI(){

                SwingUtilities.invokeLater(new Runnable() {         
                    public void run() {
                        System.out.println("repaint");
                        getGamePanel().requestFocus();
                        getGamePanel().repaint();
                    }
                });

            }
            @Override
            public void run() {
                System.out.println("Repainter run");
    System.out.println(GameController.getInstance().getGameState().isActive());

                    while (GameController.getInstance().getGameState().isActive()) {
                        System.out.println("inside while");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }

                        updateGUI();


                    }

            }

        }

框架不应再冻结

请帮忙

标签: javaswing

解决方案


这是一种疯狂的猜测,但是由于根据您的描述程序可以正常工作,直到您暂停和取消暂停它,问题可能出在startThreadAgain方法上:

public void startThreadAgain() {
    this.run();
}

您的代码没有显示您如何调用此方法,但该方法中您只是调用this.run(),即您将该run方法作为常规的阻塞方法调用,而可能仍在 UI 线程中。相反,您应该调用start()以启动一个适当的新线程来执行该run方法。

public void startThreadAgain() {
    super.start(); // not this start, otherwise it resets the ball's location
}

推荐阅读