Formato de arquivo extensível personalizado para mapas em mosaico 2D

8

Eu implementei grande parte da minha lógica de jogo no momento, mas ainda crio meus mapas com desagradáveis ​​loops on-the-fly para poder trabalhar com alguma coisa. Agora eu queria seguir em frente e fazer algumas pesquisas sobre como (des) serializar esses dados. (Não procuro um editor de mapas - estou falando do próprio arquivo de mapa)

Por enquanto, estou procurando sugestões e recursos, como implementar um formato de arquivo personalizado para meus mapas, que deve fornecer a seguinte funcionalidade (com base no método MoSCoW):

  • Deve ter
    • Extensibilidade e compatibilidade com versões anteriores
    • Manipulação de diferentes camadas
    • Metadados sobre se um bloco é sólido ou pode ser passado através
    • Serialização especial de entidades / gatilhos com propriedades / metadados associados
  • Poderia ter
    • Algum tipo de inclusão do conjunto de peças para evitar a dispersão de arquivos / conjuntos de peças

Estou desenvolvendo com C ++ (usando SDL) e segmentando apenas o Windows. Qualquer ajuda útil, dicas, sugestões, ... seria apreciada!


Resultado da discussão abaixo

Estive projetando meu formato de arquivo de mapa nas últimas horas e criei um esqueleto (contém apenas camadas por enquanto - deixarei o resto para todos que projetam seu próprio formato) que gostaria de compartilhar com todos os interessados ​​- se você tem as mesmas intenções de obter algum tipo de inspiração. A captura de tela em tamanho real pode ser baixada em Imgur .

Formato de arquivo de mapa Kore

Christian Ivicevic
fonte
2
XML é um formato fantástico para começar, extensível e compatível com versões anteriores, você pode adicionar qualquer informação e metadado que desejar. Existe amplo suporte para leitura, criação e edição de XML. Existe suporte para serialização de e para XML para quase todos os idiomas. A desvantagem é que ele é bastante ineficiente em termos de espaço, isso pode ser aprimorado usando uma versão binária ou usando a compactação zip no próprio arquivo. Nenhum ponto a menos, é uma boa partida para fazer seus próprios formatos de arquivo
Daniel Carlsson
@DanielCarlsson: Conforme mencionado na resposta aceita abaixo, vou me ater ao XML no começo para ter algo útil para depurar e trabalhar. Mais tarde, passarei para um formato binário personalizado. No entanto, a votação foi positiva porque o XML é incrível em combinação com as bibliotecas RapidXML em C ++.
Christian Ivicevic 12/09/12

Respostas:

6

Pessoalmente, sou mais fã de formatos binários com seções (como o Windows PE, apenas muito mais simples). Eles também são mais fáceis de analisar (mas essa é apenas a minha opinião ... Eu trabalhei com XML o suficiente para me causar dores de cabeça, verificando se getElementByName retornou um único valor ou uma lista de valores ... ugh). Então, se eu fosse você, eu faria algo assim:

".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

,

Vantagens:

  • Parece legal.
  • Faz você pensar que sabe algo sobre programação, fazendo coisas da maneira antiga.
  • Você pode escrever manualmente seus níveis em um editor hexadecimal:
  • Geralmente mais rápido que INIs e XMLs, tanto do ponto de vista de escrita quanto de leitura
  • É um longo fluxo de dados de bytes, na verdade. Não é necessário gastar tempo fazendo com que pareça bonito, com relação à indentação (como o que você gostaria de fazer com o XML).
  • É fácil adicionar coisas nos cabeçalhos. Se um dado chegar na parte inferior do cabeçalho, as versões antigas dos analisadores podem ser instruídas para evitá-lo e pular para a parte do arquivo que eles entendem.

Desvantagens:

  • Você precisa cuidar bem da localização dos dados.
    • Os campos de dados devem ser ordenados.
    • Você deve conhecer o tipo deles no analisador - como eu disse, é apenas um longo fluxo de bytes.
    • Mover dados por um local (por exemplo, você esquece de escrever o tipo da camada; o analisador espera um byte lá e encontra o valor de '.' - isso não é bom) atrapalha toda a matriz de dados a partir desse ponto.
  • Mais difícil de entrar - não há API, não há função como getLayerWidth () - você precisa implementar tudo isso sozinho.
  • Existe potencialmente muito espaço desperdiçado. Veja a terceira camada, por exemplo. Certamente será embalado com muitos zeros. Isso pode ser contornado, se você usar algum tipo de compactação. Mas, novamente, isso está mexendo com coisas de baixo nível mais uma vez ...

Mas a melhor coisa nessa abordagem, na minha opinião, é: você pode fazer tudo sozinho. Muitas tentativas e erros, mas no final, você acaba aprendendo muito.

Vladimir Mitrovic
fonte
Você está apenas salvando ... digamos IDs para os blocos, mas e os metadados deles? Como devo salvar se os blocos são aceitáveis ​​ou não? E quanto a gatilhos e talvez até scripts / códigos / chamadas de função associados a eles?
Christian Ivicevic
@ChristianIvicevic: Há várias maneiras de fazer isso:
Vladimir Mitrovic
@ChristianIvicevic: 1. Empacote os metadados em um único byte. Você só tem oito peças possíveis? Ótimo, guarde o resto cinco bits para outra coisa. No seu caso, você perguntou sobre blocos aceitáveis. Você pode fazer com que o primeiro bit (0º bit) armazene essas informações. Um pouco de manipulação de bits :) fará o truque ( codepad.org/Q6zfTV44 ). 2. Use camadas para isso. Tenha uma camada com um tipo único e preencha com zeros e uns, zeros para passável e zeros para ladrilhos intransitáveis. 3. Use mais de um byte por bloco. Um byte para valor e outro para metadados.
Vladimir Mitrovic 12/09
@ChristianIvicevic: Quanto aos scripts, presumo que você tenha implementado um analisador de scripts que funcione. Você pode adicionar uma seção ao seu arquivo de mapa e colocá-las lá: pastebin.com/yUKncz19 OU Você pode colocá-las em um arquivo separado e salvar um nome de arquivo na seção ".scripts". Em seguida, você anexa outra seção ".layer", descrevendo quais blocos acionam qual script: pastebin.com/BgPCR2xQ
Vladimir Mitrovic
Muitos exemplos - TOP! Agora, aderirei ao XML para criar alguns níveis básicos para depuração / trabalho e, eventualmente, passarei para um formato binário que você descreveu para o arquivo de mapa bruto que contém os dados e empacota esse arquivo com os conjuntos de peças (png etc.) e o scriptfile (s) em um zip para ter tudo estruturado da melhor maneira. Caberá escrever código, que realmente lê esses dados binários - mas esse é outro tópico da minha história ... obrigado!
Christian Ivicevic
9

Você pode usar o formato de mapa TMX usado pelo editor de mosaicos (assim como vários outros editores de mapas).

Mesmo se você não usar o Tiled, o formato TMX suporta toda a funcionalidade mencionada e possui vários carregadores / analisadores existentes para uma variedade de idiomas. Também é muito fácil entender o formato e estendê-lo para o seu próprio jogo.

Firas Assaad
fonte
Terei meu conceito XML atual com base um pouco no formato do mapa TMX e depois lerei todos os arquivos com o RapidXML - mais adiante, passarei a algum formato de arquivo binário personalizado.
Christian Ivicevic