Лучшее решение для «строки уровня»?

10

У меня есть игра, которая генерирует случайную карту уровня в начале уровня. Я хочу реализовать какой-то способ сохранения и загрузки уровня.

Я думал, что, возможно, XML будет хорошим вариантом для сохранения всех переменных, тогда мне будет легко создать что-то, что сможет анализировать этот XML и генерировать точно такой же уровень.

Но XML, вероятно, излишний для моих нужд. Я помню, как в те времена со старой консолью Sega, у которой не было возможности сохранить вашу игру (я думаю, что игра Worms тоже это делала), они давали вам кучу персонажей, которые вы могли бы записать. Если вы нажмете эту строку позже, она загрузит точный уровень.

Будет ли «строка уровня» хорошим вариантом? Будет ли это какое-то преобразование "base60"? Как бы я это реализовал?

Адам Харт
источник

Ответы:

20

Предположительно все, что вам нужно сохранить, - это случайное семя, которое, как правило, представляет собой целое число. Вы можете закодировать int в base64, если хотите сделать его более непрозрачным, но это, вероятно, не нужно.

coderanger
источник
Вы также можете сгенерировать начальное значение из реального слова (возможно, купите сложение символов, чтобы получить свое начальное значение) и таким образом вернуть что-то более значимое. Вы должны были бы сохранить слова в своей игре или получить их, хотя.
Джонатан Фишофф
Это хорошо, но в игре также нужно хранить динамические данные. Строка должна была бы сохранить позиции символов / счет и т. Д. Так, каков наилучший способ генерировать эту строку?
Адам Харт
Я бы, вероятно, использовал JSON (или XML, если хотите) для сериализации состояния мира, а затем закодировал бы эту строку с помощью base64. Не уверен, что все это полезно, почему бы просто не сделать систему сохранения / загрузки, более ориентированную на GUI? Я предполагаю, что это веб-интерфейс, и вы не хотите обрабатывать эту сторону сервера. Может быть, посмотрите на shygypsy.com/farm/p.cgi для примера?
кодеренджер
23

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

Вы пожалеете об этом, если не будете.

tenpn
источник
7
+1 на самом деле не отвечает на вопрос, но так важно.
Джонатан Фишофф
10

JSON это хорошо, но YAML лучше. :) http://www.yaml.org/ и http://code.google.com/p/yaml-cpp/ для одной из лучших в использовании реализаций.

YAML - это расширенный набор JSON, который добавляет поддержку нескольких приятных функций, в частности:

  • Бинарные узлы. Это отлично подходит для сериализации типов данных, с которыми вы можете иметь дело для описания уровней. JSON требует, чтобы вы перевели на какой-то промежуточный формат по вашему выбору, например Base64, перед записью / после разбора. YAML имеет двоичный тип узла !!, который говорит библиотеке синтаксического анализатора сделать это за вас.
  • Внутридокументарные ссылки. Если один и тот же объект появляется дважды в документе, JSON напишет его дважды, а когда вы прочитаете его обратно, вы получите две копии. Многие излучатели YAML могут обнаружить эту ситуацию и вместо второй копии вывести ссылку на первую, когда это может быть обнаружено при загрузке.
  • Пользовательские типы узлов. Вы можете пометить каждую карту в списке, например, !! Player, !! Enemy и т. Д., И таким образом сохранить информацию о вашем типе более внеполосной.
  • YAML поддерживает более читаемое форматирование.
  • Поскольку JSON является подмножеством YAML, большинство читателей YAML не будут иметь проблем с чтением документов JSON.

источник
1
Yaml очень крутой, и если вы избежите некоторых функций надмножества, его можно без проблем конвертировать в json.
Джетро Ларсон
3

Если вы хотите сериализовать все данные в игре, я бы порекомендовал JSON в качестве формата файла, поэтому проще использовать XML и поддержка очень хорошая для многих языков.

Я использовал эту библиотеку для C ++, и она работает очень хорошо.

http://jsoncpp.sourceforge.net/

Джонатан Фишофф
источник
3

XML - хороший выбор, если вы не ограничены по размеру и поддерживаются изначально (например, в .NET и Flash), но если вы хотите тонкий формат, вы можете легко создать свой собственный формат и анализатор. Я обычно использую 1 символ, например. запятая для разделения каждого объекта. Для декодирования строки сделайте разделение на запятую. Теперь каждому объекту нужны разные свойства, поэтому разделяйте их с другим символом, например, точкой с запятой, и используйте другой символ, чтобы отделить имена свойств от значений свойств, например. Двоеточие. Таким образом, все можно легко декодировать без регулярных выражений, просто используя string.split. Вот пример:

id:1;x:5;y:45.2;angle:45,id:28;x:56;y:89;angle:12;health:78

Вы можете сэкономить еще больше места, сохранив имена свойств до 1 символа, например, h для здоровья. Например.

i:1;x:5;y:45.2;a:45,i:28;x:56;y:89;a:12;h:78

Сравните с альтернативой JSON:

{"o":[{"i":1, "x":5, "y":45.2, "a":45}, {"i":28, "x":56, "y":89, "a":12, "h":78}]}

Кроме того, если вы хотите уменьшить размер своих чисел, вы можете кодировать их, используя полный набор печатных символов UTF16. Эта тема вдохновила меня задать вопрос о переполнении стека о том, сколько данных вы можете упаковать в одного экранного персонажа . Кажется, что ответ где-то более 40 000 значений для целого числа, если вы не возражаете против использования брайля, кандзи и шахматных фигур: ♔♕♖♗♘♙♚♛♜♝♞♟

Чтобы получить дальнейшее уменьшение размера, вы можете использовать порядок чтения / записи, чтобы определить, какое значение является каким, поэтому первые два символа представляют идентификатор, следующие два - это позиция x, следующие два - y, затем угол, затем здоровье и т. д. Итак:

F5DGP@%&002DFTK#OP1F

мог хранить всю ту же информацию, что и другие примеры.

Сетка плиток может быть сохранена как просто строка с каждым символом, представляющим другой тип плитки, например:

i789pog5h3kl

где я мог бы означать лаву, 9 означает траву и т. д.

Iain
источник
Это больше похоже на то, что я спрашиваю. Я упоминаю XML в своем вопросе, но все же люди предлагают это!
Адам Харт
1
Добавьте {} к этому, и у вас в основном будет JSON ;-)
coderanger
Вам также нужно добавить множество кавычек. Вероятно, удвоит количество символов, но вы получите вложенность объектов.
Иан
1

Если вы кодируете в .Net, то с XML очень легко работать, так как вы можете сериализовать / десериализовать свой класс уровня в / из XML с помощью всего лишь нескольких строк, а затем все это в хорошо управляемом классе.

TheMap - это переменная типа Map, в которую загружены все ваши данные.

Dim TheMap As New Map

Предполагая, что у вас уже есть класс Map, это сохранит вашу карту в XML:

Dim Serializer As New System.Xml.Serialization.XmlSerializer(GetType(TheMap))
Dim Strm As New FileStream("c:\Map.xml", FileMode.Create, FileAccess.Write, FileShare.None)
Serializer.Serialize(Strm, TheMap)
Strm.Close()

Затем он загружает этот XML обратно в класс карты, чтобы снова использовать в коде.

Dim Reader As New StreamReader("map.xml")
Dim Serializer As New System.Xml.Serialization.XmlSerializer(GetType(TheMap))

TheMap = Serializer.Deserialize(Reader)
Reader.Close()

С этого момента ваш XML-файл теперь загружается в ваш класс для удобства использования.

Что касается вашей проблемы «Level String», то, что было сказано ранее, будет работать отлично, вы можете просто использовать номер Seed в качестве «Level String».

В противном случае вы можете просто предварительно сгенерировать сколько угодно разных карт и сохранить их все вместе с «Строкой уровня», а затем использовать ее, чтобы открыть нужную карту.

jblaske
источник
+1, хотя я ненавижу XML - если язык предоставляет формат сериализации по умолчанию, лучше сначала рассмотреть это, особенно если это формат, который теоретически могут анализировать другие инструменты (например, XML / JSON / YAML).
0

Я бы использовал простое structили похожее устройство (в зависимости от вашего языка) для хранения всех состояний игры в центральном месте. Если вы хотите защитить сеттеры / геттеры, вы можете заключить структуру в class.

Если вам это нравится , используйте битовые поля или просто выполняйте битовые манипуляции самостоятельно, используя битовые операторы.

Имейте в виду, что в некоторых языках правила для заполнения и упаковки структуры могут быть немного сложными, но это может также не иметь большого значения для вашего случая, если у вас есть один или два байта заполнения.

Вы также можете использовать #pragma(например, #pragma pack(1)) или a __attribute__для плотной упаковки структуры, исключая заполнение. Это может или не может работать в зависимости от вашего компилятора и целевой архитектуры.

Обратите внимание, что использование битовых полей и прагм или атрибутов пакетов может снизить переносимость. На всех аппаратных архитектурах порядковый номер поля структуры (порядок байтов) также может изменяться. Таким образом, вы можете избежать этого, если вы пытаетесь переносимости.

(Например, для Pac-Man эта структура может наивно содержать идентификатор карты или начальное значение карты, позицию Pac-Man x и y, четыре позиции ghost x и y и большое битовое поле для наличия или отсутствия 32-64 гранул, какой бы максимум ни был.)

Как только вы получите свою структуру, передайте ее что-то вроде функции xxencode :

encode_save( char * outStringBuf, size_t outStringBufSize,
             const SaveStruct * inSaveData, size_t inSaveDataSize )

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

Никогда не стоит недооценивать силу старой школы structв нужных местах. Мы использовали его для игр GBA и DS здесь.

Leander
источник
Сериализация сырой структуры непереносима и чрезвычайно хрупка по сравнению с новым кодом. Если это не является последним этапом обработки данных для платформы с очень ограниченными ресурсами, это очень преждевременная оптимизация. Есть ли причина, по которой вы предпочитаете 6 бит 8? Формат в любом случае не будет удобочитаемым, вы также можете воспользоваться скоростью и отладкой реальной структуры структуры.
@Joe: я упомянул, что это непереносимо и некоторые из потенциальных проблем. Вопрос специально задавался для удобочитаемой «строки уровня», как в старых играх Sega, и упоминал конверсию base60; Передача структуры через что-то вроде xxencode сделает это. Это не обязательно преждевременная оптимизация: простая структура, не упакованная в биты, является приятным «центральным» способом хранения сохраненных данных и может упростить большую часть кода, взаимодействующего с данными. См., Например, недавние статьи Ноэля Ллописа о структурах, не являющихся его членами, и программировании «наизнанку». Это просто поцелуй.
Леандр
1
Я полностью согласен с этим. Да, он не переносится ни на версии, ни на системы, но, опять же, save-игры не являются ресурсами, которые необходимо повторно использовать во время разработки или копировать на разные платформы. Отладка вряд ли является проблемой для чтения / записи на месте, реализуйте ее один раз, и она будет работать вечно. Если расширяемость действительно является проблемой - добавьте номер версии в качестве первого ybyte / word, и вы можете включить его (хотя это может привести к уязвимости данных). Дело не в том, что xml решит проблемы с версиями - они, как правило, более сложны, чем просто устанавливают значение. Да, я прагматичный.
Кай
0

XML подходит для произвольно структурированных документов (элементы могут отображаться на разных уровнях дерева) или для встраивания в сторонний формат (например, размещение svg на странице xhtml). Если у вас нет таких требований, это действительно неэффективный формат, и предпочтительнее использовать что-то более простое, например csv или json.

Джетро Ларсон
источник