2016-11-23 49 views
-1

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

我知道我应该使用线程而不是循环,但我不知道如何正确使用它们。

部分代码不起作用:

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

感谢您的帮助!

+1

废钢所有这一切 - 只使用一个[摇摆定时器](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html )。当你想停止定时器时,只需调用'stop()'就可以了。它比你所做的更容易,也是线程安全的(你的代码绝对不是)。 –

+0

我建议也看着'SwingWorker'。 – Logan

+0

@LoganKulinski:如果他所做的只是一个简单的动画,那就完蛋了。一个摆动计时器更简单,更笨蛋的证明。如果我不得不运行长时间运行的任务(如数据库访问或文件上载或下载),但使用动画,则需要使用SwingWorker,长时间运行的部分就是延迟本身,这是Timer的基础。 –

回答

2

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

+0

这个解决方案没有错,但它增加了不必要的复杂性。在专门用于进行重复呼叫的Swing库中使用该工具更好。 –

3

这是一个Swing程序,做重复任务与Swing最简单的方法是通过摆动计时器。请检查Swing Timer Tutorial的细节,但它的要点是:

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

例如:

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