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
相关产品推荐
相关产品推荐

