Edit: Answer found! Although CAG did set me on the right track, so I will reward him for it. The correct answer is provided by me though.
I'm making a game of Snake in JavaFX using a Canvas.
I have the game running in a while loop:
The problem is, if I use Thread.sleep(), my canvas does not load at all. Behind the scenes, however, the game is still running until I hit a wall and die.
Is there something I am doing wrong here? Is thread.sleep() suspending the ability to load and display the JavaFX nodes at all?
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();
where printGrid() is:
/**
* 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);
}
and resetCanvas is:
/**
* 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);
}
}
}
and drawCell is:
/**
* 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);
}
}
My guess is you are invoking Thread.sleep()
on the FX Application Thread. That thread is responsible for keeping your UI responsive, so causing it to sleep will freeze your UI while leaving your game mechanics in a responsive state (assuming those are executed off of the FX Application Thread).
The solution is to perform your game loop in a new Thread, like so:
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();
This should prevent any freezing, so long as you are not executing any long-running tasks or invoking sleep on the FX Application Thread.