Java - 由于无限循环而无法关闭Window

时间:2016-11-23 21:58:31

标签: java

当我通过主菜单开始游戏(蛇游戏)时,它不起作用 - 它基本冻结,也可能是因为我已经到达那里的无限循环。但是当我删除那个循环时,它确实可以工作,因为我可以关闭应用程序 - 当从菜单启动游戏时我不能这样做,但没有那个循环,我的蛇就无法移动。当我通过main开始游戏时 - 只需通过调用类“View”,它就能正常工作。

我知道我应该使用Threads而不是loop,但我不知道如何正确使用它们。

部分代码不起作用:

public void Draw() throws InterruptedException, IOException{
        addKeyListener(this);
        bf = this.getBufferStrategy();



        while(true){
            tmp = System.currentTimeMillis()/1000; 
            sec = tmp - start;
            if (sec % 5 == 0) {
                Obstacles(30);
            }

            g = bf.getDrawGraphics();
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());

            g.setColor(Color.LIGHT_GRAY);
            for (int i = 0; i < obs.size(); i++) {
                g.fillRect(obs.get(i).x*SCALE, obs.get(i).y*SCALE, SCALE, SCALE);
            }


            for (Point point : snakeParts) {
                g.setColor(Color.BLUE);
                g.fillRect(point.x * SCALE, point.y * SCALE, SCALE, SCALE);
            }

            g.fillRect(head.x * SCALE, head.y * SCALE, SCALE, SCALE);   

            switch(kind){
                case 0:
                    g.setColor(Color.RED);
                    break;

                case 1:
                    g.setColor(Color.YELLOW);
                    break;

                case 2:
                    g.setColor(Color.GREEN);
                    break;
            }

            g.fillRect(bonus.x * SCALE, bonus.y * SCALE, SCALE, SCALE);

            string = "Score: " + score + ", Length: " + tailLength + ", Time: " + time / 20;
            g.setColor(Color.WHITE);

            g.drawString(string, this.getWidth()/2-80, 45);

            Move();
            bf.show();
            Thread.sleep(speed);
        }
    }

从主菜单按钮开始游戏:

 private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        try 
        {
            if (cfg.nick())
            {
                this.setVisible(false);
                new View().setVisible(true);
                new View().startGame();
            }
        } catch (InterruptedException ex) 
        {
            Logger.getLogger(HerniMenu.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) 
        {
            Logger.getLogger(HerniMenu.class.getName()).log(Level.SEVERE, null, ex);
        }
    }       

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

这是一个Swing程序,使用Swing执行重复任务的最简单方法是通过Swing Timer。请查看Swing Timer Tutorial了解详细信息,但要点是:

  • 您不编写循环代码 - 您的Timer将替换for循环或while循环。
  • 而是通过调用其构造函数创建一个javax.swing.Timer对象,传递延迟时间和ActionListener。
  • 在ActionListener的actionPerformed(ActionEvent e)方法中,您编写了想要重复的代码。
  • 除非你理解concurrency with Swing issues(检查链接),否则我建议不要直接使用线程。例如,如果您决定直接使用线程,无论是使用并发库,还是使用Thread / Runnable或SwingWorker,您都必须注意在后台线程中进行的任何Swing调用都必须排队到Swing事件中线程,通过SwingWorker的发布/进程方法对或将Runnable传递给SwingUtilities.invokeLater(...)调用。这是可行的,但它比需要的更复杂,并且使用Swing Timer会更简单。
  • 要停止Swing Timer,只需调用stop()方法即可。繁荣。就是这样。
  • 次要问题:Swing图形应以被动方式完成 - 在JPanel的paintComponent方法中绘制。我要做的是在我的Swing Timer中更改ArrayList<Point>的状态,调用repaint()然后在paintComponent方法中遍历ArrayList,在基于Point的位置绘制每个snake段ArrayList中的位置。

例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

public class SimpleSnake extends JPanel {

    private static final int PREF_W = 900;
    private static final int PREF_H = 650;
    private static final int SNAKE_WIDTH = 24;
    private static final int TIMER_DELAY = 20; // milliseconds  
    private static final int SNAKE_LENGTH = 20;
    private List<Point> pointList = new ArrayList<>();
    private boolean right = true;
    private boolean down = true;
    private JButton startButton = new JButton("Start");
    private JButton stopButton = new JButton("Stop");
    private Timer swingTimer = new Timer(TIMER_DELAY, e -> timerActionPerformed(e));

    public SimpleSnake() {
        // fill snake
        for (int i = 0; i < SNAKE_LENGTH; i++) {
            int x = (2 * (i + 1) * SNAKE_WIDTH) / 3;
            int y = (2 * (i + 1) * SNAKE_WIDTH) / 3;
            pointList.add(new Point(x, y));
        }

        startButton.addActionListener(e -> swingTimer.start());
        stopButton.addActionListener(e -> swingTimer.stop());

        setBackground(Color.BLACK);
        add(startButton);
        add(stopButton);
    }

    // called by Swing Timer's ActionListener 
    private void timerActionPerformed(ActionEvent e) {

        // get last point in ArrayList
        Point lastPoint = pointList.get(pointList.size() - 1);

        // if at any wall, reverse the direction of snake flow
        if (lastPoint.x - SNAKE_WIDTH / 2 < 0) {
            right = true;
        }
        if (lastPoint.x + SNAKE_WIDTH / 2 > getWidth()) {
            right = false;
        }
        if (lastPoint.y - SNAKE_WIDTH / 2 < 0) {
            down = true;
        }
        if (lastPoint.y + SNAKE_WIDTH / 2 > getHeight()) {
            down = false;
        }

        // remove first Point
        pointList.remove(0);

        // calculate the next Point to add
        int x = lastPoint.x;
        if (right) {
            x += (2 * SNAKE_WIDTH) / 3;
        } else {
            x -= (2 * SNAKE_WIDTH) / 3;            
        }

        int y = lastPoint.y;
        if (down) {
            y += (2 * SNAKE_WIDTH) / 3;
        } else {
            y -= (2 * SNAKE_WIDTH) / 3;            
        }

        // add point to ArrayList
        pointList.add(new Point(x, y));
        repaint();  // and repaint the JPanel
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        // to make smooth graphics
        Graphics2D g2 = (Graphics2D) g; 
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.BLUE);

        // iterate through the points, drawing the snake segments
        for (Point p : pointList) {
            int x = p.x - SNAKE_WIDTH / 2;
            int y = p.y - SNAKE_WIDTH / 2;
            g2.fillOval(x, y, SNAKE_WIDTH, SNAKE_WIDTH);
        }
    }

    // size the JPanel correctly
    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("SimpleSnake");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new SimpleSnake());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

答案 1 :(得分:2)

正如您已经诊断出来的那样,问题在于您有一个线程正在执行运行游戏和控制窗口的所有逻辑。要解决此问题,您需要将功能分离为单独的线程。网上有很多解释和例子,其中一些基础知识是: - http://docs.oracle.com/javase/tutorial/essential/concurrency/ - http://www.javaworld.com/article/2077138/java-concurrency/introduction-to-java-threads.html