Search code examples
javajavafx

timer manipulation in JavaFX


Sometimes I am halfway through the Hangman game and the "time is up" alert interrupts me not on its perfect time. The remaining time is 40 secs as an example and it tells me my time is up, but the problem is that this happens sometimes and NOT in every single game.It might be due to the JavaFX timer features but I'm no pro. Maybe there is sthg I need to know. I posted asking for help after trying to figure it out myself and not being able to, I don't need someone to come tell me this is a code dump. I wouldn't have posted if I figured it out myself in the 1st place! I appreciate your help and thank you in advance.

Controller class:

//libraries import

public class Controller {

    //other FXML elements
    @FXML
    private Text time;
    @FXML
    private Text time1;

    //variables used defined;
    
    public void initialize() {
        language1.setText("Please select a language 1.English 2.French ");
        english.setVisible(true);
        french.setVisible(true);

        //all others elements are invisible
        
    }

    public void onClick(ActionEvent event){    
        String letter = ((Button)event.getSource()).getText().toLowerCase();        
        ((Button) event.getSource()).setDisable(true);
        
        time1.setText("");
        String str1 = timer.remain();
        time.setText("The remining time is "+str1+" seconds");}
      
        if(myLetters.contains(letter)){
            int letterIndex = myLetters.indexOf(letter);
            while (letterIndex != -1) {
                correct++;
                myLetters.set(letterIndex, "*");
                answer.set(letterIndex*2, letter);
                letterIndex = myLetters.indexOf(letter);
                String res = String.join("", answer);
                text.setText(res);
            }
            
            if(correct==word.length()){
                winStatus.setText("You Win!");}
                buttons.setDisable(true);
                timer.stop();
            }
        }
        else{
            mistakes++;
            if(timer.isFinished()){
                timer.stop();
                timer.showAlertEnglish();
                buttons.setDisable(true);
            }
         
            if(mistakes ==1)       base1.setVisible(true);
            // show other parts of hangman 
            else if (mistakes ==8){
                man.setVisible(true);
                winStatus.setText("You Lose!");
                realWord.setText("The actual word was " + word);
                buttons.setDisable(true);
                timer.stop();
            }
        }
    }
    
    public void select(ActionEvent event) {
        //chnages the game due to the selected language
   
   
        time1.setText("Vous avez 1 minute. Allez !");
        timer.start();
    }
    
    
    public void newGame(){
        time.setText("");
        for(int i=0; i<35; i++){
            buttons.getChildren().get(i).setDisable(false);
        }
        newGameButton.setText("");
        initialize();
    }
    
}

Timer class:

Import java.util.Timer;
import java.util.TimerTask;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;

public class timer {
    
    private static long startTime;
    private static long remaining;
    private static long remainingTimeSeconds;
    private static long remainingTimeMilliSeconds;
    private static boolean timeUp;
    private static Timer timer;
    
    public static void start() {
        startTime = System.currentTimeMillis();
        timeUp = false;
        timer = new Timer();
        timer.schedule(new TimerTask() {
         public void run() {
                      timeUp = true;
                  }
         }, 60 * 1000);
        }
    
    public static String remain() {
        remaining=(60 * 1000) - (System.currentTimeMillis() - startTime);
        remainingTimeSeconds = (remaining)/ 1000;
        remainingTimeMilliSeconds= (remaining)%1000;
        if (remainingTimeMilliSeconds<0) {remainingTimeMilliSeconds=0;}
        String ch=String.valueOf(remainingTimeSeconds)+"."+String.valueOf(remainingTimeMilliSeconds);
        return ch;
    }    
        

    public static boolean isFinished() {
         return timeUp;
    }
    
    public static void stop() {
        timeUp=true;
    }
    

    public static void showAlertEnglish() {
        Alert alert = new Alert(AlertType.WARNING);
        alert.setTitle("Time's Up!");
        alert.setHeaderText(null);
        alert.setContentText("You ran out of time. You lose!");
        alert.showAndWait();
    }
    
    public static void showAlertFrench() {
        Alert alert = new Alert(AlertType.WARNING);
        alert.setTitle("Temps écoulé !");
        alert.setHeaderText(null);
        alert.setContentText("Vous avez dépassé le temps imparti. Vous avez perdu !");
        alert.showAndWait();
    }
}
 

Solution

  • See if you can incorporate this stopwatch that uses Timeline, or maybe you can get some ideas from it.

    Example Code:

    Main

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.Alert;
    import javafx.scene.control.Button;
    import javafx.scene.control.ButtonType;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    public class App extends Application {
    
        
        
        @Override
        public void start(Stage primaryStage) { 
            
            StopWatchGui stopWatchGui = new StopWatchGui(10);
            
            Button btnStop = new Button("Stop");
            Button btnStart = new Button("Start");
            Button btnPause = new Button("Pause");
            
            btnStart.setOnAction((t) -> {
                stopWatchGui.start();
            });
            
            btnPause.setOnAction((t) -> {
                stopWatchGui.pause();
            });
            
            btnStop.setOnAction((t) -> {
                stopWatchGui.stop();
            });
            
            stopWatchGui.remainingSecondsProperty().addListener((ov, oldValue, newValue) -> {
                if(newValue != null)
                {
                    if(newValue.intValue() == 0)
                    {
                        stopWatchGui.pause();
                        Alert alert = new Alert(Alert.AlertType.INFORMATION, "Out of Time", ButtonType.CLOSE);
                        alert.show();
                    }
                }
            });
            VBox root = new VBox(stopWatchGui.getStopWatch(), new HBox(btnStart, btnPause, btnStop));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();     
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    StopWatchGui

    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.event.ActionEvent;
    import javafx.scene.layout.VBox;
    import javafx.scene.text.Text;
    import javafx.util.Duration;
    
    /**
     *
     * @author sedj601
     */
    public class StopWatchGui
    {
        private final Text display = new Text();    
        private final VBox vbox = new VBox();
        private final Timeline stopWatchTimeline;
        
        private final IntegerProperty seconds = new SimpleIntegerProperty();
        private final int initialSeconds;
        
        public StopWatchGui(int initialSeconds)
        {
            this.initialSeconds = initialSeconds;
            seconds.set(initialSeconds);
            
            display.setText(String.format("%02d:%02d:%02d", seconds.get() / 3600, (seconds.get() % 3600) / 60, seconds.get() % 60));
            
            stopWatchTimeline = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent event) -> {           
                seconds.set(seconds.get() - 1);
                display.setText(String.format("%02d:%02d:%02d", seconds.get() / 3600, (seconds.get() % 3600) / 60, seconds.get() % 60));            
            }));
            stopWatchTimeline.setCycleCount(Timeline.INDEFINITE);
            
            vbox.getChildren().addAll(display);
        }
    
        public void pause()
        {
            stopWatchTimeline.pause();
        }
        
        public void stop()
        {
            seconds.set(initialSeconds);
            display.setText(String.format("%02d:%02d:%02d", seconds.get() / 3600, (seconds.get() % 3600) / 60, seconds.get() % 60));
        }
        
        public void start()
        {
            stopWatchTimeline.play();
        }
        
        public VBox getStopWatch()
        {
            return vbox;
        }
        
        public IntegerProperty remainingSecondsProperty()
        {
            return seconds;
        }
    
        public int getRemainingSeconds()
        {
            return seconds.get();
        }
    }
    

    Output

    enter image description here

    Code from: https://stackoverflow.com/a/50627639/2423906