Formato file estensibile personalizzato per mappe piastrellate 2D


8

Ho implementato gran parte della mia logica di gioco in questo momento, ma continuo a creare le mie mappe con cattivi loop for-the-fly per poter lavorare con qualcosa. Ora volevo andare avanti e fare qualche ricerca su come (un) serializzare questi dati. (Non cerco un editor di mappe - sto parlando del file della mappa stesso)

Per ora sto cercando suggerimenti e risorse, come implementare un formato file personalizzato per le mie mappe che dovrebbe fornire le seguenti funzionalità (basate sul metodo MoSCoW):

  • Deve avere
    • Estensibilità e retrocompatibilità
    • Gestione di diversi strati
    • Metadati su se una tessera è solida o può essere passata
    • Serializzazione speciale di entità / trigger con proprietà / metadati associati
  • Potrebbe avere
    • Qualche tipo di inclusione del tileset per evitare di avere file / tileset sparsi

Sto sviluppando con C ++ (usando SDL) e prendendo di mira solo Windows. Qualsiasi aiuto utile, suggerimenti, suggerimenti, ... sarebbe apprezzato!


Risultato della discussione di seguito

Ho progettato il mio formato di file di mappe nelle ultime ore e ho creato uno scheletro (contiene solo livelli per ora - lascerò il resto a tutti coloro che progettano il proprio formato) che volevo condividere con tutti gli interessati - se hai le stesse intenzioni che puoi ottenere una sorta di ispirazione. Lo screenshot full-size può essere scaricato da Imgur .

Formato file Kore Map


2
XML è un formato formidabile per cominciare, estendibile, retrocompatibile, puoi aggiungere qualsiasi informazione e metadata desideri. Vi è un ampio supporto per la lettura, la creazione e la modifica di XML. Il supporto per la serializzazione da e verso XML esiste in quasi tutte le lingue. Il rovescio della medaglia è che è abbastanza inefficiente nello spazio, questo può essere migliorato utilizzando una versione binaria o utilizzare la compressione zip sul file stesso. Tuttavia, è un buon punto di partenza per creare i tuoi formati di file
Daniel Carlsson,

@DanielCarlsson: Come menzionato nella risposta accettata di seguito, mi atterrerò all'inizio a XML per avere qualcosa di utile con cui eseguire il debug e lavorare. Successivamente passerò a un formato binario personalizzato. Tuttavia è stato votato perché XML è fantastico in combinazione con le librerie RapidXML in C ++.
Christian Ivicevic,

Risposte:


6

Personalmente, sono più un fan dei formati binari con sezioni (come Windows PE, solo molto più semplice). Sono anche più facili da analizzare (ma questa è solo la mia opinione .... Ho lavorato con XML abbastanza da farmi venire il mal di testa, controllando se getElementByName ha restituito un singolo valore o un elenco di valori ... ugh). Quindi, se fossi in te, lo farei in questo modo:

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

,

vantaggi:

  • Sembra fico.
  • Ti fa pensare di sapere qualcosa sulla programmazione, fare cose alla vecchia maniera.
  • Puoi scrivere manualmente i tuoi livelli in un editor esadecimale:
  • Generalmente più veloce di INI e XML, sia dal punto di vista della scrittura che della lettura
  • È un lungo flusso di dati byte, davvero. Non c'è bisogno di perdere tempo per renderlo bello, dal punto di vista del rientro (come quello che vorresti fare con XML).
  • È facile aggiungere elementi nelle intestazioni. Se un pezzo di dati arriva nella parte inferiore dell'intestazione, è possibile indicare alle vecchie versioni di parser di evitarlo e passare alla parte del file che comprendono.

svantaggi:

  • Devi prenderti cura del posizionamento dei dati.
    • I campi dati devono essere ordinati.
    • Devi conoscere il loro tipo in parser - come ho detto, è solo un lungo flusso di byte.
    • Spostando i dati di una posizione (ad esempio, si dimentica di scrivere il tipo di livello; il parser si aspetta un byte lì e trova il valore di '.' - non va bene) confonde l'intero array di dati da quel punto in poi.
  • Più difficile saltare subito - non c'è API, nessuna funzione come getLayerWidth () - devi implementare tutto da solo.
  • C'è potenzialmente molto spazio sprecato. Prendi il terzo strato per esempio. Sarà sicuramente pieno di molti zeri. Questo può essere eluso se si utilizza una sorta di compressione. Ma ancora una volta, questo è un casino con cose di basso livello ancora una volta ...

Ma la cosa migliore di questo approccio secondo me è che puoi fare tutto da solo. Molti tentativi ed errori, ma alla fine, si finisce per imparare molto.


Stai solo salvando ... diciamo gli ID per le tessere, ma per quanto riguarda i loro metadati? Come devo salvare se le tessere sono passabili o no? Che dire dei trigger e forse anche delle chiamate di script / codice / funzione ad essi associati?
Christian Ivicevic,

@ChristianIvicevic: Esistono diversi modi per farlo:
Vladimir Mitrovic,

@ChristianIvicevic: 1. Raggruppa i metadati in un singolo byte. Hai solo otto tessere possibili? Ottimo, salva il resto cinque bit per qualcos'altro. Nel tuo caso, hai chiesto di tessere passabili. È possibile fare in modo che il primo bit (0 ° bit) memorizzi tali informazioni. Un po 'di manipolazione dei bit :) farà il trucco ( codepad.org/Q6zfTV44 ). 2. Usa i livelli per questo. Avere un livello con un tipo unico e renderlo pieno di zero e uno, uno per passabile e zero per tessere invalicabile. 3. Utilizzare più di un byte per riquadro. Un byte per valore, l'altro per metadati.
Vladimir Mitrovic,

@ChristianIvicevic: per quanto riguarda gli script, suppongo che tu abbia implementato un parser di script che funziona. È possibile aggiungere una sezione al file della mappa e inserirli lì: pastebin.com/yUKncz19 OPPURE È possibile inserirli in un file separato e salvare un nome file nella sezione ".scripts". Quindi anteponi un'altra sezione ".layer" che descrive quali tessere sparano quale script: pastebin.com/BgPCR2xQ
Vladimir Mitrovic,

Un sacco di esempi - TOP! Ora mi atterrerò su XML per creare alcuni livelli di base con cui eseguire il debug / lavorare e alla fine passerò a un formato binario che hai descritto per il file map grezzo che contiene i dati e comprimerò questo file con entrambi i set di tessere (png ecc.) E i file di script in una zip per avere tutto strutturato in modo migliore. Spetterà alla scrittura del codice, che in realtà legge tali dati binari - ma questo è un altro argomento nella mia storia ... grazie!
Christian Ivicevic,

9

È possibile utilizzare il formato della mappa TMX utilizzato dall'editor affiancato (così come molti altri editor di mappe).

Anche se non usi Tiled da solo, il formato TMX supporta tutte le funzionalità che hai citato e ha diversi caricatori / parser esistenti per una varietà di lingue. È anche molto facile capire il formato ed estenderlo per il tuo gioco.


Avrò il mio attuale concetto XML basato un po 'sul formato della mappa TMX e quindi leggerò tutti i file con RapidXML - in seguito passerò a un formato di file binario personalizzato.
Christian Ivicevic,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.