JavaFX猜词游戏计时器提前触发异常问题求助
Hangman游戏计时器异常:剩余约40秒提前触发超时提示
问题现象
玩Hangman猜词游戏时,偶尔会出现剩余约40秒就弹出“时间到”提示的情况,该问题并非每次游戏都会出现,怀疑与JavaFX计时器特性相关。
问题根源
- 静态变量共享冲突:原
timer类所有核心状态(startTime、timeUp、timer)均为静态变量,多次启动游戏(如点击newGame)时,旧计时器任务未被清理,会继续修改共享的timeUp变量,导致新游戏状态被干扰,提前触发超时。 - 计时器资源未清理:原
stop()方法仅设置timeUp=true,未真正取消后台计时器任务,旧任务仍会在后台运行并篡改状态。 - 线程安全问题:使用
java.util.Timer(后台线程)修改UI相关状态变量,与JavaFX UI线程不兼容,引发不可预期的状态异常。
修复方案
1. 重构计时器类
去掉静态变量,改为实例化类,每个游戏对应独立计时器实例,避免状态共享冲突。
2. 替换为JavaFX官方计时器
使用javafx.animation.Timeline替代java.util.Timer,天然适配UI线程,消除线程安全问题。
3. 完善资源清理逻辑
游戏结束(胜利、失败、超时)时,彻底停止并清理计时器,避免残留任务干扰下一局。
修复后代码
重构后的GameTimer类
import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.util.Duration; public class GameTimer { private Timeline timeline; private long startTime; private boolean timeUp; public GameTimer() { timeUp = false; timeline = new Timeline(); // 配置60秒后触发超时逻辑 KeyFrame keyFrame = new KeyFrame(Duration.seconds(60), event -> { timeUp = true; showAlertEnglish(); }); timeline.getKeyFrames().add(keyFrame); timeline.setCycleCount(1); // 仅执行一次 } public void start() { startTime = System.currentTimeMillis(); timeUp = false; timeline.playFromStart(); } public String remain() { if (timeUp) { return "0.000"; } long elapsed = System.currentTimeMillis() - startTime; long remaining = Math.max(0, 60 * 1000 - elapsed); long seconds = remaining / 1000; long millis = remaining % 1000; return String.format("%d.%03d", seconds, millis); } public boolean isFinished() { return timeUp; } public void stop() { timeline.stop(); timeUp = true; } public 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 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(); } }
Controller类修改内容
// 导入语句保持不变 public class Controller { // 其他FXML元素定义... @FXML private Text time; @FXML private Text time1; // 替换为实例化的计时器 private GameTimer gameTimer; public void initialize() { gameTimer = new GameTimer(); language1.setText("Please select a language 1.English 2.French "); english.setVisible(true); french.setVisible(true); // 其他元素隐藏逻辑... } public void onClick(ActionEvent event){ String letter = ((Button)event.getSource()).getText().toLowerCase(); ((Button) event.getSource()).setDisable(true); time1.setText(""); String str1 = gameTimer.remain(); time.setText("The remaining 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); gameTimer.stop(); } } else { mistakes++; if(gameTimer.isFinished()){ gameTimer.stop(); gameTimer.showAlertEnglish(); buttons.setDisable(true); } if(mistakes ==1) base1.setVisible(true); // 其他Hangman部件显示逻辑... else if (mistakes ==8){ man.setVisible(true); winStatus.setText("You Lose!"); realWord.setText("The actual word was " + word); buttons.setDisable(true); gameTimer.stop(); } } } public void select(ActionEvent event) { // 语言切换逻辑... time1.setText("Vous avez 1 minute. Allez !"); gameTimer.start(); } public void newGame(){ time.setText(""); for(int i=0; i<35; i++){ buttons.getChildren().get(i).setDisable(false); } newGameButton.setText(""); // 重置计时器 gameTimer = new GameTimer(); initialize(); } }
额外说明
- 确保Controller中所有原
timer调用均替换为gameTimer,避免静态方法调用残留。 - 检查FXML元素绑定是否正确,防止空指针异常。
Timeline运行在JavaFX应用线程中,无需额外处理线程同步,完全适配UI操作。
内容的提问来源于stack exchange,提问作者Zahra
相关产品推荐
相关产品推荐

