ABC für Action-Spiele (Teil 1)

Zauberkünste sind bei der Einführung in die Programmierung von Assembler-Spielen nicht erforderlich. Auch wenn Sie nur grundlegende Kenntnisse der 68000er-Maschinensprache besitzen, folgen Sie unserem neuen Kurs Schritt für Schritt bis zum fertigen Spiel, einer Aliens-Abwehrschlacht. Im ersten Teil erfahren Sie, womit alles beginnt: mit Sprites, der Bewegung von Grafik auf dem Bildschirm.

Herzlich willkommen zu unserem kleinen Lehrgang in die Welt der Spielprogrammierung. Im ersten Teil des Kurses wollen wir eine Sprite-Routine programmieren, die die Grundlage für unseren weiteren Weg zum eigenen Spiel bildet. In den nächsten Folgen beschäftigen wir uns dann mit weiterführenden Routinen wie zum Beispiel der Abfrage der Joystick-Ports und entwerfen das Gerüst eines einfachen Action-Spiels, das Ihnen grundlegende Techniken verdeutlicht.

Die Routinen sind alle in Assembler geschrieben und gut dokumentiert, grundlegende Assembler-Kenntnisse reichen zum Verständnis aus. Ihr Assembler sollte von Digital Research stammen oder dazu Objektcode-kompatibel sein, denn die Routinen verwenden das DR-Format.

Doch jetzt wollen wir uns auch gleich in die Praxis der Spiele-Programmierung stürzen und das erste Kapitel aufschlagen: die Sprites.

Sprites, das sind bewegte Grafiken, die über den ganzen Bildschirm flitzen: unabdingbar also für Computerspiele, die vor bewegten Objekten nur so strotzen. Klassische Spielcomputer, wie etwa der C 64, erzeugen diese Sprites hardwaremäßig. Der Programmierer stellt nur die x- und y-Koordinate sowie die Grafikdaten bereit, die Aufbereitung der Grafik übernimmt die Hardware des Computers.

Der Atari ST ist leider nicht in der Lage, Hardware-Sprites darzustellen.

Die Verschiebung der Grafikdaten im Bildschirmspeicher muß der Programmierer deshalb selbst vornehmen. Das kostet natürlich Rechenzeit, aber wozu haben wir einen MC68000-Prozessor?

Durch bitweises Schieben erreichen wir ruckfreie Bewegungen
Die Bildschirmorganisation des ST stellt für die Farbspeicherung jedes Pixels 4 Bit zur Verfügung
Damit nicht gesetzte Pixel die Farbe des Hintergrunds behalten, maskieren wir sie

Wie sieht so eine Routine aber aus?

Schauen wir uns dazu mal die Organisation des Bildschirmspeichers im ST an: In der niedrigen Auflösung (320 x 200 Pixel) stellt der ST 16 Farben dar. Da wir hier die größte Auswahl an Farben haben, bedienen sich die meisten Spiele dieser Auflösung. Vier aufeinanderfolgende Speicherwörter (4x2 Byte) sind für die Farben von 16 Bildschirmpunkten verantwortlich. Je nach Bit-Kombination dieser vier Planes (Bit-Ebenen) wählt der ST für den jeweiligen Bildschirmpunkt eines von 16 Farbregistern aus. Farbregister 1 ist $0000 und Farbregister 16 ist $1111. Welche Farben in den Farbregistern stehen, läßt sich mit verschiedenen XBIOS-Funktionen oder auch, wie in unserem Beispielprogramm, durch Übermitteln der Palettenadresse an die Systemvariable $45a festlegen.

Die Software hilft aus

Die Benutzung von nur zwei Planes (zwei von vier Speicherwörtern), verringert zwar die Zahl der auswählbaren Farbregister (vier von ursprünglich 16), doch es erhöht die Geschwindigkeit der Routine, denn jetzt müssen wir nur noch zwei Speicherwörter beschreiben. Welche Planes Sie benutzen, bleibt Ihnen überlassen. Ob Sie Plane 1 und Plane 4 oder nur Plane 4 verwenden, spielt keine Rolle. Sie müssen nur beachten, daß die Sprite-Routine wissen will, welche Planes sie benutzen, sonst kann es zu unschönem Grafik-Wirrwarr kommen.

Je mehr Planes ein Sprite hat und je größer ein Sprite ist, desto langsamer ist die Sprite-Routine. Eine von 200 Bildschirmzeilen besteht aus 20 Wörtern (320/16) mal 4 Planes, also aus 80 Wörtern oder 160 Byte.

Der Bildschirmspeicher ist demnach 200 mal 160, also 32000 Byte groß.

Unsere Sprite-Routine muß die Datenwörter aus dem normalen Speicher des ST lesen und in das Video-RAM schreiben, natürlich unter Beachtung der vorhin besprochenen Organisation des Bildschirmspeichers.

Ein Beispiel: Ein achtfarbiges (drei Planes), 32 mal 16 Pixel großes Sprite soll an einer beliebigen xy-Position ausgegeben werden. Würden wir einfach die 16 mal 6 Wörter aus dem Speicher holen und unverändert in das Video-RAM schreiben, so könnten wir dieses Sprite in x-Richtung nur in 16-Punkte-Schritten verschieben, denn die Datenwörter müssen immer an einer von 20 Wortgrenzen beginnen. Um punktweise zu verschieben, müssen wir die Bits innerhalb der Wörter verschieben. Das erledigen wir mit den Datenregistern des Prozessors, in denen wir die Datenwörter entsprechend manipulieren. Die obere Registerhälfte enthält dann, bezogen auf die Organisation des Video-RAM, das erste Wort und die untere das zweite Wort.

Insgesamt verändern wir im Video-RAM drei Wörter und die dazugehörigen Planes: je zwei Bildschirmwörter für ein Datenwort, wobei sich beide Datenwörter das zweite Bildschirmwort teilen. Das klingt zwar ein wenig kompliziert, aber wenn Sie sich dazu mal unsere Bilder anschauen, leuchtet es hoffentlich vollständig ein.

Um die Adresse des ersten, für unser Sprite wichtigen Bildschirmwortes und die Anzahl der Verschiebungen innerhalb der Wörter zu ermitteln, führen wir folgende Rechnung durch: (y-Koordinate) x 160 + [(x-Koordinate) / 16] x 8, addiert zur Startadresse des Video-RAM, ergibt die Adresse des Wortes.

Die Anzahl der Bitverschiebungen lautet: (Anzahl) = (x-Koordinate) — [(x-Koordinate) / 16] x 16.

Um auf die richtige x-Position zu kommen, müssen wir die Bits in den Datenregistern um 16 Bit nach links verschieben.

Unser Sprite ist jetzt zwar auf dem Bildschirm, doch alle Pixel, die in unserem Sprite nicht gesetzt sind, haben die Farbe des ersten Farbregisters (alle Bits gleich 0).

So wollen wir das aber nicht haben: Die Pixel sollen, wenn sie nicht gesetzt sind, ihre vorherige Färbe behalten. Nachdem wir sie verschoben haben, müssen wir die Daten deshalb mit den Daten des Video-RAM verknüpfen.

Maskenfest für Bits und Planes

Zu diesem Zweck stellen wir aus den Datenplanes eines Datenwortes eine Maske zusammen, die dann bestimmte Bildschirm-Bits ausmaskiert. Bitte beachten Sie, daß Sie alle Planes maskieren müssen, egal ob Sie sie benutzen oder nicht.

Der größte Teil der Arbeit wäre jetzt geschafft. Erwähnenswert ist noch, daß wir die Werte der Video-RAM-Wörter natürlich vor dem Beschreiben in einem Puffer speichern, denn wollen wir das Sprite an eine andere Position setzen, dann müssen wir die alte Position ja wieder restaurieren. Soweit so gut, wenn da nicht noch ein kleines Problem wäre: Durch das andauernde Löschen und Setzen fangen die Sprites an zu flackern, was sehr unprofessionell aussieht. Das Flackern muß also weg.

Abschied von der Flacker-Grafik

Fast alle Spiele benutzen dabei denselben Trick: Sie verwenden zwei Bildschirme, einen logischen, nicht-sichtbaren und einen physikalischen, den Sie auf Ihrem Monitor sehen. Im logischen Bildschirm wird gelöscht und neu gezeichnet, während im physikalischen noch das alte Bild steht.

Danach vertauscht der ST einfach die beiden Bildschirme. Der logische ist dann der physikalische Bildschirm. Im neuen logischen, dem alten physikalischen Bildschirm, findet dann die nächste Lösch- und Zeichen-Operation statt. Das sogenannte »Shiften« läßt sich beliebig oft wiederholen.

Durch das schnelle Umschalten zwischen den fertigen Bildern verschwindet das unliebsame Geflacker. Genial, nicht wahr?

Außer zwei 32 KByte großen Bildschirmen müssen Sie die Anzahl der Bildschirmpuffer pro Sprite ebenfalls verdoppeln, weil gleichzeitig immer zwei Sprites auf zwei Bildschirmen sind. Um nicht auch noch die Variablen für die Koordinaten der Sprites zu verdoppeln, speichern wir im ersten Langwort des Puffers die fertig berechnete Löschadresse, die der ST dann nur noch zur Adresse des Bildschirms addiert. Das Beispielprogramm auf unserer Leser-Service-Diskette läßt unser Sprite den Mausbewegungen folgen.

Nebenbei können Sie im Programm sehen, wie man ganz einfach die Maus abfragt (wenn Sie nicht mit Blitter-TOS arbeiten, beachten Sie bitte die Änderungen in dem gut dokumentierten Programm). Als kleine Übung schlagen wir Ihnen vor, die Sprite-Routine für ein größeres oder kleineres Sprite mit mehr oder weniger Farben umzuschreiben. So bleiben Sie für den zweiten Teil unseres Spiele-Kurses gut im Training.

Am Ende der zweiten Folge werden Sie dann schon in der Lage sein, das Sprite per Joystick zu bewegen. Außerdem stellen wir Ihnen das nächste Mal ein Utility vor, mit dem Sie mit Degas gemalte Sprites in Ihre eigenen Programme einbinden können. (Tarik Ahmia/ps)

KURSÜBERSICHT ABC für Action-Spiele

In diesem Kurs lernen Sie, Action-Spiele in Assembler zu programmieren

Teil 1: Sprite-Programmierung in Maschinensprache

Teil 2: Joystickabfrage für beide Ports □ Degas-Utility: beliebige Blöcke aus Degas ausschneiden und als DR-Objektcode speichern □ Sprite-Animation

Teil 3: Erster Teil des Action-Spiels, das unsere Sprite-Routine nutzt

Teil 4: Zweiter Teil des Action-Spieles □ Erklärung von digitalisierten Soundeffekten

FACHWÖRTER FÜR ASSEMBLERPROGRAMMIERER

Bit: Kleinste Informationseinheit (1 oder 0)
Byte: 8 Bit
Pixel: 1 Bildschirmpunkt
Plane: Bit-Ebene, dient zur Darstellung der verschiedenen Farben
Sprite: Auf dem Bildschirm bewegtes Objekt
Video-RAM: Bildschirmspeicher
Wort: 16 Bit oder 2 Byte


Markus Zahnjel
Aus: ST-Magazin 01 / 1989, Seite 158

Links

Copyright-Bestimmungen: siehe Über diese Seite