La migliore soluzione per "stringa di livello"?


10

Ho un gioco che genera una mappa di livello casuale all'inizio del livello. Voglio implementare un modo per salvare e caricare il livello.

Pensavo che forse XML sarebbe stata una buona opzione per salvare tutte le variabili, quindi sarebbe stato facile per me creare qualcosa che potesse analizzare tale XML e generare esattamente lo stesso livello.

Ma XML è probabilmente eccessivo per le mie esigenze. Ricordo che in passato con la vecchia console Sega che non aveva la possibilità di salvare il tuo gioco (penso che anche il gioco Worms lo facesse), ti avrebbero dato un sacco di personaggi che potresti scrivere. Se in seguito hai inserito la stringa, questa carica il livello esatto.

Una "stringa di livello" sarebbe una buona opzione? Sarebbe una sorta di conversione "base60"? Come lo implementerei?

Risposte:


20

Presumibilmente tutto ciò che serve per salvare è il seme casuale, che è generalmente solo un int. Potresti codificare l'int in base64 se vuoi renderlo un po 'più opaco, ma probabilmente non è necessario.


Potresti anche generare il seme da una parola reale (magari comprare sommando i personaggi per ottenere il tuo seme) e in questo modo restituire qualcosa di più significativo. Dovresti memorizzare le parole nel tuo gioco o recuperarle però.
Jonathan Fischoff il

Va bene, ma dovrebbe anche memorizzare dati dinamici se si gioca. La stringa dovrebbe salvare le posizioni / i punteggi dei caratteri, ecc. Qual è il modo migliore per generare questa stringa?
Adam Harte,

Probabilmente userei JSON (o XML se preferisci) per serializzare lo stato mondiale e quindi codificare base64 quella stringa. Non sei sicuro che sia utile, perché non creare un sistema di salvataggio / caricamento più orientato alla GUI? Presumo che questo sia basato sul web e che tu non voglia gestire questo lato server. Forse guardate shygypsy.com/farm/p.cgi per un esempio?
coderanger,

23

Qualunque sia il formato che usi per i tuoi giochi di salvataggio, per l'amor del cielo inserisci un numero di versione. Sarai in grado di avere carichi compatibili con le versioni precedenti ramificando il numero di versione o sarai in grado di riconoscere in modo sicuro salvataggi che sono troppo vecchi caricare.

Te ne pentirai se non lo fai.


7
Il +1 non risponde davvero alla domanda ma è così importante.
Jonathan Fischoff,

10

JSON è buono, ma YAML è migliore. :) http://www.yaml.org/ e http://code.google.com/p/yaml-cpp/ per una delle implementazioni più piacevoli da usare.

YAML è un superset di JSON che aggiunge il supporto per alcune funzioni interessanti, in particolare:

  • Nodi binari. Questo è ottimo per serializzare il tipo di dati che potresti avere a che fare con le descrizioni dei livelli. JSON richiede di tradurre in un formato intermedio di tua scelta, come Base64, prima di scrivere / dopo l'analisi. YAML ha un tipo di nodo binario !! che dice alla libreria del parser di farlo per te.
  • Riferimenti all'interno del documento. Se lo stesso oggetto appare due volte nel documento, JSON lo scriverà due volte e quando lo rileggerai, otterrai due copie. Molti emettitori YAML possono rilevare questa situazione e invece di una seconda copia, emettono un riferimento al primo, quando possono essere rilevati durante il caricamento.
  • Tipi di nodi personalizzati. Puoi contrassegnare ogni mappa in un elenco con ad esempio !! Player, !! Enemy, ecc., E quindi mantenere le informazioni sul tipo più fuori banda.
  • YAML supporta una formattazione più leggibile.
  • Poiché JSON è un sottoinsieme di YAML, la maggior parte dei lettori YAML non avrà problemi a leggere i documenti JSON.

1
Yaml è molto interessante e, se si evitano alcune delle caratteristiche del superset, può essere convertito in json senza difficoltà.
Jethro Larson,

3

Se vuoi serializzare tutti i dati nel gioco, consiglierei JSON come formato di file, è per questo che è più facile usare l'XML e il supporto è ottimo in molte lingue.

Ho usato questa libreria per C ++ e funziona molto bene.

http://jsoncpp.sourceforge.net/


3

XML è una buona scelta se non sei limitato dalle dimensioni ed è supportato in modo nativo (ad esempio in .NET e Flash) ma se vuoi un formato sottile puoi creare il tuo formato e il tuo parser abbastanza facilmente. Di solito uso 1 carattere ad es. virgola per separare ogni oggetto. Per decodificare la stringa, eseguire una divisione su virgola. Ora ogni oggetto ha bisogno di proprietà diverse, quindi separale con un carattere diverso, ad esempio un punto e virgola, e usa un altro carattere per separare i nomi delle proprietà dai valori delle proprietà, ad es. Colon. Tutto così può essere decodificato facilmente senza regex semplicemente usando string.split. Ecco un esempio:

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

puoi risparmiare ancora più spazio mantenendo i nomi delle proprietà fino a 1 carattere, ad es. h per motivi di salute. Per esempio.

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

Confronta con l'alternativa JSON:

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

Inoltre, se si desidera ridurre la dimensione dei numeri, è possibile codificarli utilizzando il set completo di caratteri UTF16 stampabili. Questo thread mi ha spinto a porre una domanda su Stack Overflow di quanti dati potresti racchiudere in un personaggio sullo schermo . La risposta sembra essere da qualche parte oltre 40.000 valori per un numero intero, se non ti dispiace avere il coraggio, Kanji e pezzi degli scacchi: ♔♕♖♗♘♙♚♛♜♝♞♟

Per ottenere un'ulteriore riduzione delle dimensioni, è possibile utilizzare l'ordine di lettura / scrittura per determinare quale valore è quale, quindi i primi due caratteri rappresentano l'id, i due successivi sono la posizione x, i successivi due y, quindi l'angolo, quindi salute , ecc. Quindi:

F5DGP@%&002DFTK#OP1F

potrebbe memorizzare tutte le stesse informazioni degli altri esempi.

Le griglie delle tessere possono essere memorizzate come una semplice stringa con ogni carattere che rappresenta un diverso tipo di tessera, ad esempio:

i789pog5h3kl

dove potrei significare lava, 9 significa erba ecc


Questo è più in linea con quello che sto chiedendo. Cito XML nella mia domanda, ma la gente lo suggerisce ancora!
Adam Harte,

1
Aggiungi {} e in pratica hai JSON ;-)
coderanger

Dovresti aggiungere anche un sacco di virgolette. Probabilmente raddoppierebbe il numero di caratteri, ma otterrai la nidificazione degli oggetti.
Iain,

1

Se stai codificando in .Net, allora XML è semplicissimo, in quanto puoi serializzare / deserializzare la tua classe di livello in / fuori XML con solo un paio di righe, e quindi è tutto in una classe ben gestita.

La mappa sarebbe una variabile di tipo Mappa in cui sono caricati tutti i tuoi dati.

Dim TheMap As New Map

Supponendo che tu abbia già creato una classe Map, ciò salverebbe la tua mappa in 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()

In questo modo il codice XML verrà caricato nuovamente nella classe della mappa, per essere riutilizzato nel codice.

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

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

Da questo punto in poi il tuo file XML è ora caricato nella tua classe per un facile utilizzo.

Per quanto riguarda il problema "Stringa di livello", ciò che è stato affermato in precedenza avrebbe funzionato alla grande, puoi semplicemente usare il numero Seed come "Stringa di livello".

Altrimenti, potresti semplicemente pre-generare tutte le diverse mappe che desideri, e salvarle tutte con una "Stringa di livello" e quindi usarle per estrarre la mappa corretta.


+1 anche se odio XML - Se la lingua fornisce un formato di serializzazione predefinito, è meglio considerarlo prima, soprattutto se è un formato che altri strumenti potrebbero teoricamente analizzare (come XML / JSON / YAML).

0

Userei un structdispositivo semplice o simile (a seconda della lingua) per memorizzare tutto lo stato del gioco in un posto centrale. Se si desidera la protezione di setter / getter, è possibile avvolgere la struttura in a class.

Se ti senti all'altezza, usa i bitfield o semplicemente manipola i bit con gli operatori bit per bit.

Tieni presente che in alcune lingue, le regole per il riempimento e l'imballaggio della struttura possono essere un po 'complicate, ma potrebbe anche non essere molto importante per il tuo caso se hai un byte o due di riempimento.

Potresti anche essere in grado di utilizzare un #pragma(come #pragma pack(1)) o un __attribute__per imballare da vicino la struttura, eliminando l'imbottitura. Questo potrebbe funzionare o meno a seconda del compilatore e dell'architettura di destinazione.

Si noti che l'uso di campi di bit e pragmi o attributi di pacchetti può ridurre la portabilità. Attraverso architetture hardware, anche l'endianness del campo struct (ordine dei byte) può cambiare. Quindi potresti voler evitare questo se stai cercando la portabilità.

(Ad esempio Pac-Man, questa struttura potrebbe contenere ingenuamente un id di mappa o un seme di mappa, una posizione di X-y di Pac-Man, quattro posizioni di x e y di fantasmi e un ampio campo di bit per la presenza o l'assenza di 32-64 pellet, qualunque sia il massimo.)

Una volta che hai la tua struttura, passala a qualcosa come una funzione xxencode :

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

Scrivere questa funzione è un po 'soggetto a errori; è necessario spostare e combinare i byte necessari per ottenere, ad esempio, 6 bit alla volta, quindi tradurli in un carattere appropriato. Proverei personalmente a cercare il codice di qualcun altro, a meno che non lo facessi per "divertimento" (e probabilmente vorrei una suite di test per questo).

Non sottovalutare mai il potere della vecchia scuola structnei posti giusti. Lo abbiamo usato un sacco per i giochi GBA e DS qui.


La serializzazione della struttura non elaborata è un nuovo codice non portatile ed estremamente fragile. A meno che non si tratti dell'ultimo passaggio dei dati di cottura per una piattaforma con risorse molto limitate, è un'ottimizzazione molto prematura. C'è un motivo per cui preferisci 6 bit a 8? Il formato non sarà comunque leggibile dall'uomo, ma potresti anche sfruttare la velocità e il debuggability di un layout di struttura reale.

@Joe: ho già detto che non è portatile e alcune delle potenziali preoccupazioni. La domanda chiedeva specificamente una "stringa di livello" leggibile dall'uomo come i vecchi giochi di Sega e menzionava la conversione base60; passare una struttura attraverso qualcosa come xxencode farà questo. Questa non è necessariamente un'ottimizzazione prematura: una struttura semplice, non bitpackata, è un modo piacevolmente "centrale" per archiviare i dati di salvataggio e può semplificare gran parte del codice che interagisce con i dati. Vedi ad esempio i recenti articoli di Noel Llopis sulle strutture non amiche dei non membri e la programmazione "dentro e fuori". Questo è solo BACIO.
magro

1
Sono totalmente d'accordo con questo modo. Sì, non è portatile tra le versioni, né i sistemi, ma poi i salvataggi non sono risorse che devono essere riutilizzate durante lo sviluppo o copiate su piattaforme. La debuggabilità non è quasi un problema con una lettura / scrittura sul posto, implementala una volta e funzionerà per sempre. Se l'estensibilità è davvero un problema: aggiungi un numero di versione come primo ybyte / parola e puoi attivarlo (anche se ciò introdurrebbe una vulnerabilità dei dati). Non è che xml risolverà i problemi di versioning - tende ad essere più coinvolto della semplice impostazione di un valore. Sì, sono pragmatico.
Kaj,

0

XML è utile per i documenti strutturati in modo arbitrario (gli elementi possono apparire in diversi livelli dell'albero) o per l'incorporamento di formati estranei (come mettere svg in una pagina xhtml). Se non hai questi requisiti, è un formato davvero inefficiente e è preferibile qualcosa di più semplice come CSV o JSON.

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.