'Binäres XML' für Spieldaten?

17

Ich arbeite an einem Level-Editing-Tool, das seine Daten als XML speichert.

Dies ist ideal während der Entwicklung, da es schmerzlos ist, kleine Änderungen am Datenformat vorzunehmen, und es funktioniert gut mit baumartigen Daten.

Der Nachteil ist jedoch, dass die XML-Dateien ziemlich aufgebläht sind, was hauptsächlich auf die Duplizierung von Tag- und Attributnamen zurückzuführen ist. Dies ist auch darauf zurückzuführen, dass numerische Daten erheblich mehr Platz beanspruchen als native Datentypen. Ein kleiner Pegel kann leicht 1 MB + betragen. Ich möchte diese Größen erheblich reduzieren, insbesondere wenn das System für ein Spiel auf dem iPhone oder anderen Geräten mit relativ begrenztem Speicher verwendet werden soll.

Die optimale Lösung für Speicher und Leistung besteht darin, das XML in ein Binärformat zu konvertieren. Aber ich möchte das nicht tun. Ich möchte das Format ziemlich flexibel halten. Mit XML ist es sehr einfach, Objekten neue Attribute hinzuzufügen und ihnen einen Standardwert zuzuweisen, wenn eine alte Version der Daten geladen wird. Ich möchte also bei der Hierarchie der Knoten bleiben und Attribute als Name-Wert-Paare verwenden.

Ich muss dies jedoch in einem kompakteren Format speichern, um die massive Verdoppelung von Tag- / Attributnamen zu vermeiden. Vielleicht auch, um Attributen native Typen zu geben, so werden beispielsweise Gleitkommadaten als 4 Bytes pro Gleitkomma gespeichert, nicht als Textzeichenfolge.

Google / Wikipedia zeigen, dass 'binäres XML' kaum ein neues Problem darstellt - es wurde bereits mehrmals gelöst. Hat hier jemand Erfahrung mit einem der vorhandenen Systeme / Standards? - Gibt es eine ideale Lösung für Spiele - Mit einer kostenlosen, leichten und plattformübergreifenden Parser / Loader-Bibliothek (C / C ++)?

Oder soll ich dieses Rad selbst neu erfinden?

Oder ist es besser für mich, das Ideal zu vergessen und nur meine rohen XML-Daten zu komprimieren (es sollte sich gut mit einer zip-ähnlichen Komprimierung packen lassen) und nur den Speicher- / Leistungstreffer beim Laden zu berücksichtigen?

Bluescrn
quelle
1
XML kann mit gzip et al sehr gut komprimiert werden .
ThiefMaster

Antworten:

18

Wir haben für Superman Returns: The Videogame viel binäres XML verwendet . Wir sprechen von Tausenden und Abertausenden von Dateien. Es hat gut funktioniert, aber ehrlich gesagt schien es die Mühe nicht wert zu sein. Es hat einen beachtlichen Teil unserer Ladezeit in Anspruch genommen, und die "Flexibilität" von XML hat sich nicht vergrößert. Nach einer Weile hatten unsere Datendateien zu viele seltsame Bezeichner, externe Referenzen, die synchron gehalten werden mussten, und andere seltsame Anforderungen, damit sie wirklich nicht mehr von Menschen bearbeitet werden konnten.

XML ist auch wirklich ein Markup-Format und kein Datenformat. Es ist für viel Text mit gelegentlichen Tags optimiert. Es ist nicht gut für vollständig strukturierte Daten. Es war nicht mein Anruf, aber wenn es gewesen wäre und ich wusste, was ich jetzt weiß, hätte ich wahrscheinlich JSON oder YAML gemacht. Sie sind so kurz, dass keine Komprimierung erforderlich ist, und sie sind für die Darstellung von Daten und nicht von Text optimiert .

herrlich
quelle
1
Es gibt eine Binärversion von JSON namens BSON .
Philipp
12

Speichere und bearbeite deine Levels als normales XML, aber lasse deine Spiel-Engine es während des Ladens träge in binäres XML backen und speichere das binäre XML zurück auf die Festplatte, damit es das nächste Mal geladen werden kann (falls sich das rohe XML nicht geändert hat). .

Etwas wie das:

data loadXml(xmlFile)
{
    if (xmlFile has changed OR binFile doesn't exist)
    {
        binFile = convertToBinary(xmlFile)
        save(binFile)
    }
    return loadBinaryXml(binFile)
}

So bekommen Sie das Beste aus beiden Welten. Bei der Veröffentlichung müssen Sie nur sicherstellen, dass alle Binärdateien vorhanden sind.

Peter Alexander
quelle
5

Google Protocol Buffers scheinen der richtige Weg zu sein, aber ich habe sie selbst nicht verwendet.
http://code.google.com/p/protobuf/

Sie definieren eine .proto-Datei, die das Dateiformat beschreibt:

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

Dies wird dann mit einem Befehlszeilentool kompiliert, das C / C ++ - Klassen zum Schreiben und Parsen von Binärdatendateien im zuvor definierten Datenformat generiert. Es gibt auch einige Erweiterungen für verschiedene Programmiersprachen.

Der Nachteil von ProtocolBuffer ist, dass es sich nicht um ein Nur-Text-Format handelt. Sie benötigen ein Tool, um sie zu generieren, zu lesen und zu bearbeiten. Dies sollte jedoch kein Problem sein, wenn Sie sie nur zum Datenaustausch zwischen Ihrem Spieleditor und Ihrem Spiel verwenden. Ich würde es nicht verwenden, um Konfigurationsdateien zu definieren;)

Das Komprimieren der unformatierten XML-Dateien sollte ebenfalls funktionieren. Welche Art von Spiel machst du? Wenn es ebenenbasiert ist, sollten Sie alle erforderlichen Ressourcen nur einmal laden, wenn die Ebene geladen wird.

Update: Es gibt mehrere Projekte für andere Sprachen, z. B. C #, für die Zusammenarbeit mit ProtocolBuffers:
http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns

Stephen
quelle
Ist ein Serializer nicht an ein solches Problem angepasst? Ich denke nicht, aber ich sehe keinen deutlichen Unterschied. Diese Antwort erscheint mir jedoch angemessen. Aber auch tar / gzip die XML-Dateien werden ihre Größe erheblich reduzieren (da es sich um Text handelt, aber ich denke, es wird auch für XML funktionieren), so dass dies die "einfachere" Lösung sein könnte. Trotzdem ist XML eine einfache Sprache, aber es ist sehr teuer in Bezug auf das Parsen / Speichern: Wenn Sie XML verwenden, sollten Sie so wenig wie möglich lesen / schreiben.
jokoon
Es ist eine interessante Option, sieht jedoch eher nach einer vollständigen Alternative zur Verwendung von XML an einer beliebigen Stelle in der Pipeline aus. Um ehrlich zu sein, würde ich mich nicht für generierten Code begeistern - und eine weitere Komplikation ist, dass ich C # für die Toolseite verwende (ich bin froh, dass Tools weiterhin mit den großen XML-Dateien arbeiten können ). Ein XML-> PB-Konverter kann eine Option sein, obwohl ich glaube, ich suche immer noch etwas, das eher "binäres Allzweck-XML" als Möglichkeiten zum Backen spezifischer "Daten auf Binärebene" ist (auch wenn das etwas mehr wäre) effizient)
Bluescrn
"Ich benutze C # für die Toolseite der Dinge" Es gibt mehrere Projekte für C #. hat meine Antwort aktualisiert.
Stephen
@bluescrn, ich würde mir keine Sorgen um den generierten Code machen. Google bietet erstklassigen Support für C ++, Java und Python. Sie verwenden es ausgiebig intern; Der generierte Code ist ziemlich robust. Ein großer Vorteil von PB ist das Toolprogramm gegen eine .protoDatei, wodurch Probleme mit der Kommunikation nahezu beseitigt werden. Protos sind viel einfacher zu lesen / zu pflegen als ein XML-Schema, wenn Sie sogar die Disziplin (und Zeit) haben, XML-Schemas zu verwenden.
deft_code
4

Was ist mit dem JSON-Format?

http://www.json.org/xml.html

Sven
quelle
Es sieht etwas kompakter aus als XML, weist jedoch immer noch das Hauptproblem von doppelten Attributnamen auf. Wenn die Datei eine Liste von Spielobjekten mit den Attributen "XPosition", "YPosition" und "Scale" enthält, werden die Zeichenfolgen "XPosition" / "YPosition" / "Scale" für jedes einzelne Spielobjekt dupliziert. Dies ist die Hauptsache, die ich im Moment 'komprimieren'
möchte
1
@bluescrn: Nein, das Problem gibt es nicht. Objekte sind eine Struktur; Sie könnten auch Arrays verwenden [die einfach so aussehen]. Das heißt, Sie können die Namen und Eigenschaften von Autos in etwa so speichern: "cars":{"ford":[8C,FA,BC,2A,384FFFFF],"holden":[00,00,04,FF,04FF54A9]}Sie können sogar die Kennung "cars" weglassen und direkt in ein Array wechseln, wenn Sie wissen, wo sich das Feld cars befindet. Sie können sogar die Namen "ford" und "holden" weglassen, wenn Sie diese Daten nicht speichern müssen [...,[[8C,FA,BC,2A,384FFFFF],[00,00,04,FF,04FF54A9]]]. Wird es kompakter?
Doppelgreener
1
@Axidos: Wenn Sie das Markup als unlesbar und unstrukturiert kennzeichnen möchten, können Sie es auch einfach als binär kennzeichnen. Abgesehen davon handelt es sich um eine falsche Einsparung, es sei denn, Sie analysieren unkomprimierte Daten zur Laufzeit (in diesem Fall sind Sie wahrscheinlich ohnehin geschraubt) oder sind während des Parsens auf einige hundert Byte Zeichenfolgenspeicher beschränkt (es sei denn, Sie sind aktiviert) eine Mikrowelle, bist du nicht).
@ Joe: Bluescrn scheint nach einem lesbaren Format zu suchen, das keine doppelten Namen hat. Ich habe die Fähigkeit von JSON veranschaulicht, genau das zu bieten. Ich stimme jedoch voll und ganz zu, dass Sie sich zu einem bestimmten Zeitpunkt genauso gut fragen könnten, warum Sie sich überhaupt mit so einem Markup beschäftigen.
Doppelgreener
4

Verwenden Sie JSON.

(Aufbauend auf der Antwort von Munificent und vor allem auf Ihre Bedenken, die Sie an anderer Stelle geäußert haben)

Sie haben die Besorgnis erwähnt, dass JSON das Problem hat, Speicherplatz-Benennungselemente wie XML zu verschwenden. Das tut es nicht.

JSON baut auf zwei Strukturen auf: Name / Wert-Paare ( Objekte ) und geordnete Wertelisten ( Arrays ). XML basiert nur auf Name / Wert-Paaren.

Wenn Sie glauben, JSON stützt sich auf Objekte, die Sie in JSON gelesen haben, und die so aufgebaut sind, dass sie selbsterklärend und für den Menschen lesbar sind (wobei einzelne Bytes durch oktale Ziffernpaare dargestellt werden):

{
    "some": ...,
    "data": ...,
    "fields": ...,
    "cars": [
        {"name":"greg","cost":8C,"speed":FA,"age":04,"driverID":384FFFFF},
        {"name":"ole rustbucket","cost":00,"speed":00,"age":2A,"driverID":04FF54A9}
    ]
}

Sie haben jedoch auch die Möglichkeit, es so zu schreiben, solange Sie wissen, wo sich alles befinden wird (und daher nach Index 4 anstatt nach Objekt "Autos" suchen können, um Ihre Liste der Autos zu erhalten):

{
    [
        ...,
        ..., 
        ...,
        [["greg",8C,FA,04,384FFFFF],["ole rustbucket",00,00,2A,04FF54A9]],
        ...,
    ]
}

Ist es prägnanter als nur mit [, ], ,und Ihre Werte?

Nun, wenn Sie gewillt sind, einem reinen Binärstrom immer näher zu kommen.

"cars":{"names":["greg","ole rustbucket"],"stream":8CFA04384FFFFF00002A04FF54A9}
or
[["greg","ole rustbucket"],8CFA04384FFFFF00002A04FF54A9]

Schießen Sie sich einfach nicht ins Bein, indem Sie zu viel optimieren.

doppelgreener
quelle
2

Ich weiß, dass Sie eine Antwort akzeptiert haben, aber Google sowohl "Fast Infoset" (binäres XML) als auch vtd-xml.

Obwohl letzteres (VTD) den Komprimierungsaspekt Ihrer XML-Nutzung möglicherweise nicht auflöst, kann es den Knotenzugriff über große Dateien erheblich beschleunigen (es verwendet ein binäres Offsetwörterbuch, um zu Knoten zu springen, und erstellt keine Objekte für jeden Knoten stattdessen an der ursprünglichen XML-Zeichenfolge arbeiten). Daher ist die XML-Suche schneller und es wird nicht so viel In-Process-Speicher benötigt, um auf das XML-Dokument zuzugreifen bzw. es zu bearbeiten.

Beide oben genannten haben Bindungen in den gängigen Sprachen (einschließlich C #).

Prost

Reich

Big Rich
quelle
1

Sie könnten Karvonite versuchen . Es soll agil sein. Es handelt sich um ein Persistenz-Framework, das sich gut an die Änderungen in Ihren Daten anpasst (was im Vergleich zum Umgang mit Binärdateien bei Ihnen selbst nützlich ist). Ich bin mir nicht sicher, wie die Daten strukturiert sind, aber die Dateien sind viel kleiner als aufgeblähte XML-Dateien. (Ich gehe davon aus, dass es die Daten in einem binären Format anstelle von Text wie xml speichert)

Der einzige Nachteil, den ich mir dabei vorstellen kann, ist, dass wenn Ihre Daten verfälscht oder in einer Weise durcheinander gebracht werden, die Karvonite nicht mag, Sie dem Schöpfer ausgeliefert sind, es sei denn, Sie wissen, wie die Struktur der Daten aufgebaut ist Daten funktionieren.

Wenn Sie angeben, wie Ihre Daten gespeichert / geladen werden sollen, öffnen Sie einfach den zugehörigen Persistenz-Editor, importieren Ihre Assembly mit allen Datenobjekten und aktivieren einige Kontrollkästchen, um anzuzeigen, welche Objekte unterstützt werden sollen und welche Felder / Eigenschaften gespeichert werden sollen.

Es könnte einen Versuch wert sein. Da Sie C # verwenden, passt dies genau zu Ihrer Sprache, da es mit XNA funktioniert (Windows, Xbox360 und Windows Phone 7, an denen Sie meines Erachtens interessiert sind, seit Sie das iPhone erwähnt haben?).

Bearbeiten: Gerade bemerkt, dass Sie nur C # für die Tools verwenden. Dies würde wahrscheinlich nicht sehr gut in Ihren Workflow passen. Aus irgendeinem Grund hatte ich XNA im Kopf.

Michael Coleman
quelle