2014-08-30 219 views
1

编辑:找到了答案!虽然CAG确实让我走上了正轨,所以我会奖励他。尽管我提供了正确的答案。JavaFX Snake Thread.Sleep()不加载FXML

我正在使用Canvas在JavaFX中进行一场Snake游戏。

我有一个while循环运行游戏:

  1. 打印设置正确的垂直框细胞的 背景颜色网格的可视化表示。
  2. 等待输入(的Thread.sleep(1000)。
  3. 产生下一个视觉效果。

的问题是,如果我使用了Thread.sleep(),我的画布不加载所有的背后但是,游戏仍然在运行,直到我撞到墙壁并死亡。

有没有什么我在这里做错了?是thread.sleep()暂停加载和显示JavaFX节点的能力吗?

Thread gameThread = new Thread() { 

     @Override 
     public synchronized void start() { 
      super.start(); 
      printGridToGUI(); 
      while (KEEP_PLAYING) { 
       generateNextGrid(); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException ex) { 
        Logger.getLogger(SnakeGUIController.class.getName()).log(Level.SEVERE, null, ex); 
       } 
       Platform.runLater(() -> { 
        printGridToGUI(); 
       }); 
      } 
      /*Stop continuing to play. You either won or lost.*/ 
      if (WON_GAME) { 
       System.out.println("Congratulations!"); 
      } else { 
       System.out.println("You lose."); 
      } 
     } 
    }; 
    gameThread.start(); 

其中printGrid()是:

/** 
* Prints the grid, with chars in place of on and off areas. 
*/ 
public void printGridToGUI() { 
    resetCanvas(); 
    for (Coordinate c : coordinates) { 
     drawCell(c.row, c.col, true); 
    } 
    drawCell(food.row, food.col, true); 
} 

和resetCanvas是:

/** 
* Clears the boolean array, setting all values to false. A quick way to 
* wipe the grid. 
*/ 
public final void resetCanvas() { 
    /*Lay out the grid on the canvas.*/ 
    GraphicsContext gc = canvas.getGraphicsContext2D(); 
    for (int row = 0; row < GRID_SIZE; row++) { 
     for (int col = 0; col < GRID_SIZE; col++) { 
      drawCell(row, col, false); 
     } 
    } 
} 

和drawCell是:

/** 
* Draws a cell on the canvas at the specified row and col. The row, col 
* coordinates are translated into x,y coordinates for the graphics context. 
* 
* @param row The row of the cell to paint. 
* @param col The col of the cell to paint.  
* @param cellON The state of the cell, if it is on or off. 
*/ 
private void drawCell(int row, int col, boolean cellON) { 
    /*Translate the row, col value into an x-y cartesian coordinate.*/ 
    int xCoord = 0 + col * CELL_SIZE; 
    int yCoord = 0 + row * CELL_SIZE; 
    /*Draw on the canvas.*/ 
    GraphicsContext gc = canvas.getGraphicsContext2D(); 
    gc.setFill(Color.BLACK); 
    gc.fillRect(xCoord, yCoord, CELL_SIZE, CELL_SIZE); 
    if (!cellON) { 
     gc.setFill(Color.WHITE); 
     int BORDER = 1; 
     gc.fillRect(xCoord + BORDER, yCoord + BORDER, CELL_SIZE - BORDER, CELL_SIZE - BORDER); 
    } 
} 
+0

你究竟在哪里使用'Thread.sleep()'?你能发布一些代码来支持你的问题吗? – ItachiUchiha 2014-08-30 07:23:13

+0

@IchichiUchiha已添加。 – 2014-08-30 07:57:42

回答

1

我的猜测是你在FX应用程序线程调用Thread.sleep()。该线程负责保持你的UI响应,因此让它进入睡眠状态将冻结你的UI,同时让你的游戏机制处于响应状态(假设这些都是在FX应用程序线程中执行的)。

的解决方案是在一个新的线程中执行你的游戏循环,就像这样:

Thread gameLoop = new Thread(() -> 
{ 
    while (KEEP_PLAYING) 
    { 
     printGrid(); //<- I assume this prints the state of the grid to the console, and so is safe to execute off of the FX Application Thread 
     try 
     { 
      Thread.sleep(1000); 
     } 
     catch (InterruptedException ex) {} 
     Platform.runLater(() -> 
     { 
      generateNextGrid(); //<- execute this on the FX Application Thread as it modifies your UI 
     }); 
    } 
    if (WON_GAME) 
    { 
     ... 
    } 
    else 
    { 
     ... 
    } 
}); 
gameLoop.start(); 

这应该防止任何结冰,只要你不执行任何长期运行的任务或在调用睡眠FX应用程序线程。

+0

实际上,clearGrid()和printGrid()修改了JavaFX UI。 generateNextGrid()修改内部游戏的布尔值[] []。我应该尝试围绕在内部游戏逻辑中使用Thread.sleep的代码尝试使用Thread或Runnable吗?另外,platform.runLater的用途是什么? – 2014-08-30 18:19:04

+0

runLater方法将发送要在FX应用程序线程上执行的Runnable,在调用在不同线程中工作时修改UI的任何方法时,您需要执行该方法。因此,只需在上面的答案中切换generateNextGrid()和printGrid()即可。 – 2014-08-30 21:05:20

+0

当代码在后台运行时,我仍然没有结束。它告诉我,睡眠被正确调用,但没有看到FXML元素。代码更新btw。 – 2014-08-30 22:33:18

1

想通了!我不得不使用JavaFX的并发包,而是使用Task而不是简单的Thread。

Task<Boolean> gameTask = new Task() { 

     @Override 
     protected Object call() throws Exception { 
      while (KEEP_PLAYING) { 
       generateNextGrid(); 
       try { 
        Thread.sleep(GAME_SPEED_DELAY); 
       } catch (InterruptedException ex) { 
        Logger.getLogger(SnakeGUIController.class.getName()).log(Level.SEVERE, null, ex); 
       } 
       Platform.runLater(() -> printGridToGUI()); 
      } 
      /*Stop continuing to play. You either won or lost.*/ 
      if (WON_GAME) { 
       System.out.println("Congratulations!"); 
      } else { 
       System.out.println("You lose."); 
      } 
      return true; 
     } 
    }; 
    Thread gameThread = new Thread(gameTask); 
    gameThread.start(); 
+0

)有趣的是,如果你将你的游戏循环线程的超级'start()'方法中的逻辑重构为'Runnable'的新实例并将其传递给线程,它是否会提供类似的改进?你的'Task'类型为'Boolean',而被覆盖的'call()'方法返回'Object'。 – 2014-08-31 22:18:49