2016-11-23 3 views
-1

Когда я запускаю игру (змеиную игру) через главное меню, она не работает - она ​​практически замораживается, и это также, вероятно, потому, что бесконечный цикл, который у меня есть , Но когда я удаляю этот цикл, он работает так же хорошо, как я могу закрыть приложение - я не могу это сделать, когда игра запускается из меню, но без этого цикла моя змея не может двигаться. И когда я начинаю игру через главное - просто вызывая класс «Вид», он работает правильно.Java - не удается закрыть Окно из-за бесконечного цикла

Я знаю, что я должен использовать Threads вместо цикла, но я понятия не имею, как их правильно использовать.

Часть кода, который не работает:

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: это конец, если все, что он делает, это простая анимация. Таймер Swing намного проще и увереннее. Я бы использовал SwingWorker, если мне пришлось запускать долговременную задачу, например, доступ к базе данных или загрузку или загрузку файлов, но с анимацией длительная часть - это сама задержка, для которой создан таймер. –

ответ

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 Timer Tutorial для деталей, но суть его является:

  • Вы не писать код, что петли - ваш таймер будет заменить цикл или во время цикла.
  • Вместо этого вы создаете объект javax.swing.Timer, вызывая его конструктор, передавая время задержки и ActionListener.
  • В этом методе ActionListener actionPerformed(ActionEvent e) вы пишете код, который хотите повторить.
  • Я бы посоветовал не использовать потоки непосредственно для этого, если вы не поймете concurrency with Swing issues (проверьте ссылку). Например, если вы решите напрямую использовать потоки, либо с помощью библиотеки параллелизма, либо с помощью Thread/Runnable, или SwingWorker, вам нужно будет позаботиться о том, чтобы любые вызовы Swing, сделанные из фонового потока, должны были быть поставлены в очередь на событие Swing нить, либо через пару метода публикации/процесса SwingWorker, либо путем передачи Runnable в вызов SwingUtilities.invokeLater(...). Это делает, но это сложнее, чем нужно, и использование Swing Timer будет намного проще.
  • Чтобы остановить таймер поворота, просто вызовите метод stop(). Boom. Вот и все.
  • Вторичные проблемы: качели графики должны выполняться пассивным способом - рисовать внутри метода paintComponent JPanel. То, что я сделал бы, это изменить состояние ArrayList<Point> в моем таймере Swing Timer, вызвать repaint(), а затем выполнить итерацию через ArrayList в моем методе paintComponent, нарисовав каждый сегмент змеи в местоположении на основе позиций Point в 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()); 
    } 
} 
Смежные вопросы