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

JavaFX开发:如何等待Roll或Keep按钮按下以推进Pig骰子游戏?

Pig骰子游戏JavaFX玩家回合事件驱动实现方案

核心思路

抛弃同步等待的写法,采用事件驱动+异步回调的模式处理玩家交互:

  • 仅在玩家回合启用Roll和Keep按钮,绑定对应的点击事件处理器
  • 玩家点击按钮后立即禁用按钮,通过异步机制传递操作结果
  • 游戏主流程通过异步逻辑接收结果,再执行后续回合操作

具体实现步骤

1. 初始化按钮状态与事件监听器

在UI初始化阶段完成按钮的默认状态设置和点击事件绑定:

// 类级别变量,用于暂存玩家选择的回调或异步结果
private CompletableFuture<Boolean> playerChoiceFuture;

private void initGameUI() {
    // 默认禁用按钮,非玩家回合不可操作
    rollButton.setDisable(true);
    keepButton.setDisable(true);
    
    // 绑定Roll按钮点击事件
    rollButton.setOnAction(e -> {
        rollButton.setDisable(true);
        keepButton.setDisable(true);
        // 标记玩家选择继续掷骰(返回false)
        playerChoiceFuture.complete(false);
    });
    
    // 绑定Keep按钮点击事件
    keepButton.setOnAction(e -> {
        rollButton.setDisable(true);
        keepButton.setDisable(true);
        // 标记玩家选择保留点数(返回true)
        playerChoiceFuture.complete(true);
    });
}

2. 改造willHold方法为异步逻辑

将原同步方法改为返回CompletableFuture<Boolean>,让调用方异步等待玩家操作结果:

CompletableFuture<Boolean> willHold(Player p, int turnTotal){
    switch(p){
        case HumanPlayer hp -> {
            turnTotalText.setText("Turn Total: " + turnTotal + " | what will you do?");
            // 初始化异步结果容器
            playerChoiceFuture = new CompletableFuture<>();
            // 启用按钮,允许玩家操作
            rollButton.setDisable(false);
            keepButton.setDisable(false);
            return playerChoiceFuture;
        }
        case DumbAI da -> {
            // 笨AI逻辑:一直选择掷骰
            return CompletableFuture.completedFuture(false);
        }
        case ThresholdAI ta -> {
            // 阈值AI逻辑:达到设定点数则保留
            boolean shouldHold = turnTotal >= ta.getThreshold();
            return CompletableFuture.completedFuture(shouldHold);
        }
        default -> throw new IllegalStateException("Unexpected value: " + p);
    }
}

3. 游戏回合主流程调用

通过异步回调处理玩家选择,避免阻塞JavaFX UI线程:

private void processPlayerTurn(Player currentPlayer) {
    int turnTotal = 0;
    
    willHold(currentPlayer, turnTotal).thenAccept(shouldHold -> {
        if (shouldHold) {
            // 保留本轮点数,添加到玩家总分
            currentPlayer.addScore(turnTotal);
            // 切换到下一位玩家
            switchToNextPlayer();
        } else {
            // 执行掷骰子逻辑
            int diceResult = rollDice();
            if (diceResult == 1) {
                // 掷出1,清空本轮点数,结束回合
                turnTotal = 0;
                switchToNextPlayer();
            } else {
                // 累加点数,继续当前玩家回合
                turnTotal += diceResult;
                processPlayerTurn(currentPlayer);
            }
        }
    });
}

// 掷骰子工具方法
private int rollDice() {
    return new Random().nextInt(6) + 1;
}

4. 关键注意事项

  • UI线程安全:所有UI更新操作必须在JavaFX应用线程执行,若掷骰子逻辑放在后台线程,需用Platform.runLater()包裹UI更新代码
  • 按钮状态管控:确保只有在当前玩家为人类时才启用按钮,AI回合保持禁用状态,避免误操作
  • 避免阻塞UI:禁止在JavaFX应用线程中使用join()等阻塞方法,全程采用异步回调处理流程

替代方案:自定义回调接口

若不想使用CompletableFuture,可通过自定义回调接口实现:

// 定义回调接口
@FunctionalInterface
interface PlayerChoiceCallback {
    void onChoice(boolean shouldHold);
}

// 修改willHold方法
void willHold(Player p, int turnTotal, PlayerChoiceCallback callback) {
    switch(p){
        case HumanPlayer hp -> {
            turnTotalText.setText("Turn Total: " + turnTotal + " | what will you do?");
            // 启用按钮
            rollButton.setDisable(false);
            keepButton.setDisable(false);
            // 暂存回调
            this.currentCallback = callback;
        }
        case DumbAI da -> callback.onChoice(false);
        case ThresholdAI ta -> callback.onChoice(turnTotal >= ta.getThreshold());
        default -> throw new IllegalStateException("Unexpected value: " + p);
    }
}

// 修改按钮处理器
private PlayerChoiceCallback currentCallback;

private void handleRoll() {
    rollButton.setDisable(true);
    keepButton.setDisable(true);
    currentCallback.onChoice(false);
}

private void handleKeep() {
    rollButton.setDisable(true);
    keepButton.setDisable(true);
    currentCallback.onChoice(true);
}

// 调用方式
private void processPlayerTurn(Player currentPlayer) {
    int turnTotal = 0;
    willHold(currentPlayer, turnTotal, shouldHold -> {
        // 后续逻辑同异步方案
    });
}

内容的提问来源于stack exchange,提问作者Detinoy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:32:06