Sollte ich Spritesheets verwenden, wegen oder trotz meiner großen Anzahl von Bildern?

13

Ich entwickle ein 2D-Spiel und habe viele Sprites. Ich habe 3D-Animationen und -Modelle verwendet, um sie in 2D zu rendern und ihnen das Aussehen von "Fallout" oder "Diablo" zu verleihen. Es ist auch einfacher als von Hand zu zeichnen, lol.

Ich musste die Framerate bereits auf 15 fps reduzieren, was der niedrigste Wert war, den ich erreichen konnte, ohne sie abgehackt wirken zu lassen. Es war jedoch traurig, wie unglaublich glatt 24 Frames aussahen.

Es gibt zwei Gründe, warum ich das getan habe:

1) Reduzieren Sie den Festplattenspeicher. Je weniger Bilder, desto kleiner wird mein Gesamtspiel.

2) Reduzieren Sie den RAM-Verbrauch. Je weniger Bilder geladen werden müssen, desto wahrscheinlicher vermeide ich Probleme mit der RAM-Beschränkung.

Wenn es jedoch eine Möglichkeit gäbe, die Bilder sowohl im Festplattenspeicher als auch im RAM zu komprimieren, würde ich dies tun. Ich habe es schon einmal getestet und die meisten erhalten keine Qualitätsänderung, wenn sie von RGBA8888 auf RGBA5555 umsteigen, und nur einen kleinen Treffer, wenn ich in meinem TexturePacker-Programm auf RGBA4444 umsteige. Ich mache dies derzeit nicht, da SFML anscheinend dieselbe Speichermenge verwendet, unabhängig davon, um welche Art von PNG-Bild es sich handelt. Ich habe nachgeforscht, wie man es anders lädt, aber nichts zu diesem Thema gefunden.

Ich habe viel darüber gelesen, wie man mit 2D-Videospielen umgeht. Der Konsens ist überwältigend: Packe deine Sprites in eine größere Textur, um eine großartige Leistung zu erzielen! Also packe ich meine winzigen Sprites mit TexturePacker in ein viel größeres Spritesheet.

Ich plane jedoch 10-15 Animationen pro Charakter, 5 Bewegungsrichtungen und 15-40 Bilder pro Animation (wahrscheinlich durchschnittlich 24). Mit 15 Animationen, 5 Richtungen und durchschnittlich 24 Bildern pro Animation; Das sind 1800 Einzelbilder pro Zeichen. Wenn in einem Sprite-Blatt gepackt, sind das stattdessen nur 75 Bilder. (Ein Sprite-Blatt pro Animation, pro Richtung. 15 * 5)

Für den einen großen Boss im Spiel kann ich kein Spritesheet verwenden und muss eine Methode programmieren, um einfach jeweils ein Bild zu laden. Ich weiß noch nicht, ob ich das für die Aufführung tun kann.

Für die Charaktere packe ich sie bereits in ein Spritesheet. Für einen einzelnen Charakter, der herumläuft, scheint dies die meiste Zeit zu funktionieren, obwohl es manchmal zum Stillstand kommt. Ich schreibe das jedoch meinem schlecht konzipierten Code zu, der Texturen austauscht, anstatt alle Texturen für dieses Zeichen vorzuladen.

Wenn ich die Texturen vorlade, ist das für Sprite-Sheets sinnvoll. Ich würde mir nur vorstellen, dass es eine schlechte Idee ist, 1800 winzige Bilder für jeden Charakter vorzuladen.

Ich stelle mir jedoch vor, dass es extrem schnell ist, sie nacheinander in den Speicher zu streamen und wieder aus dem Speicher zu entfernen, sodass ich immer nur ein einziges Bild im Speicher haben muss. Würde das nicht bedeuten, dass ich zu jedem Zeitpunkt nur ein paar KB anstelle von 45 + MB pro Zeichen verbrauchen würde?

Ich stelle mir vor, dies würde meine Leistung beeinträchtigen, da das Streamen unglaublich schnell sein müsste (15 Bilder pro Sekunde in den Speicher und aus dem Speicher) und obwohl die Bilder sehr klein wären, wäre es möglicherweise eine bessere Idee, Zeichenspritesheets zu laden stattdessen in den Speicher. Aber ich muss trotzdem ein Stream-ähnliches Rendering-System für ein einzelnes Bild für meinen größeren Boss-Charakter programmieren.

Ich habe experimentiert, aber es ist kein einfacher Prozess. Vor allem, weil ich an anderen Teilen der Spiel-Engine arbeite, die sich momentan nicht mit Grafik befassen.

Carter81
quelle
1. Sie haben keine RAM- oder HDD-Einschränkungen angegeben. Wie viele Zeichen müssen im Schnellzugriff sein? 2. Der Text enthält mehrere Fragen. Vielleicht können Sie sie fett fokussieren oder die Fragen sogar in Teile aufteilen?
Kromster sagt Unterstützung Monica
Oh es tut mir leid. Nicht viele. Ich würde mir vorstellen, dass die maximale Anzahl der einzelnen Zeichen auf dem Bildschirm zu einem Zeitpunkt etwa 40 betragen würde. Wenn die Leute den Versuch unternahmen, ihre Clients zum Absturz zu bringen, dann ist ... 130 das absolute Maximum. Typischerweise müsste es nur 10 für das typische Maximum geben, und das absolute Maximum wäre nicht mehr als <40. Alles über 40 wäre eine extreme, extreme Seltenheit, bei der Benutzer absichtlich versuchen, Zeichen nur als Screenshot oder zum Spaß am Eingeben von Zeichen einzugeben. Alles über 10 ist selten und alles über 40 ist extrem, extrem selten.
Carter81
Das Spiel ist ein reines PC-2D-RPG (kein mobiles), ich möchte jedoch eine mobile Plattform nur dann ausschließen, wenn dies einfach nicht möglich ist. Ich stelle mir vor, dass der verfügbare RAM-Speicher auf den RAM-Speicher des PCs und den VRAM des Benutzers beschränkt ist. Ich bezweifle sehr, dass die Festplatte sehr groß sein wird. Es ist nur so, dass der Festplattenverbrauch immer umso besser ist, je kleiner er ist.
Carter81
Wie viele EINZIGARTIGE Zeichen müssen auf dem Bildschirm angezeigt werden?
Kromster sagt Unterstützung Monica
Nicht mehr als 20. Alles darüber wäre so gut wie unmöglich, wenn sie nicht schummeln würden. Normalerweise 5-10.
Carter81

Antworten:

16

Wir haben einen ähnlichen Fall mit unserem RTS Remake. Alle Einheiten und Häuser sind Sprites. Wir haben 18.000 Sprites für Einheiten und Häuser und Gelände sowie weitere ca. 6.000 für Teamfarben (angewendet als Masken). Lang gestreckt haben wir auch ungefähr 30 000 Zeichen, die in Schriften verwendet werden.

Der Hauptgrund für Atlanten sind also:

  • Weniger verschwendeter Arbeitsspeicher (in früheren Zeiten, als Sie NPOT auf die GPU hochgeladen haben, hat es sich auf POT ausgedehnt / aufgefüllt. Ich habe gelesen, dass es mit iOS und einigen Frameworks immer noch dasselbe ist. Sie sollten besser überprüfen, auf welche Hardware Sie abzielen.)
  • weniger Texturwechsel
  • schnelleres Laden von allem in weniger größeren Stücken

Was hat bei uns nicht geklappt:

  • Palettierte Texturen. Das Feature gab es nur in OpenGL 1.x 2.x und wurde auch dann meist von GPU-Herstellern verworfen. Wenn Sie jedoch auf OpenGL + -Shader abzielen, können Sie dies in Shader-Code selbst tun.
  • NPOT-Texturen, wir hatten Probleme mit falschen Rändern und verschwommenen Sprites, was in der Pixelkunst nicht akzeptabel ist. Die RAM-Auslastung war ebenfalls viel höher.

Jetzt haben wir alles in Dutzenden von 1024x1024-Atlanten gepackt (moderne GPUs unterstützen sogar noch größere Dimensionen) und das funktioniert nur gut, wenn man nur ~ 300 MB Arbeitsspeicher isst, was für ein PC-Spiel ganz gut ist. Einige Optimierungen hatten wir:

  • Hinzufügen einer Benutzeroption zur Verwendung von RGB5_A1 anstelle von RGBA8 (Schachbrettschatten)
  • Vermeiden Sie nach Möglichkeit 8-Bit-Alpha und verwenden Sie das RGB5_A1-Format
  • Packen Sie Sprites eng in Atlanten (siehe Algorithmen zum Packen von Behältern).
  • Alles in einem Stück von der Festplatte speichern und laden (Ressourcendateien sollten offline generiert werden)
  • Sie können auch Hardware-Komprimierungsformate (DXT, S3TC usw.) ausprobieren.

Wenn Sie ernsthaft in Betracht ziehen, auf mobile Geräte zu wechseln, müssen Sie sich um Einschränkungen kümmern. Lassen Sie das Spiel erst einmal laufen und ziehen Sie die Spieler an! ;)

Kromster sagt Unterstützung Monica
quelle
Dies war mit Abstand die beste Lösung! Meine Sprites sehen in RGB5_A1 oder RGBA4444 nicht einmal anders aus, aber es spart Speicherplatz. Ihr Vorschlag im Chat, alle meine Assets in RAM und VRAM vorzuladen, ist perfekt. Darüber hinaus haben Sie empfohlen, optionale Grafikebenen zu verwenden, z. B. einen High Definition-Client für Benutzer mit RAM oder die Option, die Framerate um die Hälfte zu reduzieren.
Carter81
5

Ich habe eine tangential ähnliche Antwort in hier , aber die allgemeine Idee ist , dass, wenn Sie sich Laden und Zeichnen Texturen zu unterschiedlichen Zeiten (Sie nicht zusätzliche Texturen , während Sie Rendering - Laden), dann gibt es zwei Orte , wo , was Sie wird Ihre Leistung beeinträchtigen:

Ladezeit:

Dies ist der Moment, in dem Sie Ihre Texturen in den Speicher hochladen. Das ganze Datenmenge, die Sie an VRAM senden, bestimmt hauptsächlich, wie lange Ihre Ladezeit sein wird. Wenn Sie Ihre Texturen mit kleineren Formaten wie RGBA4444 versehen, wird dies schneller. Wenn Sie jedoch keine Texturen mit einer Größe von Hunderten von Megabyte in den VRAM hochladen, haben Sie hier wahrscheinlich keinen Engpass. Wenn Sie dies tun, kann ein netter Ladebildschirm das Warten erleichtern.

Das Zusammenfügen Ihrer Texturen zu Atlanten hat nur geringe Auswirkungen, da die gesamte Menge an Informationen, die Sie an VRAM senden, identisch ist. Wenn Sie Ihre Texturen atlasieren und Leerstellen in Ihren Atlanten belassen müssen, senden Sie tatsächlich mehr Daten in den VRAM, und daher ist dieser Teil langsamer!

Rendering-Leistung:

Sobald sich alle Ihre Texturen im VRAM befinden, hat die Anzahl der vorhandenen Texturen keinen Einfluss auf die Renderleistung. Es gibt vier Elemente, die sich auf die Renderleistung auswirken:

  1. Statusänderungen rendern : Jedes Mal, wenn Sie das Bild ändern, von dem Sie rendern möchten, wird die zum Rendern erforderliche Zeit erheblich verlängert. Im Allgemeinen möchten Sie die Anzahl der Statusänderungen minimieren, und Sie können die Anzahl der Statusänderungen reduzieren, indem Sie mehrere Bilder, die Sie nacheinander zeichnen, zu einem Texturatlas zusammenfassen.

    Nur atlasing ist nicht genug. Sie müssen so atlasieren, dass Zustandsänderungen reduziert werden, um Leistungszuwächse zu erzielen. Zum Beispiel könnte man meinen, dass ein Hauptcharakter in einem Sprite-Arbeitsblatt zu einem Leistungsgewinn führt. Wenn Sie jedoch nur ein Sprite von diesem Sprite-Arbeitsblatt pro Frame zeichnen, erhalten Sie im Vergleich zu diesem keine Leistungsgewinne jedes Sprite in einer separaten Datei.

    Richtiges Atlasing ist nicht trivial, aber im Allgemeinen können Sie Sprites aus derselben Ebene sicher gruppieren. Zum Beispiel ist es eine vielversprechende Idee, alle GUI-Elemente in einem Sprite-Blatt zu haben, während das alphabetische Gruppieren von Monstern möglicherweise nicht der Fall ist.

  2. Draw Calls: Im Allgemeinen möchten Sie Ihre Draw Calls auf ein Minimum beschränken. Als Faustregel gilt, dass Sie zwei Draw-Aufrufe zu einem Draw-Aufruf zusammenfassen können, wenn sich der Render-Status zwischen zwei Draw-Aufrufen nicht ändert. Für erweiterte Leistungssteigerungen können Sie beispielsweise 8 Textur-Sampler verwenden und Zeichnungsaufrufe für jeweils 8 Texturen gruppieren, sodass Sie die Texturen nur alle 8 Texturen ändern müssen.

  3. Dreieckszahl: Je mehr Dreiecke Sie zeichnen, desto länger dauert das Zeichnen. In modernen Computern und für die meisten 2D-Spiele sind Sie jedoch weit davon entfernt, dies zu maximieren. Sie können sicher Hunderttausende von Sprites pro Frame zeichnen und trotzdem unglaublich gute Frameraten erzielen. Sie werden wahrscheinlich mehr an die CPU gebunden sein, wenn Sie extrem viele Sprites zeichnen, bevor Sie Probleme mit Ihrer GPU bekommen.

  4. API-Einstellungen: Wenn Sie alles richtig machen und immer noch seltsam niedrige Frameraten erzielen, überprüfen Sie die Einstellungen, mit denen Sie Ihre Sprites zeichnen. Ich kenne SFML nicht, aber zum Beispiel in Direct3D 9 kann das Erstellen eines Vertex-Puffers mit D3DUSAGE_DYNAMICoder in D3DPOOL_MANAGEDIhre Renderzeiten leicht verzehnfachen. Wenn Sie vSync verwenden, wird Ihre Framerate natürlich auf die Aktualisierungsrate Ihres Monitors begrenzt. Die Verwendung nicht ausgerichteter FVFs kann bei einigen GPUs die Leistung beeinträchtigen. Auch dies gilt für Direct3D 9.

    Überprüfen Sie in Ihrem Fall die Dokumentation der von Ihnen verwendeten API.

Wenn Sie nur eine geringe bis mäßige Menge an Texturen haben (weniger als 1 GB) und nur eine geringe Menge an Sprites zeichnen (weniger als eine Million pro Frame), ist das erste, worauf ich achten würde, das Ändern der API-Einstellungen und Reduzieren Sie dann die Anzahl der Renderzustände und zeichnen Sie Aufrufe.

Panda-Pyjama
quelle
Wenn mir die Ladezeiten egal sind, gehe ich dann davon aus, dass ich alles vorher in den Speicher laden sollte, es sei denn, mir geht der Arbeitsspeicher oder der VRAM aus?
Carter81
Ich kümmere mich nur um alles, WEIL ich Angst habe, dass mir der RAM / VRAM ausgeht. Ich weiß nicht warum, aber es versteinert mich, dass Benutzer, die mein Spiel spielen, abstürzen, wenn sie versuchen, in einen Bereich mit zu vielen einzigartigen Sprites zu laden, oder wenn zu viele Charaktere auf ihren Bildschirm treten. Wenn ich mich nicht irre und jedes einzelne Sprite 96 KB verbraucht, hat jedes einzelne Zeichen 15 Animationen, 5 Richtungen und durchschnittlich 24 Bilder pro Animation - jedes einzelne Zeichen, das vollständig geladen ist, ist 173 MB groß. Es können 10, vielleicht sogar mehr, eindeutige Zeichen gleichzeitig auf dem Bildschirm sein.
Carter81
@KromStern: Wenn Sie atlasieren und Leerzeichen (Auffüllen) lassen müssen, werden die Daten größer und daher die Ladezeiten länger. Es ist klar, dass die Ursache für längere Ladezeiten der leere Raum ist und dass die Gesamtladezeit mit der Gesamtdatenmenge und nicht mit der Anzahl der Texturen zusammenhängt. Ich sehe dort nichts Irreführendes, und ich denke, dass eine Person, die über ausreichende Kenntnisse verfügt, um die ursprüngliche Frage zu verstehen, in der Lage sein wird, sich den Punkten anzuschließen und ihre eigenen Schlussfolgerungen für alle Fälle zu ziehen, in denen die Texturen und Atlanten PoT und nPoT sind.
Panda Pyjama
1
@ Carter81: Sie müssen Ihre Zielhardwarekonfiguration wie "i5, 1 GB RAM, NVidia GT260, 400 MB Festplatte" auswählen und daraus arbeiten. Es wird immer PCs geben, die schwächer sind und weniger RAM haben.
Kromster sagt Unterstützung Monica
Offensichtlich benötigen sie nicht alle 15 Animationen zu einem bestimmten Zeitpunkt. Was ist jedoch, wenn alle 10 einzigartigen Charaktere gleichzeitig in den "Kampfmodus" wechseln und daher 5 Animationssätze (Gehen, Laufen, Leerlauf, Nichtkampf usw.) mit 5 weiteren ausgetauscht werden müssen (Kampflauf, Kampf untätig, Kampf usw.)? Ich habe Angst, Texturen zu tauschen, weil es beim Versuch, dies mit SFML zu tun, zu einem merklichen Einfrieren oder einer Pause des Clients beim Wechseln von Texturatlanten kam. Ich weiß nicht, was mein Engpass bei den verschiedenen Strategien wäre, mit so vielen Sprites umzugehen.
Carter81