2D-Grafiken - Warum Spritesheets verwenden?


62

Ich habe viele Beispiele für das Rendern von Sprites aus einem Spritesheet gesehen, aber ich habe nicht verstanden, warum dies die häufigste Art ist, mit Sprites in 2D-Spielen umzugehen.

Ich habe in den wenigen Demo-Anwendungen, die ich erstellt habe, mit dem Rendern von 2d-Sprites begonnen, indem ich jeden Animationsrahmen für einen bestimmten Sprite-Typ als eigene Textur behandelt habe - und diese Sammlung von Texturen wird in einem Wörterbuch gespeichert. Dies scheint für mich zu funktionieren und passt ziemlich gut zu meinem Arbeitsablauf, da ich meine Animationen in der Regel als GIF- / MNG-Dateien erstelle und dann die Frames in einzelne PNGs extrahiere.

Gibt es einen spürbaren Leistungsvorteil beim Rendern von einem einzelnen Blatt anstatt von einzelnen Texturen? Ist es bei moderner Hardware, die in der Lage ist, Millionen von Polygonen hundertmal pro Sekunde auf den Bildschirm zu ziehen, sogar für meine 2D-Spiele von Bedeutung, die nur ein paar Dutzend Rechtecke mit einer Auflösung von 50 x 100 Pixel verarbeiten?

Die Implementierungsdetails zum Laden einer Textur in den Grafikspeicher und zum Anzeigen in XNA scheinen ziemlich abstrakt zu sein. Ich weiß nur, dass Texturen beim Laden an das Grafikgerät gebunden werden. Während der Spieleschleife werden die Texturen stapelweise gerendert. Daher ist mir nicht klar, ob meine Wahl die Leistung beeinflusst.

Ich vermute, dass es einige sehr gute Gründe gibt, warum die meisten 2D-Spieleentwickler sie zu verwenden scheinen. Ich verstehe nur nicht, warum.


Nein, es ist nicht wirklich wichtig für deine 50 Sprites. Aber was gibt es zu verlieren?
Die kommunistische Ente

Antworten:


69

Ein starkes Argument für die Verwendung von Spritesheets ist, dass die Anzahl der verfügbaren Texturen auf einer Grafikkarte begrenzt werden kann. Daher müsste Ihre Grafikbibliothek ständig Texturen entfernen und Texturen auf der GPU neu zuweisen. Es ist viel effizienter, eine große Textur nur einmal zuzuweisen.

Berücksichtigen Sie außerdem, dass die Texturgröße normalerweise Potenz 2 ist. Wenn Sie also ein Sprite mit 50 x 100 Pixel haben, weisen Sie Texturen mit der Größe 64 x 128 Pixel oder im schlimmsten Fall 128 x 128 Pixel zu. Das verschwendet nur Grafikspeicher. Packen Sie besser alle Sprites in eine 1024x1024px-Textur, die 20x10 Sprites zulässt, und Sie verlieren nur 24 Pixel horizontal und vertikal. Manchmal werden sogar Sprites unterschiedlicher Größe zu einem riesigen Sprite-Sheet kombiniert, um die Textur so effizient wie möglich zu nutzen.

Nachtrag: Ein sehr wichtiger Grund für die Verwendung von Sprite-Sheets besteht darin, die Anzahl der Zugriffe auf Ihre GPU zu verringern, was sich erheblich auf die Leistung auswirken kann. Dies wurde in anderen Antworten angegeben und ich füge dies der Vollständigkeit halber hinzu, damit dies etwas sichtbarer wird.


1
Vielen Dank, ich habe keinen dieser Punkte berücksichtigt. Wissen Sie ungefähr, wie hoch die Anzahl der verfügbaren Texturen ist? Ich habe diese Statistik noch nie in den Grafikspezifikationen gesehen. Ist die Grenze im Grunde gleich der Größe des Grafik-RAM?
Columbo

Normalerweise ist die Grenze der GPU-Speicher, ja. Ich dachte, es gäbe einige Einschränkungen in Bezug auf die Anzahl der Texturen auf älteren Karten, aber ich kann anscheinend keine Referenz finden, die diesen Punkt beweist.
Mistzack

12
Auch wenn es keine Mengenbeschränkung gibt, ist der Busverkehr für das Bewegen vieler kleiner Bilder nicht so effizient wie für eine (oder mehrere) große Übertragung (en).
Mordachai

5
Gute Punkte, aber der wichtigste Aspekt ist, Pipeline-Stillstände zu minimieren, indem der Draw-Call-Countdown beibehalten wird. GPUs bevorzugen eine kleine Anzahl von großen Jobs anstelle einer großen Anzahl kleiner Jobs. Wenn Sie alle Ihre Sprites in einer kleinen Menge großer Blätter platzieren, können Sie die Textphase einmal einrichten und mehrere Sprites gleichzeitig in einem Stapel zeichnen. Spritebatch erledigt das für Sie. Wenn Sie mehr als ein Spritesheet haben, müssen Sie Spritebatch anweisen, nach Textur zu sortieren
Cubed2D

Wenn Sie die Texturkomprimierung hinzufügen, wird jede Lücke zu einem winzigen Overhead
Joe Plante,

36

Ich würde sagen, das Argument wäre die Fähigkeit, mehrere Dinge in einem einzigen Draw-Aufruf zu rendern. Nehmen wir zum Beispiel das Rendern von Schriften, ohne ein Spritesheet müssten Sie jedes Zeichen mit einem separaten Texturtausch rendern, gefolgt von einem Zeichenaufruf. Wenn Sie sie in ein Spritesheet werfen, können Sie mit einem einzigen Zeichenaufruf ganze Sätze rendern (wobei die unterschiedlichen Zeichen in der Schriftart nur durch Angabe der verschiedenen UV-Werte für die Ecken ermittelt werden). Dies ist eine viel effizientere Methode zum Rendern, wenn der Aufwand für das Rendern von Inhalten auf vielen Plattformen darin besteht, zu viele API-Aufrufe zu tätigen.

Es kann auch helfen, Platz zu sparen, aber es hängt davon ab, was Sie zuerst in das Spritesheet packen.


2
+1 Berührt bei Zeichnungsanrufen. (etwas, das ich in der akzeptierten Antwort nicht gesehen habe)
Michael Coleman

11

Jeder Draw Call hat einen gewissen Overhead. Mit Sprite-Arbeitsblättern können Sie die Zeichnung von Dingen stapeln, die nicht den gleichen Frame einer Animation verwenden (oder allgemeiner, alles, was sich auf demselben Material befindet), um die Leistung erheblich zu verbessern. Für moderne PCs ist dies abhängig von Ihrem Spiel vielleicht nicht allzu wichtig, aber es ist definitiv wichtig, zum Beispiel für das iPhone.


2
Es ist definitiv immer noch wichtig. Die Grafikhardware wurde dahingehend optimiert, Tonnen von Polygonen für eine kleine Anzahl detaillierter Modelle (z. B. Charaktere) zu verschieben, da die meisten Spiele in diese Richtung tendieren. Das Ergebnis ist, dass Sie in jedem von nur ein paar Dutzend Draw Calls so viele Polygone wie möglich verschieben müssen. Game-Engines, die große Szenen wie Städte rendern, kombinieren häufig mehrere Texturen im laufenden Betrieb zu einer, um Draw Calls zu reduzieren.
jhocking

Ich frage mich auf jeden Fall, ob das Textur-U, V-Mapping in einer Menge 3D-Hardware sowie Bit-Blitting-Operationen vom Sprite-Sheet profitieren.
Joe Plante

7

Neben Überlegungen zur Leistung können Sprite-Blätter beim Erstellen der Grafik hilfreich sein. Jedes Zeichen kann sich auf einem eigenen Blatt befinden, und Sie können alle Frames anzeigen, indem Sie einfach einen Bildlauf durchführen. Es hilft mir, einen konsistenten Überblick über alle Frames zu behalten.

Außerdem codiere ich in AS3, und für jede Bilddatei ist eine separate Einbettungsanweisung erforderlich. Ich hätte lieber eine einzelne Einbettung eines gesamten Spritesheets als 24 Einbettungen für alle Frames, selbst wenn ich eine separate Ressourcenklasse verwende.


1
+1. Obwohl das OP kein Flash verwendet, ist das Einbetten oder Laden auch ein Vorteil für Spritesheets. Dies ist auch relevant, wenn Assets von einem Webserver geladen werden, bei dem 1 Anforderung gegenüber mehreren hundert Anforderungen (für jeden "Frame") bevorzugt wird.
Bummzack

Auf jeden Fall möchten Sie die Serveranforderungen für ein Online-Spiel reduzieren. Wir haben überlegt, ob es besser ist, die Bilder in eine SWF- oder eine Spritesheet-Datei zu kopieren, da beide dieses Hauptziel erreichen (wir haben schließlich entschieden, dass Spritesheets für unsere Künstler weniger arbeitsintensiv sind).
jhocking

7

Ein weiterer Grund für die Verwendung von Spritesheets mit XNA ist, dass bei der Xbox360-Entwicklung ein bekanntes Problem mit langsamen Bereitstellungs- / Ladezeiten im Verhältnis zur Anzahl der Dateien in Ihrem Projekt besteht. Wenn Sie also viele kleine Bilddateien zu einer einzigen Bilddatei kombinieren, können Sie dieses Problem lösen.


5

Ich werde hier nicht auf Speicher / GPU eingehen, da es so genug Antworten gibt.

Stattdessen kann es Ihre Kunst aufklären, während Sie daran arbeiten - und danach. Anstatt 10 Bilder durchzublättern, um den Laufzyklus zu sehen, können Sie alle Bilder auf einmal sehen. Wenn Sie dies verteilen möchten, müssen Sie statt 10 nur noch 1 Datei bearbeiten.

Und dann ist es auch sauberer (aus organisatorischer Sicht), character.png und enemy.png über characterwalk01 bis characterwalk10, dann characterattack01 bis characterattack05 zu haben, und es geht weiter.


4

Der Grafikspeicher verfügt nicht über eine aufgeblähte Speicherverwaltung wie Dateisysteme auf Datenträgern. Das Gegenteil ist der Fall: Es ist einfach und schnell gehalten .

Eine solche einfache und schnelle Speicherverwaltung könnte so aussehen, als hätte man nur ein einziges riesiges 4096x4096-Sprite, das durch Halbieren seiner Breite und / oder Höhe in mehrere kleinere Sprites geschnitten wird. (Es gibt eine ähnliche 1-dimensionale Speicherverwaltungstechnik, aber ich habe den Namen vergessen.) Schließlich muss eine Defragmentierung durchgeführt werden.

Um eine solche Speicherverwaltung zur Laufzeit zu überspringen , füllen Entwickler einzelne große Sprites beim Kompilieren oder sogar während des Designprozesses der Grafiken automatisch mit mehreren kleineren Sprites .


3

Vergessen Sie auch nicht die E / A-Vorgänge, die Sie möglicherweise während der Initialisierung sparen. Wenn Sie von einem CD-Laufwerk laden, handelt es sich bei jeder Datei um einen zusätzlichen E / A-Aufruf. Die Suche kann einige Zeit in Anspruch nehmen (moderne Festplatten haben eine Länge von etwa 15 ms pro Datei, CDs von etwa 50 bis 100 ms?). Dies kann Ihnen viel Initialisierungszeit sparen, da nur eine Datei abgerufen werden muss. Außerdem kann Ihr Betriebssystem diese E / A-Vorgänge zwischenspeichern, falls Ihre Anwendung etwas anderes ausführt (z. B. auf die GPU warten).


1
Wenn Sie das Problem der Dateisuche lösen möchten, können Sie alle Ihre Sprites in einer einzigen Datei unter Verwendung eines benutzerdefinierten Formats packen. Die meisten Spiele machen das.
Tigrou

0

Ich bin nicht nur leistungsfähiger, sondern finde es auch einfacher. Es erfordert ein wenig zusätzliche Arbeit, um sicherzustellen, dass Ihre Bilder beim Erstellen der Grafik immer innerhalb ihrer Grenzen bleiben. Es ist jedoch viel einfacher, alle Bilder zu sehen, mit denen Sie sich gerade beschäftigen.

Es ist meiner Meinung nach auch einfacher zu codieren. Ich habe einfach eine Reihe von Objekten wie:

sheet = {
    img: (the actual image),
    rects: [
        {x:0, y:0, w:32, h:32},
        {x:32, y:0, w:32, h:32},
        {x:64, y:0, w:32, h:32}
    ]
};

sheets.push(sheet);

Dann gebe ich für jedes Item zwei Werte, sheet und rect. Das Blatt wäre der Index in dem Array von Objekten und das Rect wäre der Index in dem Rect-Array dieses Blatts.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.