Artikel

The Elder's Scrollers

Making-of von „Logon’s run - 3D meets the aging bits“ (erschienen auf der Revision 2017) von Overflow / Logon System

Artikel geschrieben und gestaltet von Overflow / Logon System im Februar 2017 — ​veröffentlicht von NoRecess im April 2017. HIER klicken für die Demo auf Pouet.

Einführung

Schummeln war immer schon Teil des Demomaking. Flüssige Animation? Denken Sie an den Hardware-Scroller: Alles kann im VRAM vorgerendert sein, und die Anzeige auf dem Bildschirm wird dann bei jedem Bild geschickt gesetzt, indem der Zeiger auf das VRAM geändert wird. Ich glaube, das hat mich von Anfang an besessen gemacht! Über die Jahre habe ich HW-Scroller auf verschiedene Weisen eingesetzt, um Animationen zu bekommen, die immer besser wurden. Bis hin zur heutigen Veröffentlichung von „Logon’s run - 3D meets the aging bits“. Als Bonus oder Making-of: hier die ganze Geschichte. Viel zu sehen, ein paar DSKs zum Herunterladen, aber nicht so viel zu lesen.

64 KB vorgerendertes VRAM = 4096 Zeichen = 60 (Breite) * 68 (Höhe) + 16 | Anzeige auf dem Bildschirm = 48*34 scrollende Fenster

gagacubiz_1

„Gagacubiz“ [2014 | Veröffentlichung 2017 in „Logon’s run - 3D meets the aging bits“] Pouet!

1991-1992 DIE FRÜHEN JAHRE

Zurück ins Jahr 1991: 12 Zeichen im Rand außerhalb des angezeigten Bildschirms verstecken.

s koh

„S&KOH“ [1991] Pouet!

Animation in 4 Schritten | 4 Zeichen nach rechts scrollen

s koh fire

Flüssiger Scroller | 4 Umschaltungen + 1 Zeichen nach rechts scrollen

s koh logon

Dann kam eine Verbesserung: eine Zeichenzeile ohne Längenbeschränkung — um auf einer einzigen Zeichenzeile viel mehr zu verstecken. Normalerweise sollten auf einer einzelnen Rasterzeile nicht mehr als 64 Zeichen stehen. Indem man den Bildschirm auf jeder Zeichenzeile splittet, fällt die Beschränkung weg.

sotb

„Shadow of the Beast — Demo-Menü“ [1992 | Vorschau 1994] Pouet!

Flüssiger Scroller | N Zeichen nach rechts scrollen + 1 Pixel Verschiebung

sotb

Wieder geschummelt: Bildschirmdaten langsam in BASIC rendern, dann 16 KB VRAM speichern; später laden und einen HW-Scroll ausführen.

16 KB vorgerendertes VRAM = 900 Zeichen | N Zeichen nach rechts scrollen — Kachelbreite = ~6 Z.

waves

„Waves“ [1992 | Vorschau 1994]

2002-2004 DEULIGNES

„Deulignes“ bedeutet „2 Zeilen BASIC“. PhenixInformatique hat 2002, 2004 und 2008 mehrere freundschaftliche Wettbewerbe zu „Deulignes“ organisiert. Mein Weg war: der größte Teil des Quellcodes rendert den Bildschirm (vermutlich 15–30 Min. Rechenzeit! denn BASIC ist so langsam), dann ein HW-Scroller, der die Animation in einer Schleife laufen lässt. Später hat CPCrulez sie neu gepackt: 16 KB VRAM auf einmal geladen. Download bei CPC-power.

4 KB vorgerendertes VRAM | 2 Zeichen nach rechts scrollen

worms

„Worms“ [2002 | Repack 2010 von cpcrulez]

4 KB vorgerendertes VRAM | 4 Zeichen nach rechts scrollen

plasmegg

„Plasmegg“ [2002 | Repack 2010 von cpcrulez]

16 KB vorgerendertes VRAM = 1024 Zeichen = 40 (Breite) * 25 (Höhe) + 24 | 2 Zeichen nach oben scrollen

barfive

„Barfive“ [2004 | Repack 2010 von cpcrulez]

​2004-2011 DAS INNENLEBEN

Endlich eine allgemeine Animation mit quadratischen Kacheln!

16 KB vorgerendertes VRAM = 1024 Zeichen = 64 * Kacheln | quadratische Kachel = 4 (Breite) * 4 (Höhe)

tiles

„0%-CPU-Animation“ [2004 unveröffentlicht — Vorschau + Grafik 2010 von cpcrulez] Download bei CPC-power.

Von 16 KB auf 64 KB umzuschwenken (um die Zahl der Animationsbilder zu vervierfachen) wäre leicht gewesen. Aber … schauen Sie sich diesen Bildschirm an: statische quadratische Kacheln? Nur mit ganzen Zahlen? Das ließ mich fragen, wie sich das verbessern lässt. Ich habe ein paar Tage darüber nachgedacht, bis ich auf ein paar Prinzipien stieß, die mich umtrieben: eine praktisch unbegrenzte Zahl von Ebenen? Nicht ganzzahlig große Kacheln mit Sub-Pixel-Scrolling??? Ich musste die Idee ausprobieren! Das war 2006.

64 KB vorgerendertes VRAM | 6 Ebenen | nur Punkte setzen und Linien zeichnen

[2006 unveröffentlicht — heimliche Vorschau auf der Croco-Chanel-Party 2007]

2006.dskDSK · 170 KB

Berechnungen & Zahlen

tess xlsm

Ohne jeden Kommentar bereitgestellt (versuchen Sie selbst herauszufinden, was da vor sich geht!):

tess.xlsmXLSM · 105 KB

Dieser Machbarkeitsnachweis war schön, aber definitiv nicht reif für eine Veröffentlichung: Die Grundidee war, in jedem Bild aus einer Animationsebene einen Punkt zu setzen (und später eine Linie zu zeichnen). Das war so langsam … Die Verbesserung, an die ich dachte, war: eine gefüllte Form auf einmal zeichnen. Also habe ich das programmiert.

64 KB vorgerendertes VRAM | 4 Ebenen | eine gefüllte Form zeichnen

[2008 unveröffentlicht]

2008.dskDSK · 170 KB

Wieder eine ziemliche Enttäuschung! Es schien, dass 3D der einzige Weg war, ein schönes Gefühl von Ebenen zu bekommen. Es schien außerdem, dass Dithering eine weitere Voraussetzung war, um die 4-Farben-Beschränkung zu umschummeln. Verflixt! Hieß das, ich brauchte meine eigene 3D-Engine? Definitiv eine große Sache! Denn ich sah mich eher als Hardware-Coder denn als Software-Coder. Nun, es hat mich ein paar Jahre gekostet, das Ziel zu erreichen: 3D-Engine fertig! Bereit, sich mit HW-Scroll zu mischen … ein paar Jahre später.

Hommage an Face Hugger [2011 unveröffentlicht]

2011.dskDSK · 170 KB

​2014-2016 DIE VERBORGENE ARBEIT AN „Logon’s run“

Tatsächlich hatte ich die 3 Haupteffekte ziemlich schnell beisammen: ein paar Monate im Jahr 2014. Das hieß: 3D-Objekte zeichnen und sie als Sprite irgendwo in den 64 KB VRAM legen können. Kleine Objekte ließen sich sogar in einem einzigen Bild auf dem Bildschirm erzeugen — was erlaubt, sie während des Scrollens erscheinen zu lassen.

doyousea

„Doyousea“ [2014 | Veröffentlichung 2017 in „Logon’s run - 3D meets the aging bits“]

dragonfly

„Dragonfly“ [2014 | Veröffentlichung 2017 in „Logon’s run - 3D meets the aging bits“]

Beeindruckende Effekte auf dem Bildschirm. Aber … es dauert ewig, so viele 3D-Objekte auf mehreren Ebenen in 64 KB VRAM zu zeichnen! Die Grundidee war damals: Nun gut, machen wir eine Art prozedurale Grafik, zeigen wir nur teilweise, was auf den Bildschirm kommt.

Frühe Vorschau: direkt auf dem Bildschirm gerendert. Schande!

Arbeitstitel „Crafted“ [2014 | Vorschau auf der Reset-Party 2015]

2014.dskDSK · 170 KB

Also habe ich beschlossen, die entstehende Grafik zu verstecken. Das habe ich auf 3 Wegen getan. Zuallererst: Wo möglich, Bitplanes verstecken, um nur einen einfarbigen Hintergrund oder Effekt zu zeigen. Dann? Zurück zu viel einfacheren Animationen auf 16 KB! 3D vergessen, nur Sprites benutzen; das lässt sich schnell erzeugen und verschafft mehr Zeit für die schweren Effekte. Und nicht zuletzt: eine Hintergrundaufgabe benutzen, um die Animation zu erzeugen, während im Vordergrund etwas anderes gezeigt wird (etwa prozedurale Grafik eines Logos).

VERSTECKTE „BITPLANE“

Mode 1 = 4 Farben = 2 Bits nötig. Der CPC hat keine Bitplanes. Allerdings: Ein geschickter Einsatz der „Inks“ erlaubt es, 1 von 2 Bits zu verstecken. So lässt sich einfarbige Grafik rendern, während über das andere Bit eine andere Grafik auf dem Bildschirm zu sehen ist.

Hinweis: Die nächsten Bildschirmfotos (die links) stammen aus diesem DSK:

2017.dskDSK · 190 KB

Mittelgroße Würfel werden gerendert … aber nicht gesehen

tess bitplane1

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess bitplane1 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

XL-große Würfel werden gerendert … aber nicht gesehen

tess bitplane2

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess bitplane2 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

Der Hintergrund von „Doyousea“ (grau — unten) wird gerendert … aber nicht gesehen

tess bitplane3

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess bitplane3 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

16-KB-ANIMATION

16 KB VRAM | Effekt nur mit Sprites | während des Renderns von „Dragonfly“ 1/2

lite1

16 KB VRAM | Effekt nur mit Sprites | während des Renderns von „Dragonfly“ 2/2

lite2

Derselbe Hintergrund für 3 Animationen | 1) Kachelhöhe = 2 Zeichen

deulignes4

Derselbe Hintergrund für 3 Animationen | 2) Kachelhöhe = 4 Zeichen — 2*4 Zeichen scrollen

deulignes3

Derselbe Hintergrund für 3 Animationen | 3) Kachelhöhe = 6 Zeichen

deulignes2

HINTERGRUNDAUFGABE

„Irgendetwas“ anzeigen, während Daten (hauptsächlich 64 KB/16 KB VRAM) erzeugt werden.

„Doyousea“ wird als Hintergrundaufgabe gerendert, während im Vordergrund prozedurale Grafik des „Logon System“-Logos zu sehen ist

tess background1

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess background1 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

„Dragonfly“ wird als Hintergrundaufgabe gerendert, während im Vordergrund Geister-Sprites zu sehen sind

tess background2

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess background2 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

„Gears“ wird als Hintergrundaufgabe gerendert, während im Vordergrund „Dragonfly“ und dann die „Donkey Kong“-Plattformen zu sehen sind

tess background3a

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess background3a ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

„Rubberbar“ wird als Hintergrundaufgabe gerendert, während im Vordergrund „Mario“ zu sehen ist

tess background3b

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess background3b ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

64 KB „Gameboy“-Daten werden als Hintergrundaufgabe erzeugt, während im Vordergrund „Rubberbar“ zu sehen ist

tess background4

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess background4 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

BONUS: SPLITRASTER!

Den Zeiger auf das VRAM zu ändern kostet definitiv nicht viel CPU! Also? Genug Zeit, um ein paar „Splitraster“ zu ergänzen (also eine einzelne „Ink“ bis zu 12 Mal auf einer einzigen Rasterzeile zu ändern).

Der leichte Weg: ein großes klobiges Sprite — oder später etwas Text

tess rasters0a

tess rasters0b

Der ungewöhnliche Weg: Würfel unten verstecken und sie von oben herunterfallen lassen

tess rasters1_1

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess rasters1 ok_1

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

Der harte Weg: viele Farben auf eine einfarbige 3D-Form auftragen

tess rasters2

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess rasters2 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

… ABER WAS IST MIT DEM GAMEBOY LOS?

Die Gameboy-Szene ist eine andere Geschichte. Sie kommt auch nicht per Hardware-Scroll zustande und passt hier nicht als Elder’s Scroller. Allerdings sieht dieser Artikel doch nach einem Making-of aus, oder? Vielleicht der richtige Ort für mehr Text und weniger animierte GIFs.

Batman hat mich umgehauen. Die Gameboy-Szene hat ihre Anregung von jenem gnadenlosen Batlogo. Sie benutzt denselben Grundalgorithmus. Klar: Die Animation ist gepackt. Für jedes gepackte Bild wird gespeichert: von Rasterzeile #n zu Rasterzeile #(n+1), welche Bytes geändert werden müssen. Anders gesagt: Delta-Packing unter der Annahme, dass der Bildschirm nur eine einzige wiederholte Rasterzeile benutzt, die dem Strahl folgend aktualisiert wird. Darauf aufbauend führt die Gameboy-Szene ein paar weitere Kniffe ein.

Zwei Rasterzeilen werden wiederholt. Es kann zu Glitches kommen, wenn auf einer einzigen Rasterzeile zu viele Bytes zu aktualisieren sind. Wird ein Byte zu spät aktualisiert, also nachdem der Strahl es passiert hat, wird es in seinem vorherigen Zustand angezeigt. Um das zu vermeiden: nicht eine einzelne Rasterzeile benutzen, sondern zwei. Die gerade Rasterzeile wird aktualisiert, während die ungerade angezeigt wird, und umgekehrt.

gb

Links: Zwei wiederholte Rasterzeilen werden während des Bildes jeweils um sehr wenige Bytes rechts aktualisiert

Dithering. Da zwei Rasterzeilen benutzt werden, war es leicht, etwas Dithering zu ergänzen.

Anzahl der aktualisierten Bytes je Rasterzeile. In dieser Gameboy-Animation werden auf einer einzigen Rasterzeile bis zu 14 Bytes aktualisiert. Die nötige CPU-Zeit beträgt 14*6 = 84 μs. Das ist mehr als 64 μs = die Breite der Rasterzeile, wie der Strahl sie sieht. Mit einem Einzelzeilen-Verfahren würde das zu Glitches führen (siehe Punkt a). Für das Doppelzeilen-Verfahren spielt es keine Rolle. Für eine bestimmte Rasterzeile sind 20 μs mehr nötig? Kein Problem: Die nächsten Rasterzeilen mit weniger Bytes gleichen diese Verzögerung aus.

Hintergrund. Im Hintergrund werden einige Quadrate angezeigt. Das gelingt über Raster (also Farbwechsel), die auf (3) Spalten angewandt werden.

Anzahl der Farben. Es werden 7 statt 4 angezeigt. Zum Farbwechsel im Hintergrund siehe den vorigen Punkt. Es gibt außerdem einen weiteren Farbwechsel (nicht immer bei demselben y) auf dem Gameboy selbst: Bildschirm und Tasten teilen sich dieselbe Ink.

Quadrate als Hintergrund und mehr Farben dank Ink-Wechsel im Takt des Strahls.

tess rasters3

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess rasters3 ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]

Nach unten auf der y-Achse scrollen. Das erfordert, dass die Entpackroutine angehalten wird, bevor die Daten für das aktuelle Bild enden.

Nach oben auf der y-Achse scrollen. Das erfordert eine Vor-Entpackroutine, die vor der Hauptschleife läuft, die dem Strahl folgt.

Kein leerer Bildschirm davor/danach. Der Effekt wechselt von einem bzw. zu einem Standard-Bildschirmverfahren, in dem der Gameboy mit denselben Daten wie der Effekt vorgerendert wurde.

Kein Nachladen von Daten. Aber: 25 Sekunden Hintergrundaufgabe, um 64 KB Daten zu rendern und zu packen.

Hintergrund/Vordergrund. Die Quadratspalten scheinen vor dem rotierenden Objekt zu liegen. Das gelingt, indem die zuvor erzeugten Daten verändert werden.

SCHLUSSSZENE

Eine einfache Szene, die nicht viel Programmierzeit gekostet hat.

Bei jedem Bild werden nur die ganz wenigen Bytes geändert, die sich geändert haben: oben an den Herzen löschen, unten Pixel setzen.

Dann wird eine umgekehrte Maskentabelle benutzt: Beim Setzen der Pixel der Herzen haben die Hintergrundpixel Vorrang.

Für jedes scrollende Herz: 7 Bytes löschen, 7 neue rote Bytes setzen.

tess 8bit

[„Logon’s run - 3D meets the aging bits“ — spezieller „hidden work“-Build]

tess 8bit ok

[„Logon’s run - 3D meets the aging bits“ — Release-Build]