【问题标题】:Waiting for Sub Tasks to Finish in JavaFX在 JavaFX 中等待子任务完成
【发布时间】:2016-01-18 15:55:36
【问题描述】:

我正在尝试使用 JavaFX 创建一个简单的游戏。游戏由主游戏和子游戏组成,玩家可能必须玩这些游戏,这取决于主游戏的结果。最后,主游戏必须根据子游戏的结果更新其状态(例如:得分)。

我对我如何实现游戏逻辑做了一个简化和概括的版本:

Result play(Player p) {
    Result r = p.play(this);
    for(SubGame game : r.getSubGames())
    {
        p.play(game);
    }
    update(r);
    return r;
}

该游戏在终端中完美运行,因为它具有线性执行。 但是使用JavaFX(在播放器中实现),由于游戏循环,我无法正确控制程序的流程。

我已按照this 教程处理主游戏和子游戏的多个屏幕。 Player 类可以使用处理程序成功地将屏幕更改为子游戏。但更新不再等待子游戏播放完毕,该函数在玩家游戏中途返回。

我试图将游戏逻辑与 UI 分开,因此对上面显示的代码的更改不应依赖于界面框架。

有什么帮助吗?

【问题讨论】:

    标签: java javafx-8


    【解决方案1】:

    使用事件驱动的方法,设置可观察属性的值,并在它们发生变化时做出响应。

    例如,你可以用

    封装游戏的状态
    public class GameState {
    
        private ObservableList<SubGame> currentGames = FXCollections.observableArrayList();
    
        public ObservableList<SubGame> getCurrentGames() {
            return currentGames();
        }
    
        private ReadOnlyObjectWrapper<SubGame> currentGame = new ReadOnlyObjectProperty<>();
    
        public ReadOnlyObjectProperty<SubGame> currentGameProperty() {
            return currentGame.getReadOnlyProperty() ;
        }
    
        public final SubGame getCurrentGame() {
            return currentGameProperty().get();
        }
    
        public GameState() {
            // initialize sub game list...
        }
    
        public void nextGame() {
            int index = currentGames.indexOf(currentGame.get());
            if (index < currentGames.size() - 1) {
                currentGame.set(currentGames.get(index + 1));
            }
        }
    
        public void start() {
            currentGame.set(currentGames().get(0));
        }
    
        public boolean hasMoreGames() {
            return currentGames.indexOf(currentGame.get()) < currentGames.size() - 1 ;
        }
    }
    

    同样,您的 SubGame 类中可能有一些可观察到的状态:

    public class SubGame {
    
        private final BooleanProperty finished = new SimpleBooleanProperty();
    
        public BooleanProperty finishedProperty() {
            return finished ;
        }
    
        public final boolean isFinished() {
            return finishedProperty().get();
        }
    
        public final void setFinished(boolean finished) {
            finishedProperty().set(finished) ;
        }
    
        // ...
    }
    

    现在你的游戏逻辑只是用监听器实现了:

    void play(Player p) {
        Result r = p.play(this);
        GameState gameState = new GameState();
        gameState.currentGameProperty().addListener((obs, oldGame, newGame) -> {
            newGame.finishedProperty().addListener((obs, wasFinished, isNowFinished) -> {
                if (isNowFinished) {
                    // maybe update score etc based on state of newGame...
                    if (gameState.hasMoreGames()) {
                        gameState.nextGame();
                    } else {
                        // logic here for "all games are finished...
                    }
                }
            });
        });
        gameState.start();
    }
    

    显然,您如何实现这一点的细节取决于您的需求等,但这种通用方法应该适用于您需要的任何东西。

    【讨论】:

    • 如果你需要一个具体的例子,我有一个人类与计算机井字游戏(noughts and crosses)here,它遵循类似的原则。
    • 感谢您的完整回答。我想我明白了,但是仍然存在将 UI 相关的东西与游戏逻辑混合的问题,我想避免这种情况。除此之外,即使正在播放奖金,此方法也会立即返回,对吧?
    • 方法会立即返回,是的。 UI 与逻辑混合在哪里?我展示的SubGameGameState 类只包含逻辑,没有UI 组件。
    • 也许吧,尽管实际上没有什么可以阻止您在任何模型中使用它们(它们不依赖于启动 FX 工具包等,并且实际上只是为延迟通知设计的普通 java 对象)。但如果您愿意,只需在模型中使用绑定的 JavaBean,在 JavaFX UI 代码中使用 JavaBeanPropertyAdapter 类。原则还是一样的:你不能阻塞 UI 线程来等待:UI 不会被渲染,所以你必须使用某种事件驱动的通知订阅者方法。跨度>
    • 理论上,我认为这可行,但它会变得棘手,因为用户输入必须来自 FX 应用程序线程(处理用户事件的地方)。因此,当从两个线程访问状态时,您必须注意正确管理状态。此外,Platform.runLater(...) 需要运行 FX 工具包,因此(至少在这种方法的幼稚实现中),最终模型与 JavaFX 的耦合比使用 FX 属性更紧密。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-18
    • 2016-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-07
    相关资源
    最近更新 更多