Пользовательский расширяемый формат файла для двумерных плиточных карт

8

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

Сейчас я ищу предложения и ресурсы, как реализовать собственный формат файла для моих карт, который должен обеспечивать следующие функциональные возможности (основанные на методе MoSCoW):

  • Должен иметь
    • Расширяемость и обратная совместимость
    • Обработка разных слоев
    • Метаданные о том, является ли плитка твердой или может быть передана
    • Специальная сериализация объектов / триггеров со связанными свойствами / метаданными
  • Мог бы иметь
    • Какой-то вид включения набора плиток, чтобы предотвратить разброс файлов / наборов плиток

Я занимаюсь разработкой с C ++ (используя SDL) и ориентируюсь только на Windows. Любая полезная помощь, советы, предложения, ... будет принята с благодарностью!


Результат обсуждения ниже

Я разрабатывал формат файла моей карты последние несколько часов и придумал скелет (пока что содержит только слои - я оставляю все остальное для всех, кто разрабатывает свой собственный формат), которым я хотел бы поделиться со всеми заинтересованными - если у вас такие же намерения, что вы можете получить какое-то вдохновение. Полный скриншот можно скачать на Imgur .

Формат файла карты Коре

Кристиан Ивицевич
источник
2
XML - это потрясающий формат для начала, он расширяемый, обратно совместимый, вы можете добавлять любую информацию и метаданные, какие пожелаете. Существует широкая поддержка для чтения, создания и редактирования XML. Поддержка сериализации в и из XML существует практически для каждого языка. Недостатком является то, что это довольно неэффективно, это можно улучшить, используя бинарную версию или используя сжатие zip для самого файла. Тем не менее, это хорошая отправная точка для создания ваших собственных форматов файлов
Даниэль Карлссон
@DanielCarlsson: Как уже упоминалось в принятом ответе ниже, вначале я остановлюсь на XML, чтобы иметь что-то удобное для отладки и работы с ним. Позже я перейду к пользовательскому двоичному формату. Тем не менее, проголосовали, потому что XML великолепен в сочетании с библиотеками RapidXML в C ++.
Кристиан Ивичевич

Ответы:

6

Лично я больше поклонник бинарных форматов с разделами (например, Windows PE, просто намного проще). Их также легче анализировать (но это только мое мнение .... Я работал с XML достаточно, чтобы вызывать головную боль, проверяя, вернул ли getElementByName одно значение или список значений ... тьфу). Итак, на вашем месте я бы сделал это примерно так:

".MMF\0" // magic value at the start, null-terminated string. stands for My Map Format :)
    char header_length // useful when parsing. char is a byte, of course, an unsigned one
    char version // version of the map file. (you don't really need ints here, because you probably won't be needing more than 255 versions for example, but you can also use them)
    char* map_name // null terminated string describing the name of the level/map
    char* author_name // if you are going to have a map editor for the general public, it would be nice to credit the person who made the map
    int width // it's probably wise to plan ahead and expect an int here when you're parsing the file
    int height
    ".layer\0" // we begin another subsection
        char header_length
        char type // type of the layer. for example, you can put 1 there if you want this to be a layer describing different tiles/block in a Terraria like game
        ".data\0" // yet another subsection. this will hold the data for the tiles
                  // in a hypothetical terraria 2d game, you would lay down tiles from
                  // the top-right corner (0,0) and then begin writing row after row
                  // write(1,0); write(2,0); write(3,0); ... then write(0,1); write(1,1);
                  // write(2,1); write(3,1); and so on..
            char t1 // tile at (0,0). for example, value 0 is empty, or passable tile
            char t2 // tile at (1,0). this might be a dirt block - value 1
            char t3 // tile at (2,0). a rock, perhaps? value 3
            (...)
            char tn // tile at (width-1, height-1) or the bottom-left tile
    ".layer\0" // another layer.
        char header_length    
        char type // let this on be of value 2, and let it describe portals.
                  // putting portals in a game makes it instantly 20% cooler
        ".data\0"
            char t1  // 0, no portal here at tile (0,0)
            char t2  // still nothing
            char t3  // nope, try again
            (...)
            char t47 // at some location, you made a red portal. let's put 1 here so we can read it in our engine
            (...)
            char t86 // looke here, another 1! you can exit here from location corresponding to t47
            (...)
            char t99 // value 2. hm, a green portal?
            (...)
            char tn  // bottom-left tile, at (width-1, height-1)
    ".layer\0" // another layer
        char header_length
        char type // value 3, player&enemies spawn points
        char something // you don't have to have header len fixed. you can add stuff later
                       // and because you were smart enough to put header length 
                       // older versions can know where the stuff of interest lays
                       // i.e. version one of the parser can read only the type of layer
                       // in version two, you add more meta-data  and the old parser
                       // just skips it, and goes straight to the .data section
            ".data\0"
                char t1  // zero
                char t2  // zero
                char t3  // zero
                (...)
                char t42 // a 1 - maybe the player spawn point. 5 tiles to the right
                         // there's a red portal
                (...)
                char t77 // a 2: some enemy spawn point
                (...)
                char tn  // last tile

,

Преимущества:

  • Выглядит круто.
  • Заставляет вас думать, что вы что-то знаете о программировании, делах по старинке
  • Вы можете вручную написать свои уровни в шестнадцатеричном редакторе:
  • Как правило, быстрее, чем INI и XML, с точки зрения написания и чтения
  • На самом деле это длинный поток байтовых данных. Не нужно тратить время на то, чтобы сделать его красивым, с отступами (например, то, что вы хотели бы сделать с XML).
  • В заголовки легко добавлять вещи. Если часть данных находится в нижней части заголовка, старые версии анализаторов могут быть проинструктированы, чтобы избежать этого и перейти к той части файла, которую они понимают.

Недостатки:

  • Вы должны позаботиться о размещении данных.
    • Поля данных должны быть упорядочены.
    • Вы должны знать их тип в парсере - как я уже сказал, это просто длинный поток байтов.
    • Перемещение данных на одно место (например, вы забыли записать тип слоя; анализатор ожидает там байт и находит значение «.» - это нехорошо) испортил весь массив данных с этого момента.
  • Труднее прыгнуть прямо - нет API, нет функции, подобной getLayerWidth () - вы должны реализовать все это самостоятельно.
  • Там потенциально много пустого места. Возьмите третий слой, например. Он, безусловно, будет заполнен множеством нулей. Это может быть обойдено, если вы используете какое-то сжатие. Но, опять же, это опять мешает вещам низкого уровня ...

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

Владимир Митрович
источник
Вы просто сохраняете ... скажем, идентификаторы для плиток, но как насчет их метаданных? Как сохранить, являются ли плитки проходимыми или нет? А как насчет триггеров и, возможно, даже вызовов сценариев / кода / функций, связанных с ними?
Кристиан Ивицевич
@ChristianIvicevic: Есть несколько способов сделать это:
Владимир Митрович
@ChristianIvicevic: 1. Упакуйте метаданные в один байт. У вас есть только восемь возможных плиток? Отлично, оставьте остальные пять битов для чего-то другого. В вашем случае вы спрашивали о проходимых плитках. Вы можете иметь первый бит (0-й бит) для хранения этой информации. Немного немного манипуляций :) сделает свое дело ( codepad.org/Q6zfTV44 ). 2. Используйте слои для этого. Создайте слой с уникальным типом и сделайте его заполненным нулями и единицами, единицами для проходимости и нулями для непроходимых тайлов. 3. Используйте более одного байта на плитку. Один байт для значения, другой для метаданных.
Владимир Митрович
@ChristianIvicevic: Что касается скриптов, я предполагаю, что вы реализовали парсер скриптов, который работает. Вы можете добавить раздел в файл карты и поместить их туда: pastebin.com/yUKncz19 ИЛИ Вы можете поместить их в отдельный файл и сохранить имя файла в разделе «.scripts». Затем вы добавляете другой раздел «.layer», описывающий, какие плитки запускаются и какой сценарий: pastebin.com/BgPCR2xQ
Владимир Митрович,
Довольно много примеров - ТОП! Теперь я остановлюсь на XML, чтобы создать некоторые базовые уровни для отладки / работы, и в конечном итоге я перейду к такому двоичному формату, который вы описали для необработанного map-файла, содержащего данные, и запакую этот файл как наборами плиток (png и т. Д.), Так и скрипт-файл (ы) в zip, чтобы все было структурировано лучше. Это будет до написания кода, который на самом деле читает такие двоичные данные - но это еще одна тема в моей истории ... спасибо!
Кристиан Ивичевич
9

Вы можете использовать формат карты TMX, используемый Tiled- редактором (а также несколькими другими редакторами карт).

Даже если вы сами не используете Tiled, формат TMX поддерживает все функции, которые вы упомянули, и имеет несколько существующих загрузчиков / анализаторов для различных языков. Также очень легко понять формат и расширить его для вашей собственной игры.

Firas Assaad
источник
Моя текущая концепция XML будет основана на формате карты TMX, а затем я прочитаю все файлы с помощью RapidXML - позже я перейду к какому-нибудь пользовательскому двоичному формату файлов.
Кристиан Ивичевич