Frage
Was machst du, wenn du ein 2D-Spiel hast, das sehr große Bilder verwendet (die größer sind, als deine Hardware unterstützt)? Vielleicht gibt es eine interessante Lösung für dieses Problem, die mir noch nie zuvor aufgefallen ist.
Im Grunde arbeite ich an einer Art Grafik-Adventure-Game-Maker. Meine Zielgruppe sind Leute mit wenig bis gar keiner Erfahrung in der Spieleentwicklung (und Programmierung). Wenn Sie mit einer solchen Anwendung arbeiten, möchten Sie nicht, dass sie das Problem intern löst, anstatt Ihnen mitzuteilen, dass Sie "Ihre Bilder aufteilen" sollen?
Kontext
Es ist allgemein bekannt, dass Grafikkarten eine Reihe von Beschränkungen hinsichtlich der Größe der Texturen auferlegen, die sie verwenden können. Wenn Sie beispielsweise mit XNA arbeiten, sind Sie auf eine maximale Texturgröße von 2048 oder 4096 Pixel beschränkt, je nachdem, welches Profil Sie auswählen.
In 2D-Spielen ist dies normalerweise kein großes Problem, da die meisten Level aus kleineren Einzelstücken (z. B. Kacheln oder transformierten Sprites) aufgebaut werden können.
Denken Sie jedoch an einige klassische Point'n'Click-Grafik-Abenteuerspiele (z. B. Monkey Island). Räume in grafischen Abenteuerspielen sind in der Regel als Ganzes gestaltet, mit wenigen oder keinen wiederholbaren Abschnitten, genau wie ein Maler, der an einer Landschaft arbeitet. Oder vielleicht ein Spiel wie Final Fantasy 7, das große vorgerenderte Hintergründe verwendet.
Einige dieser Räume können ziemlich groß werden und häufig drei oder mehr Bildschirme mit einer Breite von bis zu 30 cm umfassen. Wenn wir diese Hintergründe nun im Kontext eines modernen High-Definition-Spiels betrachten, überschreiten sie leicht die maximal unterstützte Texturgröße.
Die naheliegende Lösung besteht nun darin, den Hintergrund in kleinere Abschnitte zu unterteilen, die den Anforderungen entsprechen, und diese nebeneinander zu zeichnen. Aber sollten Sie (oder Ihr Künstler) derjenige sein, der alle Ihre Texturen aufteilt?
Wenn Sie mit einem High-Level-API arbeiten, sollte es dann nicht vielleicht darauf vorbereitet sein, mit dieser Situation umzugehen und die Texturen intern aufzuteilen und zusammenzusetzen (entweder als Vorprozess wie die Inhaltspipeline von XNA oder zur Laufzeit)?
Bearbeiten
Hier ist, was ich dachte, aus der Sicht der XNA-Implementierung.
Meine 2D-Engine benötigt nur einen kleinen Teil der Funktionen von Texture2D. Ich kann es tatsächlich auf diese Schnittstelle reduzieren:
public interface ITexture
{
int Height { get; }
int Width { get; }
void Draw(SpriteBatch spriteBatch, Vector2 position, Rectangle? source, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
}
Die einzige externe Methode, die ich verwende und die auf Texture2D basiert, ist SpriteBatch.Draw (), sodass ich eine Brücke zwischen ihnen erstellen kann, indem ich eine Erweiterungsmethode hinzufüge:
public static void Draw(this SpriteBatch spriteBatch, ITexture texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
{
texture.Draw(spriteBatch, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
}
Auf diese Weise kann ich eine ITexture austauschbar verwenden, wenn ich zuvor eine Texture2D verwendet habe.
Dann könnte ich zwei verschiedene Implementierungen von ITexture erstellen, z. B. RegularTexture und LargeTexture, wobei RegularTexture einfach eine reguläre Texture2D umschließt.
Andererseits würde LargeTexture eine Liste von Texture2Ds führen, die jeweils einem der aufgeteilten Teile des Vollbilds entsprechen. Der wichtigste Teil ist, dass das Aufrufen von Draw () in der LargeTexture in der Lage sein sollte, alle Transformationen anzuwenden und alle Teile so zu zeichnen, als wären sie nur ein zusammenhängendes Bild.
Um den gesamten Prozess transparent zu machen, würde ich eine einheitliche Texture Loader-Klasse erstellen, die als Factory-Methode fungieren würde. Es würde den Pfad der Textur als Parameter nehmen und eine ITexture zurückgeben, die entweder eine RegularTexture oder eine LargeTexture ist, abhängig davon, ob die Bildgröße die Grenze überschreitet oder nicht. Der Benutzer musste jedoch nicht wissen, um welches es sich handelte.
Ein bisschen übertrieben oder klingt es okay?
Antworten:
Ich denke, Sie sind auf dem richtigen Weg. Sie müssen die Bilder in kleinere Teile teilen. Da Sie einen Game Maker erstellen, können Sie die Content-Pipeline von XNA nicht verwenden. Es sei denn, Ihr Hersteller ist eher eine Engine, für die die Entwickler weiterhin Visual Studio verwenden müssen. Sie müssen also Ihre eigenen Routinen erstellen, die die Aufteilung durchführen. Sie sollten sogar in Betracht ziehen, die Teile vorher zu streamen, damit Sie immer noch nicht einschränken, wie viel Videospeicher die Player haben sollen.
Es gibt einige Tricks, die Sie speziell für Abenteuerspiele machen können. Ich stelle mir vor, wie bei allen klassischen Abenteuerspielen werden Sie einige Schichten dieser Texturen haben, damit ... Ihr Charakter hinter dem Baum oder dem Gebäude wandeln kann ...
Wenn Sie möchten, dass diese Ebenen einen Paralaxing-Effekt haben, überspringen Sie beim Teilen Ihrer Fliesen einfach alle durchscheinenden Bereiche einmal. Hier muss man sich die Größe der Fliesen überlegen. Kleinere Kacheln erhöhen den Verwaltungsaufwand und zeichnen Anrufe, enthalten jedoch weniger Daten, während größere Kacheln GPU-freundlicher sind, aber eine hohe Transparenz aufweisen.
Wenn Sie keinen Paralaxing-Effekt haben, können Sie, anstatt die gesamte Szene mit 2-4 riesigen Bildern mit einer Tiefe von 32 Bit abzudecken, nur eine 32-Bit-Tiefe erstellen. Sie können auch eine Schablonen- oder Tiefenebene mit nur 8 Bit pro Pixel erstellen.
Ich weiß, dass es für Nicht-Spieleentwickler schwierig ist, Assets zu verwalten, aber das muss es eigentlich nicht sein. Wenn Sie zum Beispiel eine Straße mit 3 Bäumen haben und der Spieler hinter 2 davon und vor dem dritten Baum und dem Rest des Hintergrunds steht ... Stellen Sie das Szenenlayout in Ihrem Editor so ein, wie Sie es möchten und dann in einem Bau Schritt von Ihrem Game Maker können Sie diese riesigen Bilder (auch kleine Kacheln eines großen Bildes) backen.
Wie die klassischen Spiele das machten. Ich bin mir nicht sicher, ob sie wahrscheinlich ähnliche Tricks gemacht haben, aber man muss bedenken, dass die Spiele zu der Zeit 320x240 Farben hatten, was es bei Verwendung palattelisierter Texturen ermöglicht, 2 Pixel in einem Byte zu kodieren. Aber so funktionieren die aktuellen Architekturen nicht: D
Viel Glück
quelle
Schneiden Sie sie auf, damit sie nicht mehr beliebig groß sind, wie Sie sagen. Es gibt keine Magie. Diese alten 2D-Spiele haben dies entweder getan oder sie wurden in Software gerendert. In diesem Fall gab es keine Hardwareeinschränkungen über die RAM-Größe hinaus. Bemerkenswert ist, dass viele dieser 2D-Spiele keinen großen Hintergrund hatten, sondern nur langsam scrollten, sodass sie sich riesig anfühlten.
Was Sie in Ihrem Grafik-Adventure-Game-Maker tun möchten, ist, diese großen Bilder in einer Art "Build" - oder "Integrations" -Schritt vorzubereiten, bevor das Spiel zum Laufen gebracht wird.
Wenn Sie eine vorhandene API und Bibliothek verwenden möchten, anstatt Ihre eigenen zu schreiben, empfehlen wir Ihnen, diese großen Bilder während dieses "Builds" in Kacheln umzuwandeln und eine 2D-Kachel-Engine zu verwenden, von denen es viele gibt.
Wenn Sie Ihre eigenen 3D-Techniken verwenden möchten, möchten Sie möglicherweise trotzdem in Kacheln aufteilen und eine bekannte und gut konzipierte 2D-API verwenden, um Ihre eigene 3D-Bibliothek dafür zu schreiben. Auf diese Weise können Sie sicher sein, dass Sie zumindest damit beginnen Ein gutes API-Design und weniger Design von Ihrer Seite.
quelle
Wenn Sie wissen, wie ein Quadtree in der visuellen Praxis aussieht, habe ich eine Methode verwendet, mit der detaillierte große Bilder in kleinere Bilder geschnitten werden, die dem Aussehen eines Proofs / einer Visualisierung eines Quadtrees relativ ähnlich sind. Meine wurden jedoch so erstellt, dass bestimmte Bereiche geschnitten wurden, die je nach Farbe unterschiedlich codiert oder komprimiert werden konnten. Sie können diese Methode verwenden und das tun, was ich beschrieben habe, da sie auch nützlich ist.
Wenn sie zur Laufzeit gekürzt werden, ist vorübergehend mehr Speicher erforderlich, und die Ladezeiten werden verlangsamt. Ich würde jedoch sagen, dass es davon abhängt, ob Sie mehr Wert auf physischen Speicher oder die Ladezeit eines mit Ihrem Editor erstellten Benutzerspiels legen.
Lade- / Laufzeit: Die Art und Weise, wie ich die Ladung ausführen würde, besteht darin, sie in proportional große Stücke zu schneiden. Rechnen Sie mit einem Pixel ganz rechts, wenn die Anzahl der Pixel jedoch ungerade ist. Niemand mag das Ausschneiden seiner Bilder, insbesondere unerfahrene Benutzer, die möglicherweise nicht wissen, dass es nicht ganz an ihnen liegt. Machen Sie sie halb so breit ODER halb so hoch (oder 3rds, 4rds, was auch immer). Der Grund dafür, dass sie nicht in Quadrate oder 4 Abschnitte (2 Zeilen, 2 Spalten) aufgeteilt werden, ist, dass dadurch der Kachelspeicher kleiner wird, da Sie sich nicht um x und y kümmern würden zur gleichen Zeit (und abhängig davon, wie viele Bilder so groß sind, könnte es ziemlich wichtig sein)
Vor der Hand / Automatisch: In diesem Fall gefällt mir die Idee, dem Benutzer die Wahl zu geben, ob er es in einem Bild oder als separate Teile speichern möchte (ein Bild sollte die Standardeinstellung sein). Das Speichern des Bildes als .gif würde sehr gut funktionieren. Das Bild würde sich in einer Datei befinden, und anstatt dass jeder Frame eine Animation ist, würde es sich eher um ein komprimiertes Archiv desselben Bildes handeln (was im Wesentlichen das ist, was ein GIF ist). Dies würde den physischen Datentransport und das Laden vereinfachen:
Oh, und wenn Sie die GIF-Methode verwenden, sollten Sie die Dateierweiterung in etwas anderes ändern (GIF -> Kittens), um weniger Verwirrung zu stiften, wenn Ihr GIF wie eine kaputte Datei animiert wird. (Mach dir keine Sorgen über die Legalität, .gif ist ein offenes Format)
quelle
Das wird sich natürlich anhören, aber warum teilen Sie Ihren Raum nicht einfach in kleinere Teile auf? Wählen Sie eine beliebige Größe, die keine Grafikkarten (z. B. 1024 x 1024 oder höher) trifft, und teilen Sie Ihre Räume einfach in mehrere Teile auf.
Ich weiß, dass dies das Problem vermeidet, und ehrlich gesagt ist dies ein sehr interessantes Problem, das es zu lösen gilt. Auf jeden Fall ist dies eine triviale Lösung, die manchmal für Sie hilfreich sein kann.
quelle