Ich habe einen 2D-Plattformer, der derzeit Chunks mit 100 mal 100 Kacheln verarbeiten kann, wobei die Chunk-Koordinaten als Longs gespeichert werden, so dass dies die einzige Begrenzung für Maps ist (maxlong * maxlong). Alle Entity-Positionen usw. usw. sind blockrelevant und daher gibt es dort keine Begrenzung.
Das Problem, das ich habe, ist, wie ich diese Chunks speichern und darauf zugreifen kann, ohne Tausende von Dateien zu haben. Haben Sie Ideen für ein möglichst schnelles und kostengünstiges HD-Archivformat, bei dem nicht alles auf einmal geöffnet werden muss?
long
das 64-Bit ist (also maxlong istInt64.MaxValue
).Antworten:
Erstellen Sie ein benutzerdefiniertes Kartenformat für Ihr Spiel. Es ist einfacher als Sie vielleicht denken. Verwenden Sie einfach die BinaryWriter-Klasse. Schreiben Sie zuerst die Überschrift in ein paar Zentimetern. Informationen, die in den Header aufgenommen werden sollen:
und auch (und hier kommt der leistungskritische Teil
Mit der obigen Methode können (und sollten) Sie einen Index Ihres Dateiinhalts erstellen, der eine Art Beschreibung (einen benutzerdefinierten Namen für die Region / den Block oder nur die Koordinaten) und als zweiten Wert die Position in der Datei enthält.
Wenn Sie dann einen bestimmten Block laden möchten, müssen Sie nur im Index suchen. Wenn Sie die Position erhalten haben, setzen Sie einfach fileStream.Position = PositionOfChunkFromIndex und Sie können sie laden.
Es geht nur um die Gestaltung des Dateiformats, wobei der Header den Inhalt der Datei am effizientesten beschreibt.
Speichern Sie einfach die Dateien mit einer benutzerdefinierten Erweiterung, die Sie erstellt haben, und los geht's.
BONUS: Fügen Sie BZip2-Komprimierung zu bestimmten Bereichen der Datei / zum gesamten Inhalt (nicht zum Header !!) hinzu, damit Sie bestimmte Teile aus der Datei entpacken können, um einen sehr geringen Speicherbedarf zu erzielen.
quelle
Ich hatte ein ähnliches Problem und beschloss, eine eigene Struktur für den Umgang mit den Daten zu erstellen. Es basiert lose auf einem Quadtree, ist aber unendlich (mindestens so groß wie ein Int) erweiterbar in alle Richtungen. Es wurde entwickelt, um gitterbasierte Daten zu verarbeiten, die von einem zentralen Punkt aus erweitert wurden, so wie es Minecraft jetzt tut. Es ist platzsparend im Speicher und sehr schnell.
Sie können für jeden Knoten eine minimale Größe angeben (eine Größe von 7 wäre 128 x 128). Sobald ein Knoten einen bestimmten Prozentsatz seiner Unterknoten hat, wird er automatisch zu einem zweidimensionalen Array geglättet. Dies bedeutet, dass ein sehr dicht besiedelter Teil (z. B. ein vollständig erforschter Kontinent) die Leistung eines Arrays (sehr schnell) aufweist, ein dünn besiedelter Teil (z. B. eine Küstenlinie, die auf und ab gewandert ist, jedoch nicht im Landesinneren erforscht wurde) jedoch haben eine gute Leistung und eine geringe Speichernutzung.
Mein Code kann gefunden werden hier . Der Code ist vollständig, getestet (Unit- und Load-Tests) und ziemlich optimiert. Das Innenleben ist jedoch noch nicht gut dokumentiert, aber alle öffentlichen Methoden sind so, dass es brauchbar sein sollte. Wenn jemand es ausprobieren möchte, kann er sich gerne mit Fragen oder Kommentaren an mich wenden.
Ich habe es noch nicht zum Speichern von Daten in einer Datei verwendet, aber es ist ein interessantes Problem, und ich werde es möglicherweise als nächstes angehen.
quelle
Sie könnten stattdessen eine Datenbank verwenden - PostgreSQL verfügt über einige spezielle Indizierungsfunktionen, die für diese Art von Daten optimiert sind, die sich anhand der X- und Y-Koordinaten befinden. Sie können auch festlegen, dass sich die zurückgegebenen Daten nicht in einem quadratischen oder rechteckigen Bereich, sondern in einem bestimmten Radius befinden.
PostgreSQL (kostenlos und Open Source)
http://www.postgresql.org/
Es gibt auch andere Datenbanken, und für die Client-Seite sind bestimmte Typen möglicherweise besser geeignet, da sie eigenständig ausgeführt werden können (initiiert von Ihrer Game-Client-Anwendung) oder als Teil einer Codebibliothek enthalten sein können dass Sie "nur verwenden können." Der Vorteil ist, dass Sie kein Indizierungsschema entwerfen müssen, da die meisten SQL-Datenbank-Engines dies bereits recht gut können.
Ein Vorteil des Datenbankansatzes besteht darin, dass Sie Ihre Blöcke verkleinern können (oder Blöcke vollständig entfernen und nur Kacheln direkt verwenden, aber die Verwendung von mindestens kleinen Blöcken / Gruppen von vielen Kacheln kann je nach Ihrem Design effizienter sein). Verwenden Sie dann die SQL-Abfrage, um einen größeren Bereich einzublenden, als angezeigt werden kann. Durch das Vorladen, um nicht sichtbare Bereiche in der Nähe zu überlappen, können die Kacheln vorbereitet werden, bevor der Spieler seinen Charakter bewegt, was zu einem besseren (hoffentlich reibungsloseren) Spielerlebnis führt.
Ich habe bemerkt, dass einige Spiele einen "Cache" der Kartendaten auf der lokalen Festplatte speichern, nachdem sie diese zum ersten Mal abgerufen haben (dies ist zweifellos dazu gedacht, die Netzwerk-E / A zu reduzieren), wie zum Beispiel Ashen Empires:
Ashen Empires (kostenlos spielbar, wunderschöne 2D-Implementierung)
http://www.ashenempires.com/
Das Verfolgen von "zuletzt aktualisierten" Zeitstempeln mit jedem Block / jeder Kachel ist auch hilfreich, da die SQL-Abfrage, wenn lokal gespeicherte Daten verfügbar sind, eine zusätzliche Klausel "WHERE timestamp_column> $ local_timestamp" enthalten könnte, sodass nur aktualisierte Blöcke / Kacheln abgerufen werden heruntergeladen (zwei Vorteile, die durch das Einsparen von Bandbreite entstehen, sind geringere Verbindungskosten und eine geringere Verzögerung für Ihre Spieler, was sich mit zunehmender Beliebtheit Ihres Spiels noch deutlicher bemerkbar macht).
Ein Screenshot von Ashen Empires (ein paar Charaktere sind in einer örtlichen Bank und so wie diese Knochen auf dem Boden aussehen, sehen sie aus, als wären ein paar Skelettmonster hereingewandert und wurden wahrscheinlich von den Wachen der örtlichen Stadt geschlachtet):
quelle
Speichere und greife nicht auf sie zu, sondern speichere nur die erforderlichen zufälligen Samen sowie die Änderungen des Spielers auf der Karte. Generieren Sie dann zur Laufzeit die erforderlichen Teile (führen Sie Ihren Generierungsalgorithmus aus, und wenden Sie dann die Änderungen des Players an). Bei korrekter und konsistenter Generierungsprozedur ist die resultierende Karte für denselben Startwert immer dieselbe.
Theoretisch können Sie buchstäblich unendlich viele Karten erstellen, die auf diese Weise in einer sehr kleinen Datei gespeichert werden.
quelle
Gibt es eine Möglichkeit, Chunks (Subkontinente / Länder in Ihrer Welt) zu partitionieren? Vielleicht können Sie also eine Art Index-Datei haben, mit der Sie schnell herausfinden können, welche Unterdatei / welcher Teil einer größeren Datei geladen werden muss, um einen Teil im Speicher zu haben ...
quelle
Sie könnten die Idee von Minecraft übernehmen. Ursprünglich hatten sie eine Datei pro Stück. Jetzt verwenden sie das MCRegion-Format, bei dem Blöcke in 32 x 32-Bereiche gruppiert und einer davon pro Datei gespeichert wird.
quelle