2010-06-05 9 views
20

Ich versuche, ein Oldschool-NES-Stil-Videospiel zu entwickeln, mit Sprite Flackern und grafischen Verlangsamung. Ich habe darüber nachgedacht, welche Art von Logik ich verwenden sollte, um solche Effekte zu ermöglichen.Emulation Old-School-Sprite Flackern (Theorie und Konzept)

Ich habe folgende Einschränkungen zu beachten, wenn ich die alte Schule NES Stil gehen will:

  • nicht mehr als 64 Sprites auf dem Bildschirm zu einer Zeit
  • nicht mehr als 8 Sprites pro Scanline, oder für jede Zeile auf der Y-Achse
  • Wenn es zu viel Action auf dem Bildschirm geht, friert das System das Bild für einen Rahmen der Prozessor mit dem
Aktion aufholen zu lassen

Von dem, was ich gelesen habe, wenn es mehr als 64 Sprites auf dem Bildschirm gab, würde der Entwickler nur Sprites mit hoher Priorität zeichnen, während er niedrige Priorität ignoriert. Sie könnten sich auch abwechseln, indem sie jedes gerade numerierte Sprite auf gegenüberliegenden Bildern von ungeradzahligen Bildern zeichnen.

Das Scanline-Problem ist interessant. Nach meinen Tests ist es unmöglich, eine gute Geschwindigkeit auf dem XBOX 360 XNA-Framework zu erreichen, indem Pixel Pixel für Pixel gezeichnet werden, wie es beim NES der Fall war. Dies ist der Grund, warum in Spielen der alten Schule, wenn es zu viele Sprites in einer einzigen Zeile gäbe, einige erscheinen würden, wenn sie halbiert würden. Für alle Zwecke für dieses Projekt mache ich Scanlinien mit einer Höhe von 8 Pixel und gruppiere die Sprites pro Scanlinie nach ihrer Y-Positionierung.

Um zu klären, würde ich Sprites auf den Bildschirm in Chargen von 8x8 Pixel, nicht 1x1 zeichnen.

Also, verdummt Ich muss mit einer Lösung kommen, dass ....

  • 64 Sprites auf dem Bildschirm auf einmal
  • 8 Sprites pro 'Scanline'
  • Kann Sprites zeichnen basierend auf Priorität
  • Kann zwischen Sprites pro Rahmen
  • Emulate Verlangsamung
wechseln

Hier ist meine aktuelle Theorie

erster Linie eine grundlegende Idee kam ich mit Sprite Priorität anspricht. Unter der Annahme, Werte zwischen 0-255 (0 niedrig ist), kann ich Sprites Prioritätsstufen, zum Beispiel zu:

  • 0 zu 63 niedrig ist
  • 63-127 ist mittel
  • 128-191 hoch ist
  • 192 bis 255 werden maximal

Innerhalb meiner Daten-Dateien, kann ich jeden Sprite zuweisen eine gewisse Priorität. Wenn das übergeordnete Objekt erstellt wird, wird dem Sprite zufällig eine Zahl zwischen dem angegebenen Bereich zugewiesen. Ich würde dann Sprites in der Reihenfolge von hoch nach niedrig zeichnen, mit dem Endziel, jedes Sprite zu zeichnen.

Jetzt, wenn ein Sprite in einem Rahmen gezeichnet wird, würde ich dann zufällig einen neuen Prioritätswert innerhalb seiner anfänglichen Prioritätsstufe erzeugen. Jedoch, wenn ein Sprite nicht in einem Rahmen gezeichnet wird, könnte ich 32 zu seiner aktuellen Priorität hinzufügen. Wenn das System beispielsweise Sprites nur bis zu einer Prioritätsebene von 135 zeichnen kann, könnte ein Sprite mit einer anfänglichen Priorität von 45 gezeichnet werden, nachdem 3 Frames nicht gezeichnet wurden (45 + 32 + 32 + 32 = 141)

Dies würde es Sprites theoretisch ermöglichen, Frames abwechselnd zu verwenden, Prioritätsstufen zuzulassen und Sprites auf 64 pro Bildschirm zu begrenzen.

Die interessante Frage ist nun, wie kann ich Sprites auf nur 8 pro Scanline beschränken?

Ich denke, wenn ich die Sprites mit hoher Priorität zu niedriger Priorität sortiere, durchlaufen Sie die Schleife, bis ich 64 Sprites gezogen habe. Allerdings sollte ich nicht nur die ersten 64 Sprites in die Liste aufnehmen.

Bevor ich jedes Sprite zeichnete, konnte ich überprüfen, wie viele Sprites in seiner jeweiligen Scanlinie über Zählervariablen gezeichnet wurden. Zum Beispiel:

  • Y-Wert zwischen 0 und 7 gehört, 0 bis Abtastzeile, scanlineCount [0] = 0
  • Y-Wert zwischen 8 und 15 gehört 1, scanlineCount [1] = 0
  • bis Abtastlinie
  • usw.

Ich könnte die Werte pro Scanline für jeden Frame zurückgesetzt werden. Wenn Sie in der Sprite-Liste nach unten gehen, fügen Sie 1 zum entsprechenden Zähler der Scanlinie hinzu, wenn ein Sprite in dieser Scanlinie gezeichnet wird. Wenn es gleich 8 ist, zeichne nicht dieses Sprite und gehe zum Sprite mit der nächstniedrigeren Priorität.

ABSCHWÄCHUNG

Das letzte, was ich tun muss, ist Verlangsamung zu emulieren. Meine ursprüngliche Idee war, dass wenn ich 64 Sprites pro Frame zeichne und es noch mehr Sprites gibt, die gezeichnet werden müssen, könnte ich das Rendering um 16ms oder so pausieren. In den NES-Spielen, die ich gespielt habe, gibt es jedoch manchmal eine Verlangsamung, wenn kein Sprite-Flimmern auftritt, während sich das Spiel schön bewegt, selbst wenn etwas Sprite flimmert.

Vielleicht geben Sie einen Wert für jedes Objekt, das Sprites auf dem Bildschirm verwendet (wie die obigen Prioritätswerte), und wenn die kombinierten Werte aller Objekte mit Sprites einen Schwellenwert überschreiten, führen Sie Verlangsamung ein?

SCHLUSS ...

Does ich alles schrieb klingen tatsächlich legitim und könnte funktionieren, oder ist es ein Wunschtraum? Welche Verbesserungen können Sie möglicherweise mit dieser Spielprogrammtheorie von mir denken?

+0

das ist alles sehr cool, und Sie haben meine +1 für gründlich .. aber gibt es eine * Frage * hier? –

+0

+1, weil du es geschafft hast, dass ich alles lese. –

+0

Unter "Fazit". :) Ich fragte, ob das legitim klang und ob es funktionieren könnte oder ob es Verbesserungen bei meiner Theorie geben könnte. Ich betrachte dies als ein Problem, da ich meine Ziele und meine aktuelle Theorie angegeben habe, um sie zu erhalten, und ich bitte um Rat, wie das geht: D –

Antwort

3

Erstens, ich liebe die Idee. Wenn man ein Retro-Spiel macht, wäre es nicht dasselbe, wenn man die Komplexitätsgrenzen ignoriert. Durch das Schreiben eines Rahmens, der erzwungengroße Idee ist.

Kann ich nur sagen, wenn Sie die Engine als Open Source "Retro Game" Grafik-Engine abstrahiert haben, wäre das in der Tat cool und ich wäre sicher, mitzumachen!

  • In Bezug auf Ihre höchste Priorität Sprites, vielleicht wäre ein Priority Queue diesen Teil, dass vereinfachen Sie die 64 höchste Priorität Sprites gerade herausziehen können.

  • Mit der Verlangsamung, vielleicht in der Maschine könnten Sie eine limitValue haben. Jedes Sprite als passendes limitUse. Addieren Sie alle limitUse vars und wenn dies unter limitValue ist, keine Verlangsamung. Nun, für slowDelay = f(limitUseTotal - limitValue), wobei f eine Funktion ist, die die überschüssige Menge an Sprites in einen berechneten Verlangsamungswert umwandelt. Ist das die allgemeine Idee, die du vorschlägst oder habe ich sie falsch verstanden?

+0

Ich vermute, Priority Queue wäre im Grunde das gleiche wie ich eine benutzerdefinierte Sprite-Klasse erstellen und mit einem benutzerdefinierten Sortierer sortieren? :) Und für die Verlangsamung, das ist die Idee direkt auf den Kopf. Ich persönlich würde mich jedoch wohler fühlen, wenn ich es mit einem bestimmten Wert, z. eine Verzögerung nicht länger als 16ms. Weil ich einen Mathefehler sehen konnte, der den Redraw-Zyklus um 100ms oder so verzögerte, haha. Vielen Dank! –

+0

Ja, was auch immer Sie für Ihre f (x) -Funktion entscheiden, könnte eine obere Grenze enthalten. –

3

Verwenden Sie für die Priorität nur den Z-Index. Das ist zumindest was der GBA tut; Priorität 0 wird an vorderster Stelle dargestellt und hat somit die schwächste Priorität. Also, render hoch zu niedrig, und wenn du zu viele Sprites gerendert hast, hör auf.

Die Priorität eines Sprites wird durch seinen Index in der Sprite-Tabelle bestimmt. Der GBA hatte 128 Einträge [0,127], die als aufeinanderfolgende vier Wörter (jeweils 16 Bytes) angeordnet waren. Für XNA würden Sie wahrscheinlich stattdessen eine List<Sprite> oder ähnliche verwenden. Vielleicht ein Sprite[256] um die Dinge ein wenig zu vereinfachen.

Die Begrenzung der Sprite-Rendering fällt natürlich von diesem Mechanismus. Da Sie von 255 bis 0 rendern, können Sie verfolgen, welche Scanlinien Sie berührt haben. Also im Grunde:

int[] numPixelsDrawnPerScanline = new int[Graphics.ScreenHeight]; 

foreach(Sprite sprite in SpriteTable.Reverse()) { 
    int top = Math.Max(0, sprite.Y); 
    int bottom = Math.Min(Graphics.ScreenHeight, sprite.Y + sprite.Height); 

    // Could be rewritten to store number of pixels to draw per line, starting from the left. 
    bool[] shouldDrawOnScanline = new bool[Graphics.ScreenHeight]; 

    for(int y = top; y < bottom; ++y) { 
     bool shouldDraw = numPixelsDrawnPerScanline[y] + sprite.Width <= PixelsPerScanlineThreshold; 

     shouldDrawOnScanline[y] = shouldDraw; 

     if(shouldDraw) { 
      numPixelsDrawnPerScanline[y] += sprite.Width; 
     } 
    } 

    Graphics.Draw(sprite, shouldDrawOnScanline); // shouldDrawOnScanline defines clipping 

    skipDrawingSprite: 
} 

Ich bin nicht ganz sicher, was Sie von der Abschwächung bedeuten, so kann ich nicht auf diesen Teil Ihrer Frage antworten. Es tut uns leid.

Hoffe, das hilft.

+0

Der GBA hat eine zusätzliche Falte in Bezug auf Sprites mit Hardware-Rotation/Skalierung. Sprites mit hw scaling/rotation sind teurer zu rendern, sodass die Hardware weniger davon rendern kann. z.B. Wenn du den Bildschirm füllst, indem du 4 64x64-Sprites ausdehnst (die doppelte Größe sollte 256x256 in der Theorie abdecken), ist es wahrscheinlich, dass der untere Teil des Bildschirms überhaupt keine Sprites enthält. – richq