Я разрабатываю 2D-платформер с друзьями из университета. Мы основали его на XNA Platformer Starter Kit, который использует файлы .txt для хранения карты тайлов. Хотя это очень просто, это не дает нам достаточного контроля и гибкости при проектировании уровней. Несколько примеров: для нескольких слоев контента требуется несколько файлов, каждый объект прикреплен к сетке, не допускает вращения объектов, ограниченного числа символов и т. Д. Поэтому я провожу некоторое исследование о том, как хранить данные уровня и файл карты.
Это касается только хранения файловой системы карт тайлов, а не структуры данных, которая будет использоваться игрой во время ее работы. Карта листов загружается в 2D-массив, поэтому вопрос заключается в том, из какого источника заполнить массив.
Обоснование БД: С моей точки зрения, я вижу меньшую избыточность данных при использовании базы данных для хранения данных тайлов. Плитки в одной и той же позиции x, y с одинаковыми характеристиками можно повторно использовать от уровня к уровню. Кажется, что было бы достаточно просто написать метод для извлечения всех плиток, которые используются на определенном уровне, из базы данных.
Причины для JSON / XML: визуально редактируемые файлы, изменения можно отслеживать через SVN намного проще. Но есть повторное содержание.
Есть ли какие-либо недостатки (время загрузки, время доступа, память и т. Д.) По сравнению с другими? А что обычно используется в промышленности?
В настоящее время файл выглядит так:
....................
....................
....................
....................
....................
....................
....................
.........GGG........
.........###........
....................
....GGG.......GGG...
....###.......###...
....................
.1................X.
####################
1 - Начальная точка игрока, X - Уровень выхода,. - Пустое место, # - Платформа, G - Gem
Ответы:
Итак, читая обновленный вопрос, вы, похоже, больше всего обеспокоены «избыточными данными» на диске, с некоторыми второстепенными проблемами относительно времени загрузки и того, что используется в отрасли.
Во-первых, почему вы беспокоитесь о лишних данных? Даже для раздутого формата, такого как XML, было бы довольно легко реализовать сжатие и уменьшить размер ваших уровней. У вас больше шансов занять место с текстурами или звуками, чем с данными об уровне.
Во-вторых, двоичный формат, скорее всего, будет загружаться быстрее, чем текстовый формат, который вы должны проанализировать. Вдвойне так, если вы можете просто поместить его в память и иметь свои структуры данных просто там. Однако в этом есть некоторые недостатки. С одной стороны, двоичные файлы практически невозможно отладить (особенно для людей, создающих контент). Вы не можете различать или версия их. Но они будут меньше и загружаться быстрее.
То, что делают некоторые движки (и то, что я считаю идеальной ситуацией) - это реализация двух путей загрузки. Для разработки вы используете какой-то текстовый формат. На самом деле не имеет значения, какой конкретный формат вы используете, если вы используете надежную библиотеку. Для выпуска вы переключаетесь на загрузку бинарной (меньшей, более быстрой загрузки, возможно, лишенной отладочной сущности) версии. Ваши инструменты для редактирования уровней выплевывают оба. Это намного больше работы, чем просто один формат файла, но вы можете получить лучшее из обоих миров.
Несмотря на все сказанное, я думаю, что вы немного прыгаете.
Шаг первый к этим проблемам - всегда подробно описывайте проблему, с которой вы столкнулись. Если вы знаете, какие данные вам нужны, тогда вы знаете, какие данные вам нужно хранить. Оттуда это проверяемый вопрос оптимизации.
Если у вас есть сценарий использования для тестирования, вы можете протестировать несколько различных методов хранения / загрузки данных, чтобы оценить, что вы считаете важным (время загрузки, использование памяти, размер диска и т. Д.). Не зная ничего из этого, трудно быть более конкретным.
источник
Я рекомендую использовать пользовательский двоичный формат файла. В моей игре у меня просто есть
SaveMap
метод, который проходит и сохраняет каждое поле, используяBinaryWriter
. Это также позволяет вам выбрать сжатие, если вы хотите, и дает вам больше контроля над размером файла. т.е. сохранитьshort
вместо вместо,int
если вы знаете, что он не будет больше 32767. В большом цикле сохранение чего-либоshort
вместо вместоint
может означать намного меньший размер файла.Кроме того, если вы идете по этому пути, я рекомендую вашей первой переменной в файле будет номер версии.
Рассмотрим, например, класс карты (очень упрощенный):
Теперь предположим, что вы хотите добавить новое свойство на карту, скажем,
List
изPlatform
объектов. Я выбрал это, потому что это немного сложнее.Прежде всего, вы увеличиваете
MapVersion
и добавляетеList
:Затем обновите метод сохранения:
Затем, и вот где вы действительно видите преимущество, обновите метод загрузки:
Как видите,
LoadMaps
метод может загружать не только последнюю версию карты, но и более старые версии! Когда он загружает старые карты, вы можете контролировать значения по умолчанию, которые он использует.источник
Короткий рассказ
Хорошая альтернатива этой проблеме - хранить уровни в растровом изображении, по одному пикселю на плитку. Использование RGBA позволяет легко сохранять на одном изображении четыре разных измерения (слои, идентификаторы, вращение, оттенок и т. Д.).
Длинная история
Этот вопрос напомнил мне о том, когда Нотч в прямом эфире записал свою запись Ludum Dare несколько месяцев назад, и я хотел бы поделиться, если вы не знаете, что он сделал. Я думал, что это было действительно интересно.
В основном он использовал растровые изображения для хранения своих уровней. Каждый пиксель в растровом изображении соответствовал одной «плитке» в мире (на самом деле он не был плиткой, так как это была лучевая игра, но достаточно близкая). Пример одного из его уровней:
Поэтому, если вы используете RGBA (8 бит на канал), вы можете использовать каждый канал в качестве отдельного слоя и до 256 типов плиток для каждого из них. Будет ли этого достаточно? Или, например, один из каналов может содержать вращение для плитки, как вы упомянули. Кроме того, создание редактора уровней для работы с этим форматом должно быть довольно тривиальным.
И что самое приятное, вам даже не нужен какой-либо пользовательский контент-процессор, поскольку вы можете просто загрузить его в XNA, как любой другой Texture2D, и прочитать пиксели обратно в цикле.
IIRC он использовал чистую красную точку на карте, чтобы сигнализировать противнику, и в зависимости от значения альфа-канала для этого пикселя он будет определять тип врага (например, значение альфа 255 может быть летучей мышью, в то время как 254 будет зомби или что-то еще).
Другой идеей было бы разделить ваши игровые объекты на те, которые зафиксированы в сетке, и те, которые могут «плавно» перемещаться между плитками. Сохраняйте фиксированные плитки в растровом изображении, а динамические объекты - в списке.
Может даже быть способ мультиплексировать еще больше информации в эти 4 канала. Если у кого-то есть идеи по этому поводу, дайте мне знать. :)
источник
Вы могли бы сойти с рук почти все. К сожалению, современные компьютеры настолько быстры, что эти предположительно относительно небольшие объемы данных не будут иметь заметной разницы во времени, даже если они хранятся в раздутом и плохо построенном формате данных, который требует огромного объема обработки для чтения.
Если вы хотите сделать «правильную» вещь, вы создаете двоичный формат, подобный предложенному Drackir, но если вы сделаете другой выбор по какой-то глупой причине, он, вероятно, не вернется и не укусит вас (по крайней мере, не в плане производительности).
источник
Смотрите этот вопрос: «Двоичный XML» для игровых данных? Я также выбрал бы JSON, YAML или даже XML, но это имеет много накладных расходов. Что касается SQLite, я бы никогда не использовал это для хранения уровней. Реляционная база данных удобна, если вы планируете хранить много связанных данных (например, имеет реляционные ссылки / внешние ключи), и если вы собираетесь задавать большое количество различных запросов, хранение мозаичных карт, кажется, не выполняет такие виды вещи и добавил бы ненужной сложности.
источник
Основная проблема этого вопроса состоит в том, что он объединяет две концепции, которые не имеют ничего общего друг с другом:
Вы можете хранить ваши файлы в виде простого текста в произвольном формате, JSON, Lua-скрипте, XML, произвольном двоичном формате и т. Д. И ни для чего из этого не потребуется, чтобы представление этих данных в памяти принимало какую-либо конкретную форму.
Задача вашего уровня загрузки кода - преобразовать парадигму хранения файлов в представление в памяти. Например, вы говорите: «Я вижу меньшую избыточность данных при использовании базы данных для хранения данных плитки». Если вы хотите меньше избыточности, это то, к чему должен обращаться ваш код загрузки уровня. Это то, что ваше представление в памяти должно уметь обрабатывать.
Это не тот вопрос, который нужен вашему формату файла. И вот почему: эти файлы должны прийти откуда-то.
Либо вы будете писать их вручную, либо будете использовать какой-то инструмент, который создает и редактирует данные уровня. Если вы пишете их от руки, то самая важная вещь, которая вам нужна, - это формат, который легко читать и изменять. Избыточность данных - это не то, что вашему формату даже нужно учитывать, потому что вы будете тратить большую часть своего времени на редактирование файлов. Вы действительно хотите вручную использовать любые механизмы, которые вы придумали для обеспечения избыточности данных? Не лучше ли использовать ваше время, чтобы ваш загрузчик уровня справился с этим?
Если у вас есть инструмент, который их создает, то реальный формат имеет значение (в лучшем случае) для удобства чтения. Вы можете положить что-нибудь в эти файлы. Если вы хотите опустить избыточные данные в файле, просто разработайте формат, который может это сделать, и заставьте ваш инструмент правильно использовать эту возможность. Ваш формат тайла карты может включать в себя сжатие RLE (кодирование длины серий), сжатие дублирования, любые методы сжатия, которые вы хотите, потому что это ваш формат тайла карты.
Проблема решена.
Реляционные базы данных (RDB) существуют для решения проблемы выполнения сложных поисков по большим наборам данных, которые содержат много полей данных. Единственный вид поиска, который вы когда-либо делаете на карте тайлов, - это «получить плитку в позиции X, Y». Любой массив может справиться с этим. Использование реляционной базы данных для хранения и поддержки данных карты тайла было бы чрезмерным излишним и ничего не стоило бы.
Даже если вы встроите некоторое сжатие в свое представление в памяти, вы все равно сможете легко превзойти RDB с точки зрения производительности и занимаемой памяти. Да, вам придется реализовать это сжатие. Но если размер данных в памяти настолько важен, что вы бы рассматривали RDB, то вы, вероятно, захотите реализовать конкретные виды сжатия. Вы будете быстрее, чем RDB, и будете меньше в памяти одновременно.
источник
Для действительно простых данных я бы, вероятно, просто пошел по пути XML, если вы уже знакомы с загрузкой и анализом XML.
источник