2014-08-30 335 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