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

时间:2018-05-12 15:14:21

标签: java oop serialization game-development

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

  1. 这些卡片有名称和成本,序列化非常简单。但是当谈到序列化他们的行为及其影响时,我就迷失了。我想我可以想到这些卡提供的每个原子效应,将相应的那些存储在JSON字符串数组中(对于每张卡),然后在方法useCard()中有一个巨大的开关案例,它将循环遍历数组效果。但我觉得这将是一个黑客,因为我仍然会以更复杂的方式硬编码软件中的效果。如果我要添加一个全新行为的新卡(例如将您的计算机扔出窗外),我必须更新并重新分发该软件。当我与我的主管面对此问题时,他说:“嗯,你是对的,但至少你可以写一些调试卡并立刻测试所有效果”。我还是不相信。什么是(de)序列化卡片行为的更好方法?

  2. 所有这些卡应该是同一个类的Java实例,我们称之为ActionCard。假设此类提供方法useCard()。但是,有些卡只有在满足某些条件时才能使用,或者它们的效果需要一个参数(例如,如果卡片允许你重新掷骰子,你想重新掷骰子是什么?)。我怎样才能将所有这些复杂的行为置于useCard()大伞下?我想过使用varargs,然后开关盒会处理参数;我还想过重载useCard()方法,它有很多版本的版本,但它仍然有点黑客的味道。

  3. 我知道这听起来很模糊,但我无法提供有关游戏本身的许多细节;如果你需要更多的信息让我知道,我会更新/评论这个问题。但我认为同样的问题也适用于其他游戏,第一个让我想到的是Clash Royale和Magic the Gathering Online。 此外,我不需要工作代码,只需要其他人的想法和提示。提前谢谢。

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

2 个答案:

答案 0 :(得分:0)

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

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课有哪些方法?这可能是一个很好的起点。

为&#34;命令创建类&#34;比如drawsrerollDice,他们应该实现像Command这样的通用界面:

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

你的语言&#34;也可以允许多个语句在程序上执行。因此,而不是创建一个名为drawsAndLoses的新命令:

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

你可以用两个&#34;语句&#34;:

做同样的事情
dealer draws 3
dealer loses 1

答案 1 :(得分:0)

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

结果是你的序列化必须是多态的。

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

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

[
  {"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}
]

type字段是我们的识别标签。根据它包含的内容,您可以知道要在值字段中找到的对象类型。 当然,使用字符串,数字或枚举作为标记会更安全,特别是如果玩家可以修改序列化数据;但现在至少你有基本的想法。