You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX猜词游戏计时器提前触发异常问题求助

Hangman游戏计时器异常:剩余约40秒提前触发超时提示

问题现象

玩Hangman猜词游戏时,偶尔会出现剩余约40秒就弹出“时间到”提示的情况,该问题并非每次游戏都会出现,怀疑与JavaFX计时器特性相关。

问题根源

  1. 静态变量共享冲突:原timer类所有核心状态(startTime、timeUp、timer)均为静态变量,多次启动游戏(如点击newGame)时,旧计时器任务未被清理,会继续修改共享的timeUp变量,导致新游戏状态被干扰,提前触发超时。
  2. 计时器资源未清理:原stop()方法仅设置timeUp=true,未真正取消后台计时器任务,旧任务仍会在后台运行并篡改状态。
  3. 线程安全问题:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:47:08