Warm tip: This article is reproduced from serverfault.com, please click

JavaFX AnimationTimer not stopping properly

发布于 2020-11-29 15:44:00

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.

Questioner
Mubin
Viewed
0
Jimbo McHiggins 2020-11-30 01:02:06

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.