Ein Game Boy Game machen

Ursprünglicher Autor: InvisibleUp
  • Übersetzung
Bild

Vor ein paar Wochen habe ich beschlossen, an einem Game Boy-Spiel zu arbeiten, dessen Erstellung mich sehr erfreut hat. Ihr Arbeitstitel lautet "Aqua and Ashes". Das Spiel hat Open Source und ist auf GitHub verfügbar .

Wie bin ich auf diese Idee gekommen?


Kürzlich bekam ich einen Job im PHP- und Python-Backend-Praktikum für die Website meiner Universität. Dies ist ein guter und interessanter Job, für den ich sehr dankbar bin. Aber ... gleichzeitig hat mich dieser hochrangige Webentwicklungscode mit einem unersättlichen Verlangen angesteckt. Und es war das Streben nach niedrigerer Arbeit mit Bits.

Ich habe eine wöchentliche Zusammenfassung von itch.io über Game Jam erhalten und den Beginn von Mini Jam 4 angekündigt . Es war ein 48-stündiger Jam (naja, eigentlich ein bisschen mehr), bei dem die Einschränkung die Erstellung von Grafiken im Game Boy-Stil war. Meine erste logische Antwort war der Wunsch, ein Game Boy-Homebrew-Spiel zu entwickeln. Das Thema der Marmelade war "Jahreszeiten" und "Flamme".

Nachdem ich ein wenig über die Handlung und die Mechanik nachgedacht habe, die in 48 Stunden implementiert werden kann und in die Grenzen des Themas passte, fand ich einen Klon einer neuen Interpretation des Levels für SNES 1993 Tiny Toon Adventures: Buster Busts Loose !, in dem der Spieler Buster spielt .


Ich habe immer gemocht, wie die Schöpfer dieses Levels eine unglaublich schwierige Sportart ausübten, alle Tricks, Positionen und strategischen Elemente loswurden, als Ergebnis eines äußerst interessanten und einfachen Spiels. Offensichtlich wird eine solche vereinfachte Sichtweise des American Football nicht Madden ersetzen, genau wie der NBA Jam (eine ähnliche Idee: Nur 4 Spieler in einem viel kleineren Feld mit einfacherem Gameplay als in einem regulären Spiel) werden die 2K-Serie nicht ersetzen. Diese Idee hat jedoch einen gewissen Charme, und die Verkaufszahlen von NBA Jam bestätigen dies.

Wie hängt das alles mit meiner Idee zusammen? Ich habe mich entschieden, dieses Fußball-Level zu nehmen und es so umzubauen, dass es dem Original ähnlich bleibt und gleichzeitig frisch ist. Zuerst habe ich das Spiel auf vier Spieler reduziert - einen Verteidiger und einen Angreifer. Dies ist hauptsächlich auf die Einschränkungen der Hardware zurückzuführen, aber gleichzeitig werde ich ein wenig mit einer intelligenteren KI experimentieren können, die nicht auf das Prinzip "nach links laufen und manchmal aufspringen" aus dem Spiel auf SNES beschränkt ist.

Um der Thematik Rechnung zu tragen, werde ich das Tor durch brennende Säulen oder Feuer oder ähnliches (ich habe mich noch nicht entschieden) und einen Fußball mit Fackeln und einen Eimer mit Wasser ersetzen. Der Gewinner wird das Team sein, das beide Feuer kontrolliert. Um dieses einfache Konzept herum können Sie leicht eine Handlung ausarbeiten. Die Jahreszeiten werden ebenfalls berücksichtigt: Ich entschied, dass sich die Jahreszeiten mit jedem Zug ändern würden, so dass das Feuerteam im Sommer Vorrang hat und das Feuerwehrteam im Winter. Dieser Vorteil sieht aus wie Hindernisse auf dem Feld, die nur die gegnerische Mannschaft behindern.

Wenn wir zwei Teams zusammenstellten, brauchten wir natürlich zwei Tiere, die Feuer lieben und nicht mögen. Zuerst dachte ich über Feuerameisen und einen Wasserkäfer, eine Gottesanbeterin und dergleichen nach, aber nachdem ich die Frage studiert hatte, fand ich keine Insekten, die im Winter aktiv waren, und ersetzte sie durch Polarfuchs und Geckos. Polarfüchse lieben Schnee, Geckos liegen gerne in der Sonne, daher erscheint alles logisch. Am Ende ist es nur ein Spiel für Game Boy.

Falls es noch nicht klar ist, war das Spiel am Ende des Staus noch nicht einmal kurz vor dem Abschluss. Trotzdem hat es trotzdem Spaß gemacht.

Game Boy Training


Zuerst müssen Sie die Anforderungen festlegen. Ich entschied mich für DMG zu schreiben (interner Name für das Game Boy-Modell, kurz für Dot Matrix Game). Hauptsächlich um den Anforderungen von Game Jam gerecht zu werden, aber auch, weil ich es wollte. Persönlich hatte ich noch nie Spiele für DMG (obwohl es mehrere Spiele für Game Boy Color gibt), aber 2-Bit-Ästhetik finde ich eine sehr schöne und interessante Einschränkung für Experimente. Vielleicht werde ich eine zusätzliche Farbe für SGB und CGB hinzufügen, aber bisher habe ich nicht darüber nachgedacht.

Ich entschied mich auch für eine 32K ROM + -Kassette ohne RAM, nur für den Fall, dass ich eine physische Kopie des Spiels erstellen möchte. CatSkull, der mehrere Game Boy-Spiele wie Sheep it Up! Veröffentlicht hat, hat sehr günstige 32-Kilobyte-Flash-Patronen zum Verkaufdas ist perfekt für mich. Dies ist eine weitere zusätzliche Einschränkung, aber ich glaube nicht, dass ich in naher Zukunft das Volumen von 32 K mit einem so einfachen Spiel überwinden kann. Am schwierigsten wird es mit Grafiken sein, und wenn alles wirklich schlecht ist, werde ich versuchen, es zu quetschen.

Was die Arbeit des Game Boy angeht, ist alles ziemlich schwierig. Um ehrlich zu sein, war der Game Boy von allen Retroconsoles, mit denen ich arbeiten musste, der schönste. Ich habe mit einem tollen Tutorial angefangen .(zumindest zum ersten Mal, weil es nie hinzugefügt wurde) vom Autor „AssemblyDigest“. Ich wusste, dass es am besten war, auf ASM zu schreiben, egal wie schmerzhaft es manchmal war, weil die Hardware nicht für C konzipiert war, und ich war mir nicht sicher, ob die in diesem Tutorial erwähnte coole Sprache Wiz auf lange Sicht gültig sein würde. Außerdem mache ich das hauptsächlich, weil ich mit ASM arbeiten kann. Erkundigen

Sie sich beim 8c0a4ea-Commit.

Als erstes müssen Sie den Game Boy zum Booten bringen. Wenn durch Versatz$104Das Nintendo-Logo wird nicht gefunden, und der Rest des Headers wird nicht korrekt konfiguriert. Die Game Boy-Ausrüstung geht davon aus, dass die Kassette falsch eingesetzt ist und nicht booten kann. Die Lösung dieses Problems ist sehr einfach, da bereits viele Tutorials darüber geschrieben wurden. So habe ich das Problem mit dem Titel gelöst. Es gibt nichts, das besondere Aufmerksamkeit verdient.

Es wird schwieriger, nach dem Laden sinnvolle Aktionen auszuführen. Es ist sehr einfach, das System in einen endlosen Zyklus von Beschäftigten zu bringen, in dem es immer wieder eine Codezeile ausführt. Die Ausführung des Codes beginnt mit einem Etikett main(wo die Passage zur Adresse angibt $100), daher sollte dort ein einfacher Code eingefügt werden. Zum Beispiel:

main:
.loop:
	halt
	jr .loop

und es tut nichts weiter, als auf den Beginn des Interrupts zu warten, woraufhin er zum Label zurückkehrt .loop. (Im Folgenden werde ich die ausführliche Beschreibung des ASM-Vorgangs weglassen. Wenn Sie verwirrt sind, lesen Sie die Assembler-Dokumentation, die ich verwende .) Möglicherweise sind Sie neugierig, warum ich nicht zurück zum Etikett gehe main. Dies geschieht, weil ich möchte, dass alles bis zur Markierung .loopdie Initialisierung des Programms ist, und alles, was danach passiert, bei jedem Frame. Daher muss ich nicht das Laden von Daten von der Kassette in einer Schleife umgehen und den Speicher in jedem Frame löschen.

Lassen Sie uns noch einen Schritt machen. Das von mir verwendete RGBDS-Assembler-Paket enthält einen Bildkonverter. Da ich zu diesem Zeitpunkt noch keine Ressourcen für das Spiel gezeichnet habe, entschied ich mich, den Monochrom-Button von meiner About-Seite als Testbit-Bild zu verwenden. Ich habe es mit RGBGFX in das Game Boy-Format konvertiert und mit dem Befehl .incbin assembler nach der Funktion eingefügt main.

Bild

Um es auf dem Bildschirm anzuzeigen, benötige ich Folgendes:

  1. Schalten Sie das LCD aus
  2. Palette einstellen
  3. Bildlaufposition einstellen
  4. Videospeicher löschen (VRAM)
  5. Laden Sie gekachelte Grafiken in VRAM herunter
  6. Laden Sie eine Kachelhintergrundkarte in VRAM hoch
  7. Schalten Sie das LCD-Display wieder ein

LCD aus


Für Anfänger wird dies zum ernstesten Hindernis. Beim ersten Game Boy ist es unmöglich, Daten einfach in VRAM zu schreiben. Es ist notwendig zu warten, bis das System nichts zeichnet. Nach dem Nachleuchten des Phosphorglühens in alten CRT-Fernsehern wird das Intervall zwischen den einzelnen Bildern bei geöffnetem VRAM als Vertical-Blank oder VBlank bezeichnet (in CRT ist dies der Impuls, den Bildröhrenstrahl während der Bildumkehrung zu löschen). (Es gibt auch eine HBlank zwischen den einzelnen Zeilen der Anzeige, sie ist jedoch sehr kurz.) Sie können dieses Problem jedoch umgehen, indem Sie den LCD-Bildschirm ausschalten, dh wir können im VRAM aufnehmen, unabhängig davon, wo sich die Leuchtstoffspur des CRT-Bildschirms befindet.

Wenn Sie etwas vermasseln, hat diese Bewertung viel zu erklären.. Es wird das Thema aus der Sicht von SNES untersucht. Vergessen Sie nicht, dass es keinen Elektronenstrahl gibt, aber die Anzahl ist unterschiedlich, aber für den Rest ist es völlig anwendbar. Im Wesentlichen müssen wir das Flag „FBlank“ setzen.

Der Game Boy-Trick besteht jedoch darin, dass Sie das LCD nur während des VBlank ausschalten können. Das heißt, wir müssen auf VBlank warten. Dafür müssen Sie Interrupts verwenden. Interrupts sind Signale, die der Game Boy "Hardware" an den Zentralprozessor sendet. Wenn der Interrupt-Handler gesetzt ist, stoppt der Prozessor seine Arbeit und ruft den Handler auf. Game Boy unterstützt fünf Interrupts, und einer davon startet, wenn VBlank startet.

Interrupts können auf zwei verschiedene Arten verarbeitet werden. Die erste und häufigste Aufgabe ist die Aufgabe des Interrupt-Handlers.was funktioniert wie oben erklärt. Wir können jedoch einen bestimmten Interrupt aktivieren und alle Handler deaktivieren, indem Sie das Interrupt-Aktivierungsflag setzen und den Opcode verwenden di. Normalerweise tut es nichts, aber es hat den Nebeneffekt, den HALT-Opcode zu verlassen, wodurch die CPU angehalten wird, bevor ein Interrupt auftritt. (Dies geschieht auch, wenn Handler aktiviert mainsind, sodass wir den HALT-Zyklus beenden können.) Falls Sie interessiert sind, erstellen wir eventuell einen VBlank-Handler, aber ein großer Teil davon hängt von bestimmten Werten an bestimmten Adressen ab. Da wir im RAM noch nichts angegeben haben, kann ein Versuch, den VBlank-Handler aufzurufen, zu einem Systemabsturz führen.

Um die Werte einzustellen, müssen wir Befehle an die Game Boy-Hardware-Register senden. Es gibt spezielle Speicheradressen, die direkt mit verschiedenen Geräten verbunden sind, in unserem Fall mit der CPU, mit denen Sie die Funktionsweise ändern können. Wir interessieren uns besonders für Adressen $FFFF(Interrupt-Enable-Bit-Feld), $FF0F(Bit-Feld eines aktivierten, aber nicht verarbeiteten Interrupts) und $FF40(LCD-Steuerung). Eine Liste dieser Register finden Sie auf den Seiten, die mit dem Abschnitt "Dokumentation" der Awesome Game Boy-Entwicklungsliste verbunden sind.

Um das LCD auszuschalten, aktivieren wir nur den VBlank-Interrupt, weisen einen $FFFFWert zu $01, führen HALT durch, bis die Bedingung erfüllt ist $FF0F == $01, und weisen dann dem Adressenbit 7 der Adresse den $FF40Wert 0 zu.

Einstellen der Palette und der Bildlaufposition


Das ist leicht zu machen. Jetzt, da das LCD ausgeschaltet ist, müssen wir uns keine Sorgen um VBlank machen. Um die Bildlaufposition einzustellen, genügt es, die X- und Y-Register auf 0 zu setzen. Mit der Palette ist alles etwas komplizierter. Im Game Boy können Sie die ersten bis vierten Farbschattierungen der 4 Graustufen (oder, falls gewünscht, Sumpfgrün) zuweisen. Dies ist nützlich, wenn Sie Übergänge oder ähnliches machen möchten. Ich lege einen einfachen Farbverlauf als Palette fest, definiert als Liste von Bits %11100100.

Reinigen von VRAM und Laden von Kachelgrafiken


Beim Start bestehen alle Bilddaten und die Hintergrundkarte nur aus dem scrollenden Nintendo-Logo, das beim Systemstart angezeigt wird. Wenn ich Sprites aktiviere (sie sind standardmäßig deaktiviert), werden sie auf dem Bildschirm verstreut. Sie müssen den Videospeicher löschen, um von vorne zu beginnen.

Dazu brauche ich eine Funktion wie die memsetvon C. (Ich brauche auch ein Analog, memcpyum die Grafikdaten zu kopieren.) Die Funktion memsetsetzt das angegebene Speicherfragment auf ein bestimmtes Byte. Es wird mir leicht fallen, mich selbst zu implementieren, aber AssemblyDigest hat diese Funktionen bereits im Tutorial , also benutze ich sie.

Zu diesem Zeitpunkt kann ich das VRAM löschen, indem ich memsetes schreibe $00(obwohl beim ersten Commit der Wert verwendet wurde)$FFdas auch aufkam), und laden Sie dann die Kachelgrafik mit VRAM in VRAM memcpy. Genauer gesagt, ich muss es an die Adresse kopieren $9000, da dies Kacheln sind, die nur für Hintergrundgrafiken verwendet werden. (Adressen $8000-$87FFwerden nur für Sprite-Kacheln verwendet und Adressen $8800-$8FFFsind für beide Typen üblich.)

Job Fliese Karte


Game Boy hat eine Hintergrundebene, die in 8x8-Kacheln unterteilt ist. Die Hintergrundebene selbst nimmt ungefähr 32x32 Kacheln ein, d. H., Sie hat eine Gesamtgröße von 256x256. (Zum Vergleich: Der Bildschirm der Konsole hat eine Auflösung von 160x144.) Ich musste die Kacheln, die mein Bild zeilenweise bilden, manuell festlegen. Glücklicherweise werden alle Steine in Reihenfolge angeordnet sind , so dass ich nur in jeder Zeile mit den Werten zu füllen , hatte N*11von N*11 + 10denen N- die Zeilennummer, und die restlichen 22 Fliesen , das Element zu füllen $FF.

Schalten Sie das LCD ein


Hier müssen wir nicht auf VBlank warten, da sich der Bildschirm bis VBlank immer noch nicht einschalten lässt. Deshalb habe ich einfach einen neuen Eintrag im LCD-Steuerregister vorgenommen. Ich habe auch Hintergrundebenen und Sprites sowie die korrekten Adressen der Kachelkarte und Kachelgrafiken eingefügt. Danach erhielt ich die folgenden Ergebnisse. Ich habe auch Interrupt-Handler wieder mit dem Opcode aktiviert ei.

Um es noch interessanter zu machen, habe ich einen sehr einfachen Interrupt-Handler für VBlank geschrieben. Durch Hinzufügen eines $40Opcodes zur Adresse kann ich den Handler zu jeder beliebigen Funktion machen, die ich brauche. In diesem Fall habe ich eine einfache Funktion geschrieben, die den Bildschirm nach oben und nach links rollt.

Hier sind die fertigen Ergebnisse. [Zusatz: Ich habe gerade festgestellt, dass GIF falsch geschleift ist, es muss das Bild ständig übertragen.]


Bisher nichts besonders überraschend, aber trotzdem großartig, dass ich theoretisch meine alte Game Boy Color bekommen und sehen kann, wie mein eigener Code darauf läuft.

Plaid-Blatt-Spaß


Um etwas auf dem Bildschirm zu zeichnen, brauche ich natürlich Sprites. Nachdem ich die PPU (Picture Processing Unit) der Game Boy-Konsole untersucht hatte, entschied ich mich, bei Sprites mit 8 x 8 oder 8 x 16 zu stoppen. Wahrscheinlich brauche ich die letzte Option, aber um die Dimensionen zu spüren, kritzelte ich schnell einen Screenshot des Spiels im Maßstab 1: 8 auf dem karierten Papier.


Ich wollte den oberen Bildschirmrand unter dem HUD lassen. Es schien mir, dass es natürlicher aussehen würde als die Unterseite, denn wenn es fertig ist, müssen die Charaktere das HUD, wie in Super Mario Bros, vorübergehend sperren müssen, damit sie es schaffen. In diesem Spiel gibt es keine komplexe Plattform, und in der Tat gibt es auch kein Level-Design. Daher muss ich keine sehr allgemeine Sicht auf das Feld zeigen. Es reicht aus, die Charaktere auf dem Bildschirm und möglicherweise ab und zu Hindernisse zu positionieren. So kann ich mir genug Sprites leisten.

Also, wenn ein Quadrat der Fliese ein 8x8 ist, dann einer der Sprite nichtwird ausreichen, egal welche Größe ich wähle. Dies gilt insbesondere angesichts der Tatsache, dass mit Ausnahme von Sprüngen fast keine vertikale Bewegung im Spiel stattfindet. Also entschied ich mich, Sprites aus vier 8x16-Sprites zu erstellen. Die Ausnahme war der Schwanz des Fuchses, der zwei 8x16 Sprites belegt. Nach einer einfachen Berechnung wurde klar, dass zwei Füchse und zwei Geckos 20 von 40 Sprites belegen werden. Das heißt, Sie können viele weitere Sprites hinzufügen. (8x8-Sprites würden mein Limit schnell erschöpfen, was ich in den frühen Entwicklungsstadien nicht tun möchte.)

Für den Moment muss ich nur die Sprites rendern. Nachfolgend sind grobe Skizzen auf kariertem Papier dargestellt. Ich habe ein wartendes Sprite, ein "denkendes" Sprite, um zu entscheiden, ob ich einen Pass machen oder wie in einem SNES-Spiel laufen soll ... und das ist es. Ich plante auch, Sprites aus laufenden Charakteren, springenden Charakteren und Charakteren zu machen, die Gegner ergreifen. Aber zunächst habe ich nur Wart- und Denk-Sprites gezeichnet, um die Dinge nicht zu komplizieren. Den Rest habe ich noch nicht gemacht, ich muss es tun.


Ja, ich weiß, ich zeichne nicht sehr gut. Perspektive ist eine komplizierte Sache. (Ja, und dieses Gesicht des Polarfuchses ist schrecklich.) Aber es passt perfekt zu mir. Das Zeichendesign hat keine Besonderheiten, ist aber für Game Jam geeignet. Natürlich habe ich echte Geckos und Polarfuchs als Referenz verwendet. Ist es nicht wahrnehmbar?



Du kannst es nicht sagen. (Um es festzuhalten: Nachdem ich mir diese Bilder noch einmal angesehen hatte, wurde mir klar, dass es einen großen Unterschied zwischen Geckos und Eidechsen gibt. Ich weiß nicht, was ich damit anfangen soll, außer ich halte mich für dumm ...) Fox-Köpfe servierten Blaze the Cat aus der Sonic-Spielserie.

Zunächst wollte ich, dass Verteidiger und Angreifer in jedem Team unterschiedliche Geschlechter sind, und es war einfacher, sie voneinander zu unterscheiden. (Ich würde auch den Spielern erlauben, das Geschlecht ihres Charakters zu wählen.) Allerdings würde dies viel mehr Zeichnen erfordern. Deshalb machte ich bei den männlichen Geckos und weiblichen Füchsen Halt.

Und zum Schluss habe ich den Bildschirmschoner gezeichnet, weil auf einem karierten Blatt Papier Platz dafür war.


Ja, Aktionspositionen sind noch lange nicht ideal. Der Polarfuchs sollte verärgert sein und laufen, und der Gecko sieht bedrohlich aus. Der Verteidigerfuchs im Hintergrund ist eine lustige Referenz zu Kunst auf der Doom-Box.

Sprites digitalisieren


Dann begann ich, die Papierzeichnungen in Sprites umzuwandeln. Dafür habe ich das Programm GraphicsGale verwendet, das ich kürzlich frei gemacht habe. (Ich weiß, dass es möglich war, Asesprite zu verwenden, aber ich bevorzuge die GraphicsGale.) Die Arbeit mit Sprites war viel schwieriger als ich erwartet hatte. Jedes dieser Quadrate der oben gezeigten Sprites nimmt in einem 2x2-Raster bis zu 4 Pixel auf. Und in diesen Feldern gab es oft VIEL mehr Details als in 4 Pixeln. Daher musste ich viele Details von Skizzen loswerden. Manchmal war es sogar schwierig, sich an eine einfache Form zu halten, weil es notwendig war, einen für Augen oder Nase akzeptablen Ort zu verlassen. Mir scheint aber, dass alles gut aussieht, auch wenn das Sprite völlig anders ist.


Die Augen des Fuchses verloren ihre Mandelform und verwandelten sich in eine zwei Pixel hohe Linie. Die Augen des Geckos haben ihre Rundung behalten. Der Kopf des Geckos musste vergrößert werden, um die breiten Schultern loszuwerden, und alle Biegungen, die ein Fuchs haben konnte, waren deutlich geglättet. Aber um ehrlich zu sein, all diese einfachen Änderungen sind nicht so schlimm. Manchmal konnte ich kaum entscheiden, welche der Varianten besser ist.


GraphicsGale bietet außerdem praktische Ebenen- und Animationsfunktionen. Das bedeutet, dass ich den Schwanz eines Fuchses getrennt von seinem Körper animieren kann. Dies ist eine großartige Hilfe, um wertvollen VRAM-Speicherplatz zu sparen, da ich den Schwanz nicht in jedem Frame duplizieren muss. Darüber hinaus bedeutete es, dass Sie Ihren Schwanz mit variabler Geschwindigkeit wedeln können, sich verlangsamen, wenn der Charakter steht, und beim Laufen beschleunigen. Die Programmierung ist jedoch etwas komplizierter. Aber ich übernehme diese Aufgabe immer noch. Ich habe bei 4 Animationsbildern angehalten, denn das reicht.

Man kann sehen, dass es im Polarfuchs drei der hellsten Grauschattierungen gibt und im Gecko drei der dunkelsten. Beim GameBoy ist dies zulässig, da das Sprite zwar nur drei Farben enthält, die Konsole jedoch die Möglichkeit bietet, zwei Paletten festzulegen. Ich habe es so gemacht, dass der Fuchs eine Palette von 0 verwendet, und für einen Gecko eine Palette 1. Dies komplettiert die gesamte Palette an verfügbaren Paletten, aber ich glaube nicht, dass ich andere brauche.

Ich musste mich auch um den Hintergrund kümmern. Ich habe mich nicht mit seinen Skizzen beschäftigt, weil ich vorhatte, dass es sich um eine Volltonfarbe oder ein einfaches geometrisches Muster handeln würde. Ich habe den Begrüßungsbildschirm auch nicht digitalisiert, weil nicht genügend Zeit vorhanden war.

Lade Sprites zum Spiel herunter


Überprüfen Sie mit dem be99d97- Commit .

Nachdem jeder einzelne Frame der Charaktergrafik gespeichert wurde, konnte mit dem Konvertieren in das GameBoy-Format begonnen werden. Es stellte sich heraus, dass das RGBDS ein sehr praktisches Dienstprogramm namens RGBGFX ist. Sie kann vom Team angerufen werden rgbgfx -h -o output.bin input.pngund erstellt ein mit GameBoy kompatibles Kachelset. (Mit der Option -h wird der 8x16-kompatible Kacheln-Modus festgelegt, sodass die Konvertierung von oben nach unten und nicht von links nach rechts erfolgt.) Es werden jedoch keine Bindungen bereitgestellt und doppelte Kacheln können nicht verfolgt werden, wenn jedes Bild ein separates Bild ist. Aber wir werden dieses Problem für später belassen.

Nachdem Sie die .bin-Ausgabedateien erstellt haben, fügen Sie sie einfach mit dem Assembler hinzuincbin "output.bin". Um alles zusammenzuhalten, habe ich eine gemeinsame Datei "gfxinclude.z80" erstellt, die alle hinzugefügten Grafiken enthält.

Es war jedoch sehr langweilig, die Grafiken bei jeder Änderung manuell neu zu generieren. Also bearbeitete ich die Datei build.bat und fügte eine Zeile hinzu for %%f in (gfx/*.png) do rgbds\rgbgfx -h -o gfx/bin/%%f.bin gfx/%%f, die jede PNG-Datei im Ordner gfx / in die bin-Datei konvertiert und in gfx / bin speichert. Das hat mein Leben sehr vereinfacht.

Um Hintergrundgrafiken zu erstellen, habe ich viel fauler vorgegangen. RGBASM hat eine Direktivedw `. Es folgt eine Reihe von 8 Werten von 0 bis 4, die einer Reihe von Pixeldaten entspricht. Da die Hintergrund-Sprites sehr einfach waren, erwies es sich als einfacher, ein einfaches geometrisches Muster zu kopieren und einzufügen, um ein Volumen-, Streifen- oder Schachbrettmuster zu erzeugen. Hier zum Beispiel sieht es aus wie ein Landplättchen.

bg_dirt:
    dw `00110011
    dw `00000000
    dw `01100110
    dw `00000000
    dw `11001100
    dw `00000000
    dw `10011001
    dw `00000000

Er schafft eine Reihe von verschobenen Streifen mit der Illusion der Perspektive. Dies ist ein einfacher, aber intelligenter Ansatz. Das Gras war etwas komplizierter. Anfangs war es eine Gruppe horizontaler Linien mit einer Höhe von 2 Pixeln, aber ich habe manuell einige Pixel hinzugefügt, wodurch ein bisschen Rauschen entsteht, mit dem das Gras besser aussieht:

bg_grass:
    dw `12121112
    dw `12121212
    dw `22112211
    dw `11121212
    dw `22112211
    dw `21212121
    dw `12121212
    dw `12211222

Grafik-Rendering


In GameBoy werden Sprites in einem Bereich namens OAM oder Object Attribute Memory gespeichert. Es enthält nur Attribute (Richtung, Palette und Priorität) sowie die Kachelnummer. Es genügte mir, diesen Bereich zu füllen, um die Sprites auf dem Bildschirm anzuzeigen.

Es gibt zwar einige kleinere Funktionen. Zunächst müssen Sie Grafiken von ROM in VRAM laden. GameBoy kann nur Kacheln rendern, die in einem speziellen Speicherbereich namens VRAM gespeichert sind. Um von ROM in VRAM zu kopieren, reicht es aus, dass es memcpyin der Initialisierungsphase des Programms ausgeführt wird. Gleichzeitig stellte sich heraus, dass ich mit nur 6 Zeichen Sprites und 4 Endstückplättchen bereits ein Viertel des für Sprites zugewiesenen VRAM-Bereichs belegte. (VRAM wird normalerweise in Hintergrund und Sprites unterteilt, und 128 Bytes sind für sie gemeinsam.)

Außerdem ist der Zugriff auf OAM nur während VBlank möglich. Ich habe zunächst gesagt, dass die Sprites vor den Berechnungen auf VBlank gewartet haben, aber ich hatte Probleme, da die Berechnungen der Sprites über die gesamte VBlank-Zeit hinausgingen und nicht abgeschlossen werden konnten. Die Lösung hier ist, in einen separaten Speicherbereich außerhalb von VBlank zu schreiben und sie während VBlank einfach in OAM zu kopieren.

Wie sich herausgestellt hat, verfügt der GameBoy über ein spezielles Hardware-Kopierverfahren, eine Art DMA (Direct Memory Access), der genau dies tut. Durch Schreiben in ein bestimmtes Register und Durchlaufen des Besetztzyklus in HiRAM (da das DMA-ROM nicht verfügbar ist), können Sie Daten viel schneller aus dem RAM in das OAM kopieren als mit der Funktionmemcpy. Wenn es interessant ist, können pikante Details finden Sie hier .

Zu diesem Zeitpunkt musste ich lediglich ein Verfahren erstellen, das bestimmt, was schließlich in der DMA aufgezeichnet wird. Dafür musste ich den Status der Objekte an anderer Stelle speichern. Zumindest war Folgendes erforderlich:

  1. Typ (Gecko, Polarfuchs oder tragbares Objekt eines Teams)
  2. Richtung
  3. X-Position
  4. Y-Position
  5. Frame-Animation
  6. Animations-Timer

In der ersten, sehr unordentlichen Entscheidung habe ich den Typ des Objekts überprüft und, je nachdem, den Übergang zur Prozedur vorgenommen, die diesen Objekttyp spritzig renderte. Das Polarfuchsverfahren nahm beispielsweise eine Position auf X ein, addierte oder entfernte 16 je nach Richtung, fügte zwei Schwanzsprites hinzu und bewegte sich dann entlang des Hauptsprites auf und ab.

Hier ist ein Screenshot davon, wie ein Sprite in VRAM auf einem Bildschirm aussah. Der linke Teil besteht aus einzelnen Sprites, daneben hexadezimale Zahlen, von oben nach unten - vertikale und horizontale Position, Kachel- und Attributmarkierungen. Rechts sehen Sie, wie es nach der Montage aussah.


Mit der Tail-Animation war alles etwas komplizierter. Bei der ersten Lösung habe ich einfach den Animations-Timer-Inkrement in jedem Frame ausgeführt und einen Boolean andmit dem Wert erstellt %11, um die Frame-Nummer zu erhalten. Dann können Sie einfach zu der ersten Endkachel in VRAM 4 * Frame-Nummer hinzufügen (jeder Animationsframe besteht aus 4 Kacheln), um vier verschiedene im VRAM gespeicherte Frames zu erhalten. Es hat funktioniert (vor allem der Schwanz-Schwanz-Suchteil), aber der Schwanz wackelte wahnsinnig schnell und ich musste einen Weg finden, ihn zu verlangsamen.

Zweitens bessere Entscheidungen, führte ich in jedem Rahmen erhöht den globalen Timer, und wenn der Wert der Transaktionen andmit ihnenund die von mir gewählte Zweierpotenz war gleich 0, das Objekt-Zeitgeberinkrement wurde durchgeführt. Somit kann jedes einzelne Objekt den Countdown seines Animationstimers mit jeder erforderlichen Geschwindigkeit durchführen. Es hat perfekt funktioniert und ich konnte den Schwanz auf ein vernünftiges Niveau bremsen.

Schwierigkeiten


Aber wenn alles so einfach wäre. Vergessen Sie nicht, dass ich all dies im Code mit Hilfe der eigenen Unterprozedur für jedes Objekt erledigt habe. Wenn dies erforderlich war, sollten Sie dies in jedem Frame tun. Ich musste festlegen, wie mit dem nächsten Sprite fortgefahren werden soll, und aus welcher Kachel es besteht, indem die Register manuell bearbeitet werden.

Es war ein völlig instabiles System. Um ein einzelnes Bild zu zeichnen, war es notwendig, eine ausreichend große Anzahl von Registern und CPU-Zeit zu verwenden. Es war fast unmöglich, Unterstützung für andere Kader hinzuzufügen, und selbst wenn es mir gelang, wäre die Unterstützung des Systems sehr schmerzhaft. Glaub mir, es war echtes Chaos.Ich brauchte ein System, in dem der Code für das Rendern von Sprites verallgemeinert und unkompliziert sein würde, so dass es keine Verschränkung von Bedingungen, Manipulation von Registern und mathematischen Operatoren gibt.

Wie habe ich das Problem behoben? Ich werde darüber im folgenden Teil des Artikels berichten.

Bild

Jetzt auch beliebt: