如何在棋盘游戏中(反)序列化改变游戏规则的卡牌行为?



我正在做一个棋盘游戏。在每个回合中,玩家可以使用一张牌(支付一些代币(,例如,允许他们抽另一张牌,允许他们将他的棋子放在棋盘上忽略某些规则(仅适用于一回合(,允许他们重新掷骰子等等。我被要求从JSON文件中加载这些卡,以便可以在不重新编译/更新整个软件的情况下向游戏添加新卡牌,也因为"硬编码它们会很糟糕"。

  1. 这些卡有名称和成本,这很容易序列化。但是当涉及到序列化它们的行为和效果时,我迷失了。我想我可以想到这些卡提供的每个原子效应,将适当的原子效应存储在 JSON 字符串数组中(对于每张卡(,然后在方法useCard()中有一个巨大的开关大小写,它将循环处理效果数组。但我觉得这将是一个黑客,因为我仍然会在软件中硬编码效果,只是以一种更复杂的方式。如果我要添加一张具有全新行为的新卡(例如,将您的计算机扔出窗外(,我将不得不更新并重新分发软件。当我就此事质问我的主管时,他说:"嗯,你是对的,但至少你可以写一些调试卡并一次测试所有效果"。我仍然不相信。(反(序列化卡行为的更好方法是什么?

  2. 所有这些卡片都应该是同一类的 Java 实例,我们称之为 ActionCard。假设此类提供了一个useCard()的方法。但是,某些卡只有在满足某些条件或其效果需要参数时才可以使用(例如,如果卡允许您重新掷骰子,您想重新掷什么骰子?我怎样才能把所有这些复杂的行为放在useCard()大伞下?我想使用 varargs,然后开关情况会处理参数;我还想过重载具有大量版本的useCard()方法,但它仍然闻起来像黑客。

我知道这听起来很模糊,但我不能提供有关游戏本身的许多细节;如果您需要更多信息,请告诉我,我会更新/评论这个问题。但我认为同样的问题也适用于其他游戏,我首先想到的是《皇室战争》和《万智牌在线聚会》。 另外,我不需要工作代码,只需要来自其他人的一些想法和技巧。提前谢谢。

编辑:我正在使用gson作为序列化库。我已经写了一些TypeAdapter,所以我可以让它处理复杂的数据类型。

一种解决方案是创建自己的"编程语言"。将卡片的效果存储为字符串。然后,您可以编写一些代码来分析和解释字符串,并根据字符串更改游戏状态。一些例子是:

dealer draws 3 if hand < 5
dealer rerollDice 1
opponent loses 3
dealer ignoresRule 3 1

它们的意思是:

dealer can draw 3 cards if he has less than 5 cards
dealer re-rolls the first dice
opponent loses 3 of his cards randomly
dealer can ignore rule number 3 (whatever that maybe) for one turn

我在这里提出了这个相当简单的系统。第一个词是效果影响谁,第二个词是命令,之后的所有内容,但在单词if之前是命令的参数。

显然,这只是一个例子。您可能希望设计语言,以便它可以用于控制游戏的各个方面。一个好的起点是看看你的Player课(你可能有一个对吗?你的Player课有什么方法?这可能是一个很好的起点。

drawsrerollDice等"命令"创建类,它们可能应该实现一个通用接口,如Command

interface Command {
void execute(String[] args, GameState state) {
// mutate game state here
}
}

您的"语言"还可以允许以程序方式执行多个语句。因此,而不是创建一个名为drawsAndLoses的新命令:

dealer drawsAndLoses 3 1
// dealer can draw 3 cards but then loses one card randomly

你可以用两个"语句"做同样的事情:

dealer draws 3
dealer loses 1

最合适的概念是你的类 ActionCard 包含一个 Effect 列表,其中 Effect 将是带有方法 perform(( 的接口。 当然,您必须用它们采用的参数写下所有可能的效果,并开发许多效果实现。

这样做的结果是序列化必须是多态的。

要知道要反序列化的对象类型,在序列化时,还要编写对象标记。 在最简单的形式中,它可以是 Effect 实现的完全限定名称。

下面是一个简单的示例。假设您将要读取一个项目列表,其中每个项目可以是字符串、整数或日期。 您的列表可以序列化为:

[
{"type": "java.lang.String", "value": "Hello"},
{"type": "java.lang.String", "value": "World"}
{"type": "java.lang.Integer", "value": 123}
{"type": "java.lang.String", "value": "Hi!"},
{"type": "java.util.Date", "value": {"year": 2018, "month": 5, "day": 12}},
{"type": "java.lang.Integer", "value": 456}
]

类型字段是我们的标识标签。根据它包含的内容,您知道要在值字段中找到哪种对象。 当然,使用字符串、数字或枚举作为标签要安全得多,尤其是在玩家可以修改序列化数据的情况下;但现在至少你有了基本的想法。

最新更新