回合制游戏设计:事件驱动与游戏循环

13

我正在使用Java创建我的第一个游戏,它是大富翁。我正在考虑如何设计游戏以建模其回合制结构(管理玩家回合)。我想允许单个人类控制和一个或多个AI控制的玩家参与游戏。

我遇到的具体问题是,我不知道是否要实现游戏循环,也就是可以直接管理与Monopoly游戏相关的变量和玩家的循环(例如提示每个玩家进行回合、将回合递增至下一位玩家或获取每个玩家的骰子点数——依次进行)。我不是指更低级别意义上的“游戏循环”,它更涉及在屏幕上绘制帧、更新物理或以特定时间率更新AI。

我理解我尝试实现所需内容的选项为:

  1. 实现完全事件驱动程序,没有这样的游戏循环
  2. 实现游戏循环——作为后台长期运行并基本上永远不会停止,只要游戏在运行。这将是更过程化的方法。

当我开始尝试解决这个问题时,我遇到了UI冻结的问题,因为我的游戏循环是无休止的,并且完全占用了它运行的线程(我只是创建一个非常简单的while循环来说明这一点)。因此,我努力创建了一个SwingWorker来封装我的游戏循环。这解决了UI冻结问题,但仍让我想知道我是否走错了方向。

通常,我发现网上的大多数建议似乎青睐于任何事件驱动的方法,因此我目前的实现使用SwingWorker可能是朝错误的方向迈出的一步。但是,我无法完全理解如何为此特定任务实现完全事件驱动的系统(没有游戏循环)。在某个地方必须存在一个循环以管理玩家回合。

以下是我的具体问题:

  1. 像大富翁这样的回合制游戏是否适合使用游戏循环(我所描述的)——特别是用于排队玩家回合并提示适当的玩家进行回合,每次一个玩家(和排队整个回合的过程/步骤序列)?
  2. 如果要创建纯事件驱动系统来管理玩家回合,如何迭代每个玩家以提示他们进行回合,并保持迭代直到游戏结束?
  3. 如果要使用游戏循环来解决上述特定问题,是否必须在自己的线程中运行它(可能使用SwingWorker)以避免UI冻结?我的情况是Java特定的,但我想我也会对非Java特定情况的答案感兴趣。

目前,我正在使用MVC模式组织我的代码。我的控制器是我的游戏循环(实际上是SwingWorker线程)所在的位置。它远未完成,但它有助于说明我如何在我所称的“游戏循环”中管理玩家回合。

控制器中的SwingWorker代码:

swingWorker = new SwingWorker<Void, Model>() {
@Override
protected Void doInBackground() throws InterruptedException {
gameLoopRunning = true;
while (gameLoopRunning) {

    //to do: use a timer instead of thread.sleep
    Thread.sleep(1000);

    //user turn prompt
    if (model.getActivePlayer().isUserControlled()) {

        boolean userRolled = false;
        while(!userRolled) {
            System.out.println("Roll the dice please...");
            Thread.sleep(3000);
        }

    }
    //bot turn prompt
    else {
        //insert code for bot rolling dice here
        model.rollDice();
    }

    publish(model);

    Thread.sleep(1000);
    model.incrementPlayerTurn();
    publish(model);

}
return null;
}

@Override
protected void process(List<Model> chunks) {
Model gameModel = chunks.get(chunks.size() - 1);
//hard-coded for 6 players
for (int i = 0; i < 6; i++) {
    view.getPlayerPanel(i).setTurn(gameModel.getPlayers().get(i).isTurn());
}
view.getGamePanel().getDice().setDie1(model.getDie1());
view.getGamePanel().getDice().setDie2(model.getDie2());
}

};
swingWorker.execute();

“大富翁”游戏与国际象棋相差不多,对我来说让它成为事件驱动更有意义,因为这将使规则实现变得更容易,就像在国际象棋中一样,因为每个事件都试图影响某些状态。 - arynaq
无论你如何实现,你可能不希望游戏玩法的 CPU 循环在 UI 线程上执行。 - Tim
1
使用有限状态机来表示游戏状态变化怎么样?我曾经使用一个简单的有限状态机开发跳棋游戏,并配合基于事件的UI代码。 - SirDarius
1个回答

11

SirDarius 的评论很到位。

但是,对于像推进玩家回合这样简单的事情,你不需要费心实现一个完整的有限状态机。

在MVC方面,针对人类玩家,应该采取以下做法:

  • The Model: 提供将活动玩家前进到下一个玩家并运行“回合过程”(即掷骰子、移动活动玩家的令牌等)的方法。由于大部分回合过程是基于事件驱动的,因此这些方法调用将从控制器中的事件监听器中进行。

  • The View: 当活动玩家完成其回合时引发事件,以及根据各种其他输入引发事件。

  • The Controller: 当玩家完成其回合时,告诉模型进入下一个玩家,并重新开始“回合过程”。每当玩家提供输入时,都会触发一个事件,告诉模型进入回合的下一个阶段。

对于AI玩家,可以使用大部分相同的代码,但是没有必要让回合进度由视图驱动。相反,模型需要另一个“回合过程”方法,专门用于AI玩家。唯一的区别是代码将按顺序执行,而不是作为一系列事件监听器。


我不理解你关于视图的评论。我可以理解当按钮被点击时视图会触发事件,但是就《大富翁》游戏中回合开始和结束而言,可能没有专门的按钮来开始或结束回合。此外,那些人工智能机器人开始和结束他们的回合怎么办呢?显然,它们并不涉及视图来管理回合,对吧? - nairware
@nairware 在大富翁游戏中,玩家的回合遵循自然进程。只有那么多任务需要执行,其中一个是任何给定回合的最后一个任务。通常情况下,玩家会掷骰子、移动棋子、执行操作(购买财产、支付罚款、抽卡等)、购买房屋,以及(可选)进行交易。如果玩家被监禁,则回合流程会发生变化,但仍有最后一个动作需要执行。当玩家完成最后一个动作(或者点击上下文中的“结束回合”按钮)时,回合可能会结束。 - Luke Willis
我明白了。当然,结束回合的事件是有意义的。我只是不清楚为什么视图必须与该事件有关。我认为你假设有一个“结束回合”按钮,这种情况下你对视图的描述是有道理的。但是,由 AI 控制的玩家不会点击这样的按钮,因此你也假设只有人类控制的玩家。相同的结束回合事件将为由 AI 控制的玩家触发,但不是来自按钮点击或任何与视图相关的东西(至少在我的估计中)。 - nairware
我同意。对于人类玩家来说,回合进程由UI驱动是有意义的,但对于AI玩家来说,则应由控制器驱动。我会更新我的答案,以指定AI回合的任务。 - Luke Willis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接