So, I am creating the snake game using JavaFX and I cannot seem to make the game pause properly, i.e. it pauses occasionally and other times, the game just ignores the pause. So, basically I have a Main
class where I initialize all the GUI components, and it also acts as the controller for the javafx Application.
I have a Button
named gameControl
which starts/pauses the game, a variable Boolean pause
which keeps track of the game states (new/paused/running), and the methods startGame
, pauseGame
.
The gameControl
button's EventHandler
is as follows:
gameControl.setOnClicked(event->{
if(paused == null) startGame(); //new game
else if(paused) continueGame(); //for paused game
else pauseGame(); //for running game
});
The startGame
function looks something like this:
void startGame(){
paused = false;
Snake snake = new Snake(); //the snake sprite
//following gameLoop controls the animation of the snake
gameLoop = new AnimationTimer(){
@Override
public void handle(long now){
drawSnake(); //draws the snake on the game
snake.move(); //move snake ahead
//following code is for slowing down the gameLoop renders to make it easier to play
Task<Void> sleeper = new Task<>(){
@Override
protected Void call() throws Exception {
gameLoop.stop();
Thread.sleep(30);
gameLoop.start();
return null;
}
};
new Thread(sleeper).start();
//force garbage collection or else throws a bunch of exceptions after a while of running.
//not sure of the cause...
System.gc();
}
};
gameLoop.start();
}
AnimationTimer gameLoop
are variables of the class to allow calling from other functions.
And the pauseGame
function:
void pauseGame() {
paused = true;
gameLoop.stop();
}
So, as I have said before the game doesn't pause everytime I hit the gameControl
button, and I suspect it is due to the Thread.sleep(30);
line inside the Task
of the gameLoop
. That being said, I am still not fully sure and have no idea how to fix this. Any help would be appreciated.
Your intuition that Thread.sleep(30);
may be causing an issue is on the right track. A user may click the button calling pauseGame
, setting paused to true, and telling the gameloop to stop. If the new sleeper thread is started and sleeping at that moment it will call start()
on the gameloop when it wakes up.
One option may be to simply check the value of paused
in the sleeper task to determine if it should start the gameloop.
Task<Void> sleeper = new Task<>(){
@Override
protected Void call() throws Exception {
gameLoop.stop();
Thread.sleep(30);
if (!paused) {
gameLoop.start();
}
return null;
}
};
Given that the paused value is being set and read from multiple threads, I would recommend adding a mechanism to ensure you don't get non-deterministic behavior. Swapping the Boolean to an AtomicBoolean is a straight forward option for ensuring consistency.