Beste Lösung für "Level String"?

10

Ich habe ein Spiel, das zu Beginn des Levels eine zufällige Levelkarte generiert. Ich möchte eine Möglichkeit implementieren, um das Level zu speichern und zu laden.

Ich dachte, XML wäre vielleicht eine gute Option zum Speichern aller Variablen, dann wäre es für mich einfach, etwas zu erstellen, das dieses XML analysieren und genau die gleiche Ebene generieren kann.

Aber XML ist wahrscheinlich übertrieben für meine Bedürfnisse. Ich erinnere mich an die alte Sega-Konsole, die Ihr Spiel nicht speichern konnte (ich glaube, das Worms-Spiel hat es auch getan), dass sie Ihnen eine Menge Charakter geben würde, den Sie aufschreiben konnten. Wenn Sie diese Zeichenfolge später eingeben, wird der genaue Pegel geladen.

Wäre eine "Level-Zeichenfolge" eine gute Option? Wäre es eine Art "base60" -Konvertierung? Wie würde ich das umsetzen?

Adam Harte
quelle

Antworten:

20

Vermutlich ist alles, was Sie speichern müssen, der zufällige Startwert, der im Allgemeinen nur ein Int ist. Sie könnten das int in base64 codieren, wenn Sie es etwas undurchsichtiger machen möchten, aber das wird wahrscheinlich nicht benötigt.

Codierer
quelle
Sie können den Samen auch aus einem echten Wort generieren (vielleicht kaufen Sie die Zeichen, um Ihren Samen zu erhalten) und auf diese Weise etwas Bedeutenderes zurückgeben. Sie müssten die Wörter in Ihrem Spiel speichern oder abrufen.
Jonathan Fischoff
Das ist in Ordnung, aber es müsste auch dynamische Daten speichern, wenn das Spiel gespielt wird. Die Zeichenfolge müsste Zeichenpositionen / Punkte usw. speichern. Was ist also der beste Weg, um diese Zeichenfolge zu generieren?
Adam Harte
Ich würde wahrscheinlich JSON (oder XML, wenn Sie es vorziehen) verwenden, um den Weltstatus zu serialisieren und diese Zeichenfolge dann mit base64 zu codieren. Sie sind sich nicht sicher, ob das alles so nützlich ist. Warum nicht einfach ein GUI-orientierteres Speicher- / Ladesystem erstellen? Ich gehe davon aus, dass dies webbasiert ist und Sie diese Serverseite nicht behandeln möchten. Vielleicht sehen Sie sich unter Shygypsy.com/farm/p.cgi ein Beispiel an?
Coderanger
23

Unabhängig davon, welches Format Sie für Ihre Speicherspiele verwenden, geben Sie um Himmels willen eine Versionsnummer ein. Sie können abwärtskompatible Ladevorgänge durchführen, indem Sie auf die Versionsnummer verzweigen, oder Sie können zu alte Speicherungen sicher erkennen Laden.

Sie werden es bereuen, wenn Sie es nicht tun.

Tenpn
quelle
7
+1 beantwortet die Frage nicht wirklich, ist aber so wichtig.
Jonathan Fischoff
10

JSON ist gut, aber YAML ist besser. :) http://www.yaml.org/ und http://code.google.com/p/yaml-cpp/ für eine der benutzerfreundlicheren Implementierungen.

YAML ist eine Obermenge von JSON, die Unterstützung für einige nette Funktionen bietet, insbesondere:

  • Binärknoten. Dies ist ideal, um die Art von Daten zu serialisieren, mit denen Sie möglicherweise für Ebenenbeschreibungen arbeiten. JSON erfordert, dass Sie vor dem Schreiben / nach dem Parsen in ein Zwischenformat Ihrer Wahl wie Base64 übersetzen. YAML hat einen binären Knotentyp !!, der die Parser-Bibliothek anweist, dies für Sie zu tun.
  • Dokumentinterne Referenzen. Wenn dasselbe Objekt zweimal im Dokument angezeigt wird, schreibt JSON es zweimal, und wenn Sie es wieder einlesen, erhalten Sie zwei Kopien. Viele YAML-Emitter können diese Situation erkennen und anstelle einer zweiten Kopie einen Verweis auf die erste ausgeben, wenn dies beim Laden erkannt werden kann.
  • Benutzerdefinierte Knotentypen. Sie können jede Karte in einer Liste mit zB !! Spieler, !! Feind usw. kennzeichnen und so Ihre Typinformationen außerhalb des Bandes halten.
  • YAML unterstützt eine besser lesbare Formatierung.
  • Da JSON eine Teilmenge von YAML ist, haben die meisten YAML-Leser keine Probleme, JSON-Dokumente zu lesen.

quelle
1
Yaml ist sehr cool, und wenn Sie einige der Superset-Funktionen vermeiden, kann es problemlos in JSON konvertiert werden.
Jethro Larson
3

Wenn Sie alle Daten im Spiel serialisieren möchten, würde ich JSON als Dateiformat empfehlen. Aus diesem Grund ist die Verwendung von XML einfacher und die Unterstützung ist in vielen Sprachen sehr gut.

Ich habe diese Bibliothek für C ++ verwendet und sie funktioniert sehr gut.

http://jsoncpp.sourceforge.net/

Jonathan Fischoff
quelle
3

XML ist eine gute Wahl, wenn Sie nicht an die Größe gebunden sind und nativ unterstützt werden (z. B. in .NET und Flash). Wenn Sie jedoch ein schlankes Format wünschen, können Sie ganz einfach Ihr eigenes Format und Ihren eigenen Parser erstellen. Normalerweise benutze ich 1 Zeichen, z. Komma, um jedes Objekt zu trennen. Um die Zeichenfolge zu dekodieren, teilen Sie das Komma. Jetzt benötigt jedes Objekt unterschiedliche Eigenschaften. Trennen Sie diese mit einem anderen Zeichen, z. B. einem Semikolon, und verwenden Sie ein anderes Zeichen, um die Eigenschaftsnamen von den Eigenschaftswerten zu trennen, z. Doppelpunkt. Alle können somit einfach ohne Regex einfach mit string.split dekodiert werden. Hier ist ein Beispiel:

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

Sie können noch mehr Platz sparen, indem Sie die Eigenschaftsnamen auf 1 Zeichen reduzieren, z. B. h für die Gesundheit. Z.B.

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

Vergleichen Sie mit der JSON-Alternative:

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

Wenn Sie die Größe Ihrer Zahlen verringern möchten, können Sie sie auch mit dem vollständigen Satz druckbarer UTF16-Zeichen codieren. Dieser Thread hat mich dazu inspiriert, eine Frage zu Stack Overflow zu stellen, wie viele Daten Sie in einen Bildschirmcharakter packen können . Die Antwort scheint irgendwo über 40.000 Werte für eine ganze Zahl zu liegen, wenn es Ihnen nichts ausmacht, Brail-, Kanji- und Schachfiguren zu haben: ♔♕♖♗♘♙♚♛♜♝♞♟

Um eine weitere Größenreduzierung zu erhalten, können Sie mithilfe der Lese- / Schreibreihenfolge bestimmen, welcher Wert welcher ist. Die ersten beiden Zeichen stehen also für die ID, die nächsten beiden für die x-Position, die nächsten beiden für das y, dann für den Winkel und dann für den Zustand usw. Also:

F5DGP@%&002DFTK#OP1F

könnte alle die gleichen Informationen wie die anderen Beispiele speichern.

Kachelgitter können nur als Zeichenfolge gespeichert werden, wobei jedes Zeichen einen anderen Kacheltyp darstellt, z.

i789pog5h3kl

wo ich Lava bedeuten könnte, 9 Gras usw. bedeuten

Iain
quelle
Dies entspricht eher dem, was ich verlange. Ich erwähne XML in meiner Frage, aber die Leute schlagen es immer noch vor!
Adam Harte
1
Fügen Sie {} hinzu und Sie haben im Grunde JSON ;-)
Coderanger
Sie müssten auch eine Menge Anführungszeichen hinzufügen. Würde wahrscheinlich die Anzahl der Zeichen verdoppeln, aber Sie würden die Verschachtelung von Objekten erhalten.
Iain
1

Wenn Sie in .Net codieren, ist XML sehr einfach zu handhaben, da Sie Ihre Level-Klasse mit nur wenigen Zeilen in / aus XML serialisieren / deserialisieren können und dann alles in einer gut verwalteten Klasse.

TheMap ist eine Variable vom Typ Map, in die Sie alle Ihre Daten geladen haben.

Dim TheMap As New Map

Angenommen, Sie haben bereits eine Map-Klasse erstellt, würde dies Ihre Map in XML speichern:

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()

Dies würde dann dieses XML zurück in Ihre Kartenklasse laden, um es erneut im Code zu verwenden.

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

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

Ab diesem Zeitpunkt wird Ihre XML-Datei zur einfachen Verwendung in Ihre Klasse geladen.

Was Ihr "Level String" -Problem betrifft, würde das, was zuvor angegeben wurde, großartig funktionieren. Sie könnten einfach die Seed-Nummer als "Level String" verwenden.

Andernfalls können Sie einfach so viele verschiedene Karten vorgenerieren, wie Sie möchten, und sie alle mit einer "Level-Zeichenfolge" speichern lassen, um dann die richtige Karte aufzurufen.

jblaske
quelle
+1, obwohl ich XML hasse - Wenn die Sprache ein Standard-Serialisierungsformat bereitstellt, sollten Sie dies zuerst berücksichtigen, insbesondere wenn es sich um ein Format handelt, das andere Tools theoretisch analysieren könnten (wie XML / JSON / YAML).
0

Ich würde ein einfaches structoder ähnliches Gerät (abhängig von Ihrer Sprache) verwenden, um den gesamten Spielstatus an einem zentralen Ort zu speichern. Wenn Sie den Schutz von Setzern / Gettern wünschen, können Sie die Struktur in a einschließen class.

Wenn Sie Lust dazu haben, verwenden Sie Bitfelder oder führen Sie die Bitmanipulation einfach selbst mit bitweisen Operatoren durch.

Beachten Sie, dass in einigen Sprachen die Regeln für das Auffüllen und Packen von Strukturen etwas kompliziert sein können - es kann jedoch auch für Ihren Fall keine große Rolle spielen, wenn Sie ein oder zwei Bytes Auffüllen haben.

Möglicherweise können Sie auch ein #pragma(z. B. #pragma pack(1)) oder ein verwenden, __attribute__um die Struktur eng zu verpacken, wodurch das Auffüllen vermieden wird. Dies kann je nach Compiler und Zielarchitektur funktionieren oder auch nicht.

Beachten Sie, dass die Verwendung von Bitfeldern und Pack-Pragmas oder -Attributen die Portabilität verringern kann. In allen Hardwarearchitekturen kann sich auch die Strukturfeldendigkeit (Bytereihenfolge) ändern. Sie sollten dies also vermeiden, wenn Sie Portabilität anstreben.

(Zum Beispiel Pac-Man kann diese Struktur naiv eine Karten-ID oder einen Karten-Seed, eine Pac-Man-x- und -y-Position, vier Geister-x- und -y-Positionen und ein großes Bitfeld für das Vorhandensein oder Fehlen von 32-64 Pellets enthalten. was auch immer das Maximum ist.)

Sobald Sie Ihre Struktur haben, übergeben Sie sie an eine xxencode- Funktion:

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

Das Schreiben dieser Funktion ist etwas fehleranfällig. Sie müssen Bytes nach Bedarf verschieben und kombinieren, um z. B. 6 Bits gleichzeitig zu erhalten, und dann in ein geeignetes Zeichen übersetzen. Ich persönlich würde versuchen, den Code eines anderen aufzuspüren, es sei denn, ich mache dies zum "Spaß" (und ich würde wahrscheinlich eine Testsuite dafür wollen).

Unterschätzen Sie niemals die Macht der alten Schule structan den richtigen Stellen. Wir haben es hier für GBA- und DS-Spiele verwendet.

schlanker
quelle
Die Serialisierung der Rohstruktur ist nicht portierbar und für neuen Code äußerst fragil. Sofern dies nicht der letzte Schritt zum Backen von Daten für eine Plattform mit sehr begrenzten Ressourcen ist, handelt es sich um eine sehr vorzeitige Optimierung. Gibt es einen Grund, warum Sie 6 Bits gegenüber 8 bevorzugen? Das Format ist ohnehin nicht für Menschen lesbar. Sie können auch die Geschwindigkeit und Debugbarkeit eines realen Strukturlayouts nutzen.
@ Joe: Ich habe erwähnt, dass es nicht portabel ist und einige der möglichen Bedenken. Die Frage stellte speziell eine für Menschen lesbare "Level-Zeichenfolge" wie die alten Sega-Spiele und erwähnte die base60-Konvertierung; Wenn Sie eine Struktur durch etwas wie xxencode führen, wird dies ausgeführt. Dies ist nicht unbedingt eine vorzeitige Optimierung: Eine einfache Struktur, die nicht bitpacked ist, ist eine sehr "zentrale" Methode zum Speichern gespeicherter Daten und kann einen Großteil des Codes vereinfachen , der mit den Daten interagiert. Siehe z. B. Noel Llopis 'jüngste Artikel über Nicht-Mitglieder-Nicht-Freund-Strukturen und "Inside-Out" -Programmierung. Das ist nur KISS.
Leander
1
Ich stimme diesem Weg voll und ganz zu. Ja, es ist weder über Versionen noch Systeme hinweg portierbar, aber Save-Games sind keine Ressourcen, die während der Entwicklung wiederverwendet oder plattformübergreifend kopiert werden müssen. Debuggbarkeit ist kaum ein Problem mit einem vorhandenen Lese- / Schreibvorgang. Implementieren Sie es einmal und es wird für immer funktionieren. WENN Erweiterbarkeit wirklich ein Problem ist - fügen Sie eine Versionsnummer als erstes Ybyte / Wort hinzu, und Sie können diese aktivieren (obwohl dies zu einer Datenanfälligkeit führen würde). Es ist nicht so, dass XML Versionsprobleme lösen wird - die sind eher involviert als nur das Festlegen eines Werts. Ja, ich bin pragmatisch.
Kaj
0

XML eignet sich für beliebig strukturierte Dokumente (Elemente können in verschiedenen Ebenen des Baums angezeigt werden) oder zum Einbetten von Fremdformaten (z. B. das Einfügen von SVG in eine XML-Seite). Wenn Sie diese Anforderungen nicht haben, ist es ein wirklich ineffizientes Format, und etwas Einfacheres wie CSV oder JSON ist vorzuziehen.

Jethro Larson
quelle