Как (де) сериализовать изменяющее игру поведение карт в настольной игре? - PullRequest
0 голосов
/ 12 мая 2018

Я работаю над настольной игрой.В течение каждого хода игрок может использовать карту (заплатив несколько жетонов), которая, например, позволит ему взять другую карту, позволит ему разместить свою пешку на доске, игнорируя какое-то правило (только за ход), и позволяет емуперекатить кубик и так далее.Меня попросили загрузить эти карты из файла JSON, чтобы можно было добавлять новые игры в игру без перекомпиляции / обновления всего программного обеспечения, а также потому, что «жесткое их кодирование было бы плохо».

  1. карты имеют название и стоимость, и это довольно просто сериализовать.Но когда дело доходит до сериализации их поведения и их последствий, я теряюсь.Я подумал, что мог бы подумать о каждом элементарном эффекте, который обеспечивают эти карты, сохранить соответствующие в массиве строк JSON (для каждой карты), а затем иметь огромный регистр переключателя в методе useCard(), который будет циклически повторять массив эффектов.Но я чувствую, что это будет взломом, так как я все еще буду жестко программировать эффекты в программном обеспечении, только более сложным способом.Если бы я добавил новую карту с совершенно новым поведением (например, выбросил компьютер из окна), мне пришлось бы обновлять и распространять программное обеспечение.Когда я сказал об этом своему руководителю, он сказал: «Ну, вы правы, но, по крайней мере, вы могли бы написать несколько отладочных карт и протестировать все эффекты сразу».Я до сих пор не убежден.Как лучше (де) сериализовать поведение карты?

  2. все эти карты должны быть экземпляром Java одного и того же класса, назовем его ActionCard.Предположим, этот класс предлагает метод useCard().Однако некоторые карты можно использовать только при соблюдении определенных условий, или для их эффекта необходим параметр (например, если карта позволяет перебрасывать кубик, какой кубик вы хотите перебросить?).Как я могу поместить все это сложное поведение под большой зонтик useCard()?Я подумал об использовании varargs, и тогда переключатель позаботится о параметрах;я также думал о перегрузке метода useCard(), имеющего массу версий, но он все еще пахнет хаком.

Я знаю, это звучит очень расплывчато, но я не могу дать многоподробности о самой игре;если вам нужно больше информации, дайте мне знать, и я буду обновлять / комментировать вопрос.Но я думаю, что этот же вопрос может относиться и к другим играм, в первую очередь мне пришло в голову Clash Royale и Magic the Gathering Online.Кроме того, мне не нужен рабочий код, просто некоторые идеи и советы от других людей.Заранее спасибо.

РЕДАКТИРОВАТЬ: я использую gson в качестве библиотеки сериализации.Я уже написал TypeAdapter s, чтобы я мог заставить его работать со сложными типами данных.

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

Наиболее подходящей концепцией будет то, что ваш класс ActionCard содержит список Effect, где Effect будет интерфейсом с методом execute ().Вам, конечно, нужно будет записать все возможные эффекты с теми параметрами, которые они принимают, и разработать множество реализаций Effect.

Следствием этого является то, что ваша сериализация должна быть полиморфной.

Чтобы узнать, какой тип объекта вы собираетесь десериализовать, во время сериализации также напишите тег объекта.В простейшей форме это может быть полное имя реализации 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}
]

Поле типа является нашим идентифицирующим тегом.В зависимости от того, что он содержит, вы знаете, какой тип объекта вы найдете в поле значения.Конечно, гораздо безопаснее использовать строку, число или перечисление в качестве тега, особенно если игроки могут модифицировать сериализованные данные;но теперь, по крайней мере, у вас есть основная идея.

0 голосов
/ 12 мая 2018

Одним из решений является создание собственного «языка программирования».Вы сохраняете эффект карт в виде строки.Затем вы можете написать некоторый код, который анализирует и интерпретирует строку и изменяет состояние игры в соответствии со строкой.Вот некоторые примеры:

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 класса?Это, вероятно, хорошее место для начала.

Создайте классы для «команд», таких как draws и rerollDice, и они, вероятно, должны реализовать общий интерфейс, такой как 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
...