任何模拟棋盘游戏的模式?
为了好玩,我试图将我儿子最喜欢的一款棋盘游戏写成一款软件。 最终,我希望在其上构建一个WPF UI,但现在我正在构建模拟游戏及其规则的机器。
当我这样做时,我一直看到我认为很多棋盘游戏常见的问题,也许其他人已经比我更好地解决了它们。
(请注意,AI玩游戏,高性能模式对我来说并不有趣。)
到目前为止,我的模式是:
表示游戏盒中的实体的几种不可变类型,例如骰子,跳棋,卡片,棋盘,棋盘上的空间,金钱等。
每个玩家的对象,包含玩家资源(例如金钱,分数),他们的名字等。
表示游戏状态的对象:玩家,轮到它的人,棋盘上的peices的布局等等。
管理转向序列的状态机。 例如,许多游戏都有一个小游戏,每个玩家都会先看看谁先走; 这是开始状态。 当一个玩家轮到他们时,他们先滚动,然后他们移动,然后他们必须跳起来,然后其他玩家猜猜他们是什么品种的鸡,然后他们得到分数。
是否有一些我可以利用的现有技术?
编辑:我最近意识到的一件事是游戏状态可以分为两类:
游戏神器状态 。 “我有10美元”或“我的左手是蓝色的”。
游戏顺序状态 。 “我打了两次双打;下一次让我入狱”。 状态机可能在这里有意义。
编辑:我真正在这里寻找的是实现多人回合制游戏如国际象棋或拼字游戏或垄断的最佳方式。 我相信我可以通过开始完成这个游戏来创建这样的游戏,但是,像其他设计模式一样,可能有一些方法可以使事情顺利得多,而且没有仔细研究就不会明显。 这就是我所希望的。
看起来这是我刚才注意到的一个2个月大的线程,但是到底是什么。 我之前设计并开发了一个商业,网络棋盘游戏的游戏玩法框架。 我们有一个非常愉快的经验与它合作。
你的游戏可能处于(接近)无限多的状态,因为玩家A有多少钱,玩家B有多少钱等等,所以我很确定你想要远离状态机。
我们框架背后的想法是将游戏状态表示为结构,并且所有数据字段一起提供完整的游戏状态(即:如果您想将游戏保存到磁盘,则将该结构写出)。
我们使用命令模式来表示玩家可以制作的所有有效游戏动作。 这将是一个示例操作:
class RollDice : public Action
{
public:
RollDice(int player);
virtual void Apply(GameState& gameState) const; // Apply the action to the gamestate, modifying the gamestate
virtual bool IsLegal(const GameState& gameState) const; // Returns true if this is a legal action
};
所以你看到要决定一个移动是否有效,你可以构造这个动作,然后调用它的IsLegal函数,传递当前的游戏状态。 如果它是有效的,并且玩家确认该动作,则可以调用应用功能来实际修改游戏状态。 通过确保您的游戏代码只能通过创建并提交合法的Actions来修改游戏状态(换句话说,Action :: Apply系列方法是唯一直接修改游戏状态的方法),那么您可以确保您的游戏状态永远不会失效。 此外,通过使用命令模式,可以序列化玩家所需的动作,并通过网络发送给其他玩家的游戏状态。
最终,这个系统陷入了一个难题,后来证明它有一个相当优雅的解决方案。 有时行动会有两个或更多的阶段。 例如,玩家可能会在大富翁的地产上登记,现在必须作出新的决定。 玩家掷骰子之间以及他们决定购买房产之前的游戏状态是什么? 我们通过设置游戏状态的“Action Context”成员来管理这种情况。 动作上下文通常为空,表示游戏目前没有处于任何特殊状态。 当玩家掷骰子并且将骰子滚动动作应用于游戏状态时,它将意识到玩家已经落在非拥有的财产上,并且可以创建包含玩家的索引的新的“PlayerDecideToPurchaseProperty”行为上下文我们正在等待一个决定。 到RollDice动作完成时,我们的游戏状态表示它当前正在等待指定玩家决定是否购买房产。 除了“BuyProperty”和“PassPropertyPurchaseOpportunity”操作之外,所有其他操作的IsLegal方法都很容易返回false,这些操作仅在游戏状态具有“PlayerDecideToPurchaseProperty”操作上下文时才合法。
通过使用动作上下文,棋盘游戏的生命周期永远不会有一个单一点,其中游戏状态结构并不完全代表该时间点游戏中正在发生的事情。 这是你的棋盘游戏系统非常理想的属性。 通过检查只有一个结构,当你能够找到你想知道的关于游戏中发生的事情的东西时,你会更容易编写代码。
此外,它非常适合于网络环境,客户可以通过网络向主机提交他们的动作,主机可以将动作应用到主机的“官方”游戏状态,然后将该动作回送给所有其他客户端让他们将它应用到他们复制的游戏状态。
我希望这是简洁而有用的。
游戏引擎的基本结构使用状态模式。 你的游戏盒的物品是各种类的单身。 每个州的结构可以使用策略模式或模板方法。
工厂用于创建插入队员名单中的球员,这是另一个单身人士。 GUI将通过使用Observer模式继续监视游戏引擎,并通过使用Command模式创建的几个Command对象之一与此进行交互。 Observer和Command的使用可以在被动视图的上下文中使用,但根据您的偏好,可以使用任何MVP / MVC模式。 当你保存游戏时,你需要获得当前状态的纪念品
我建议查看本网站上的一些模式,看看他们中的任何人是否抓住你作为起点。 游戏板的核心将再次成为一台状态机。 大多数游戏将由游戏前/安装和实际游戏中的两个状态来表示。 但是如果你正在建模的游戏有几种不同的游戏模式,你可以拥有更多的状态。 各国并不一定是连续的,例如战争游戏Axis&Battles有一个战斗板可供玩家解决战斗。 所以有三个国家在游戏前,主板,游戏主板之间不断切换主板和战斗板。 当然,转向顺序也可以用状态机来表示。
我刚刚完成了使用多态性设计和实现基于状态的游戏。
使用一个名为GamePhase
的基本抽象类,它有一个重要的方法
abstract public GamePhase turn();
这意味着每个GamePhase
对象都保持游戏的当前状态,并且对turn()
的调用turn()
查看其当前状态并返回下一个GamePhase
。
每个具体的GamePhase
都有构造函数来保存整个游戏状态。 每个turn()
方法都有一些内部的游戏规则。 虽然这扩大了规则,但它将相关规则保持在一起。 每turn()
的最终结果就是创建下一个GamePhase
并将完整状态传递到下一个阶段。
这允许turn()
非常灵活。 根据你的游戏,一个给定的状态可以分支到许多不同类型的阶段。 这形成了所有游戏阶段的图表。
在最高级别上,驱动它的代码非常简单:
GamePhase state = ...initial phase
while(true) {
// read the state, do some ui work
state = state.turn();
}
这非常有用,因为我现在可以轻松创建任何游戏的状态/阶段进行测试
现在回答你的问题的第二部分,这是如何工作在多人游戏? 在某些需要用户输入的GamePhase
,来自turn()
的调用会根据当前状态/阶段向当前Player
询问他们的Strategy
。 Strategy
只是一个Player
可以做出的所有可能决策的界面。 这个设置也允许Strategy
与AI一起实施!
Andrew Top也表示:
你的游戏可能处于(接近)无限多的状态,因为玩家A有多少钱,玩家B有多少钱等等,所以我很确定你想要远离状态机。 我认为这种说法很具误导性,但确实有很多不同的游戏状态,只有少数游戏阶段。 为了处理他的例子,所有它将是我的具体GamePhase
的构造函数的整数参数。
垄断
一些GamePhase
的例子是:
基地GamePhase
一些州是:
然后有些阶段会根据需要记录他们自己的状态,例如PlayerRolls会记录一名球员连续翻倍的次数。 一旦我们离开PlayerRolls阶段,我们不再关心连续的掷骰了。
许多阶段可以重复使用并链接在一起。 例如, GamePhase
CommunityChestAdvanceToGo
会用当前状态创建下一阶段PlayerLandsOnGo
并返回它。 在PlayerLandsOnGo
的构造函数中,当前玩家将被移动到Go,他们的钱会增加200美元。