Spiele-Kurs für Assemblerprogrammierer
(Teil 1)

64'er, Ausgabe 6/Juni 1989

Sprites rasen über den Bildschirm, Landschaften bewegen sich sanft unter dem Betrachter weg, der von zahllosen Gegnern bombardiert wird. Die Programmierung derartig spannender Spiele soll nicht länger ein Geheimnis für Sie bleiben.

Kennen Sie folgende Situation? Sie sitzen völlig entspannt vor Ihrem Computer und sehen nicht den geringsten Anlaß, irgendeinen Handschlag zu machen, der in Richtung Arbeit geht. Was tun Sie in solch einem Augenblick? Sie legen die Diskette mit Ihrem Lieblingsspiel ein. Nach einigen Sekunden zieht ein mit faszinierender Grafik, tollem Sound und fesselnder Handlung ausgestattetes Meisterwerk die Blicke auf sich. Der erste Gedanke, der Ihnen kommt, ist: »Mensch, wenn ich so ein Spiel schreiben könnte...«. Dieser Kurs soll Ihnen die zur Programmierung eines Spiels notwendigen Kenntnisse vermitteln. Er spricht vor allem diejenigen Leser an, die der Assemblersprache mächtig sind. Am Ende jedes Teils wird das neu Hinzugekommene zu einem, auch von Basic aus nutzbaren Utility zusammengefaßt. Diejenigen, die sich noch nicht sicher in der Assemblerprogrammierung fühlen, seien auf den Kurs »Assembler für Einsteiger« verwiesen, der in der 64'er-Ausgabe 6/88 startete. Möchten Sie nicht genau wissen, wie eine Problemlösung im einzelnen funktioniert, so können Sie natürlich auch nur die Beispielprogramme eintippen und für eigene Spiele verwenden. Wir beschreiben, woraus ein Spiel besteht, wie man Rasterzeilen-Interrupts (Bild 1) programmiert, wie man mit deren Hilfe flimmerfreie Sprite-Bewegung realisiert, wie man eigene Zeichensätze installiert und nicht zuletzt: wie man flimmerfrei scrollt. Natürlich darf die Sound- und Musikprogrammierung nicht fehlen. In diesem Zusammenhang wird auch eine Musikroutine mitgeliefert, die Sie in eigene Programme einbauen können. Im letzten Teil schließlich wird ein mit den in diesem Kurs erworbenen Kenntnissen erstelltes Spiel beschrieben und das Listing im einzelnen kommentiert.

Bild 1. Durch Programmierung des Rasterzeilen-Interrupts lassen sich derartige Effekte im Bildschirmrahmen erzielen

Keine Sorge, lassen Sie sich nicht von den verwendeten Fachbegriffen abschrecken, sie werden in den verschiedenen Kursteilen erklärt.

Bevor wir uns näher mit der Programmierung von Computerspielen befassen, müssen erst einmal einige Fragen geklärt werden: Was ist ein Computerspiel überhaupt?

Man könnte nun sagen, ein Computerspiel sei ein Spiel das auf dem Computer läuft. Klingt einleuchtend, allerdings gibt es beispielsweise Monopoly oder Trivial Persuit auch als Computerspiel. Da es diese Spiele aber auch als Brettspiele gibt, möchte ich auf sie nicht eingehen. Auch die sogenannten Adventures (Abenteuerspiele) gibt es in einer etwas »unmoderneren« Fassung als Frage- und Antwort-Buch. Natürlich ist das Spielen eines Adventures mit dem Computer viel komplexer und interessanter, aber sie zählen nicht zu den Computerspielen, die wir hier erläutern wollen. Wer wissen möchte, wie man ein Adventure programmiert, sei auf die 64'er Sonderhefte 4/86 und 2/85 verwiesen. Dort finden Sie Programmierkurse für Adventures. Das typische Computerspiel gibt es eigentlich gar nicht - dafür immer wieder Spiele (und das ist gut so), die völlig neuartige Spielideen haben. Man kann sie aber größtenteils in bestimmte Kategorien einordnen:

  1. Adventures und Rollenspiele
  2. Geschicklichkeitsspiele
  3. Actionspiele
  4. Simulationen
  5. Strategiespiele

Sicherlich gibt es auch Spiele, die nicht in diese Tabelle passen; uns sollen Geschicklichkeits- und Actionspiele am meisten interessieren, denn diese sind nicht nur programmtechnisch am aufwendigsten, sondern oft auch am eindrucksvollsten.

Wie ist ein solches Spiel aufgebaut?

Generell bestehen diese Spiele aus folgenden Teilen:

Der Punkt »Spielablauf« dürfte einigen noch unklar sein. Der Spielablauf ist das schwierigste am ganzen Spiel. Die Grafik kann noch so formvollendet, die Musik noch so perfekt sein, wenn der Ablauf nicht »in Ordnung« ist, ist das Spiel höchstens noch halb so gut. Unter dem Spielablauf versteht man die Spielidee und die daraus resultierende Koordination der Spielfiguren, die Erhöhung der Punktzahl, das Verlieren der Leben und so weiter.

Bild 2. Die Scrollgrafik von »Pacmania« steht Modell für die eigene Spiele-Programmierung

Nehmen wir als Beispiel »Pac Man« (Bild 2). Auf dem Bildschirm ist ein Labyrinth, dessen Gänge mit Punkten aufgefüllt sind. Unser Pac Man hat nun die Aufgabe, durch das Labyrinth zu rasen und diese Punkte »aufzumampfen«. Hinderlich sind ihm dabei allerdings vier Gespenster, die ihn verfolgen. Verzehrt Pac Man einen der Eckpunkte, so hat er für einige Sekunden die Fähigkeit, die Gespenster genüßlich zu verspeisen. Je nachdem, wieviel und was unser Pac Man verzehrt, steigt die Punktzahl. Wird er von einem der Gespenster berührt, so verliert er ein Leben. Sind keine Leben mehr vorhanden, so ist das Spiel zu Ende. Das wäre nur das Wesentliche. Eine ganze Menge für das simple Spiel, nicht wahr? Es tauchen hier nun einige Probleme auf, zum Beispiel: Wie soll sich unser Pac-Männchen über den Bildschirm bewegen? Es soll mit dem Joystick bewegt werden, darf aber die Wände des Labyrinths nicht überschreiten. Wie sollen die Gespenster bewegt werden? Sie sollen die Wände des Labyrinths ebenfalls nicht überschreiten. Aber nach welcher Vorschrift sollen sie sich durch die Gänge bewegen? Das ist ein großes Problem. Läßt man die Geister per Zufall durch die Gänge wandern, so ist das Spiel zu leicht. Läßt man sie »intelligent«, also Pac Man folgend, durch die Gänge schleichen, ist das Spiel zu schwer. Man macht hier einen Kompromiß zwischen intelligenten und zufälligen Bewegungen. (Siehe auch »Die künstliche Intelligenz der Gespenster«--Webredaktion.)

Bild 3. »Scrollbildschirme« sind ein Zeichen für ausgefeilte Spiele. Beim »Kickstart« gibt es sogar zwei unabhängig bewegbare Hintergrundgrafiken (»Bildschirmsplitting«).

Warum in Assembler? Action oder Geschicklichkeitsspiele werden praktisch immer in Maschinensprache geschrieben, da das Basic des C64 die Geschwindigkeit einer Schnecke nur selten überschreitet. Den Gelegenheitsprogrammierern wird es vielleicht noch nicht so dramatisch aufgefallen sein, wer aber einmal versucht hat, eine Bitmap (hochauflösende Grafik) in Basic zu löschen, wird mir zustimmen. Eine solche Aktion dauert um die 3 Minuten (je nach Länge des Programms). In Assembler dagegen geht dies im Bruchteil einer Sekunde vonstatten, so daß man gar nicht sehen kann, wie die Grafik Byte für Byte verschwindet. Ferner lassen sich in Assembler Dinge realisieren, von denen ein Basic-Programmierer nur träumen kann. Man ist zum Beispiel in der Lage, die 20 KByte RAM zu nutzen, die normalerweise durch das Betriebssystem verdeckt werden, man kann zwei Programme praktisch gleichzeitig ablaufen lassen, und, und, und...

Wie geht man nun an die Programmierung eines Spiels heran? Zuallererst ist eine möglichst orginelle Spielidee erforderlich. Man sollte dann, bevor man sich an den Computer setzt, zunächst einen Ablaufplan (Bild 4) anfertigen und besser noch einmal einige Nächte drüber schlafen, bis man sicher ist, alles erfaßt zu haben. Natürlich kann man kleine Verbesserungen auch noch am fertigen oder fast fertigen Spiel vornehmen. Fällt einem jedoch noch eine grundsätzliche Änderung ein, so ist diese doch im allgemeinen sehr schwer nachträglich einzubauen.

Nach dem Ablaufplan überlegt man sich, wie man alles in die Tat umsetzt. Fangen wir bei der Grafik an. Wir bleiben beim Beispiel »Pac Man«. Zunächst muß das Labyrinth aufgebaut werden. Das könnte man nun mit einer hochauflösenden Grafik machen. Man zeichnet beispielsweise dieses Labyrinth mit Hi-Eddi oder Koala-Painter und speichert es auf Diskette ab. Diese Möglichkeit ist sicherlich nicht die beste, da eine Bitmap 8 KByte (Kilobyte, 8 x 1024 Byte) kostet. Man benutzt unter anderem deshalb den normalen Zeichensatz-Modus des VIC (Videochip). Dieser benötigt nur 1 KByte für das Video-RAM. Spiele jedoch mit dem gewöhnlichen Zeichensatz des C64 aufzubauen, ist auf die Dauer etwas eintönig. Deshalb nutzen wir die technischen Gegebenheiten des C64, eigene Zeichensätze zu installieren. Ein Zeichensatz benötigt 2 KByte. Video-RAM und Zeichensatz benötigen zusammen 3 KByte. Wie Sie sehen, wesentlich weniger als eine Bitmap. Man erstellt sich also einen eigenen Zeichensatz mit einem Character-Editor, dessen Zeichen dann auf dem Bildschirm zu einer Gesamtgrafik zusammengesetzt werden. Wie dies im einzelnen läuft, erfahren Sie in einem späteren Teil dieses Kurses.

Dann werden die Spielfiguren benötigt, also Pac Man und die Gespenster. Hierfür nutzen wir die Fähigkeit des C64, sogenannte Sprites (siehe Lexikon) darzustellen. Man erstellt sich diese Sprites mit einem Sprite-Editor. Hat man nun die Grafik- und Sprite-Daten auf Diskette, überlegt man sich, mit welcher Technik gearbeitet werden muß. Gewöhnlich kann man nämlich nicht einfach Maschinenroutinen aus einer Programmbibliothek zusammenfügen (linken); die Strukturen der einzelnen Teilprogramme sind meist voneinander abhängig, wobei die Art der Grafikverwaltung alle anderen Programmabschnitte wesentlich beeinflußt. Bei dem Beispiel »Pac Man« wäre eine Verknüpfung fertiger Unterprogramme noch denkbar, da man einen stehenden Bildschirm hat. Es gibt aber auch, wie Sie wahrscheinlich wissen, sogenannte »scrollende Bildschirme« (Bild 3). Das sind Grafiken, die sich im ganzen über den Bildschirm bewegen, es bewegen sich also nicht nur die Figuren, sondern auch der ganze Hintergrund. Da diese Technik sehr kompliziert ist, wird erst später in unserem Kurs darauf eingegangen. Hat man aber einen stehenden Bildschirm, ist das Darstellen der Grafik selbst kein großes Problem. Man schreibt nun zuerst die benötigten Routinen, die zum Beispiel den eigenen Zeichensatz darstellen, die Sprites bewegen und gegebenenfalls auch den Bildschirm scrollen.

Um bei unserem heiß geliebten Beispiel zu bleiben: Man schreibt die Routine, die den neuen Zeichensatz installiert, die das Labyrinth mit diesen neuen Zeichen aufbaut und die Routine, die Pac Man und die Gespenster über den Bildschirm flitzen läßt, also alles, was zur Bildschirmdarstellung gehört. Nach sorgfältigem (!) Austesten der Routinen macht man sich an den Sound heran. Unter Sound versteht man die Geräusche, die der Computer während des Spiels von sich gibt, zum Beispiel wenn Pac Man einen Punkt verzehrt.

Einige Spiele haben auch eine Hintergrundmusik. Eine eigene Musik für ein Spiel zu komponieren, wird wohl nur den wenigsten musikalischen Computerfreaks vorbehalten sein. Man kann aber auch fertige Musikstücke, die es in jedem Notengeschäft zu kaufen gibt, in den Computer eingeben. Nun schreibt man sich die Geräusch- und Musikroutinen. Diese müssen natürlich so gehalten sein, daß sie mit den Grafikroutinen zusammenarbeiten (sprich: keine gemeinsam benutzten Speicherzellen besitzten usw.). Jetzt hat man alles für die »Fassade« fertig. Nun müssen Sie Ihren hoffentlich bis ins Detail gehenden Ablaufplan, auch Flußdiagramm genannt, in die Tat umsetzen. Dieser Programmteil sagt den schon existierenden Grafik- und Soundroutinen, was sie wann tun müssen. In Basic würde man sagen: Es läuft vorwiegend auf IF-THEN-Konstruktionen heraus (siehe Bild 4).

Bild 4. Ein Flußdiagramm, hier für ein Spiel ähnlich »Pacmania«, ist eine wertvolle Gedankenstütze für jedes Programmierprojekt

Das wär's eigentlich schon. Hört sich ganz einfach an, oder? Ganz so einfach, wie es sich anhört, ist es aber bei weitem nicht. Zunächst einmal müssen Sie Assembler einigermaßen beherrschen. Dann müssen Sie gut zeichnen können, Sie müssen gute musikalische Fähigkeiten aufweisen und nicht zuletzt: Sie müssen viel Fantasie und eine aufregende Spielidee haben. Das ist vermutlich auch der Grund, warum ein Großteil aller Spiele im Teamwork entstanden ist. Das sollte Sie allerdings nicht abschrecken, nobody is perfect. Natürlich kann man auch gute Spiele schreiben, wenn man eine oder mehrere dieser Voraussetzungen nicht erfüllt.

Die oben genannten Fähigkeiten nützen Ihnen allerdings gar nichts, wenn Sie nicht wissen, wie man an so ein Projekt im Detail herangeht und vom Gedanken letztendlich zum Spiel kommt. Wie diese Dinge im einzelnen funktionieren, erfahren Sie in den nächsten Ausgaben.

(H.Rosenfeldt/ap)

  Anforderungen zur Kursteilnahme  
Der Programmierkurs »Spieleprogrammierung für Assemblerfreaks« ist an Assemblerkundige gerichtet und knüpft von den Ansprüchen her an den Assemblerkurs für Ansteiger an. Dieser Kurs begann im 64'er-Magazin 6/88 und endete in der Ausgabe 2/89.
 
  Steckbrief des Autors:
Harald Rosenfeldt
 

Es begann alles im Jahre 1985. Zum Geburtstag bekam ich einen C64 mit Datassette. Nach etwa einem halben Jahr konnte ich einigermaßen mit dieser Wundermaschine umgehen: Die ersten Gehversuche in Basic waren erfolgreich. Schnell entstand ein fantastisches Grafik-Programm, das so unbrauchbar war, daß es kurz nach der Fertigstellung in der untersten Schublade verschwand. Kurze Zeit später versuchte ich mich in Maschinensprache. Ein recht hochentwickeltes Programm diente zum Erstellen von Programmen in Basic. Ein Freund hatte mir geraten, dieses Programm an das 64'er-Magazin zu schicken: So entstand das Listing des Monats 1/88, »Master Tool«.

Kurz nach der Fertigstellung des Master Tools hatte ich meinem C64 einen zweiten Soundchip eingebaut. Leider mußte ich feststellen, daß es keine Software für diese Erweiterung gab und programmierte deshalb den »Musik-Assembler«, das Listing des Monats im Januar 1989.

(Harald Rosenfeldt)

 
  Die künstliche Intelligenz der Gespenster  
  • Es gibt vier Type von Gespenstern, die unterschiedlich gefarbt sind.
  • Der erste Typus von Gespenstern bewegt sich gemäß einer Vorschrift (z.B. links, links, rechts, links, links, links, rechts, rechts).
  • Der zweite Gespenst-Typ bewegt sich nur willkürlich.
  • Der dritte Gespenst-Typ folgt Pac Man aktiv, d.h. durch zu ihm zu drehen an jeder Ecke.
  • Der vierte Gespenst-Typ benutzt ein ziemlich komplexes Zielsuche-Algorithmus zum Raten wo Pac Man sich nach X Anzahl Ecken befindet und versucht diesen Weg zu folgen, oder Pac Man auf Sicht zu folgen.
  • Falls Pac Man eine »Kraft-Pille« verzehrt hat, wird das Algorithmus umgekehrt benutzt (zum Flüchten).
  • Die Gespenntype sind versehen mit unterschiedlichen Geschwindigkeiten.
  • In manche Level kan einer der vier Gespenst-Type Tricks von Pac Man nachahnen (wie z.B. den Sprung in »Pacmania«).
  • Die Gespenst-Type (1, 2, 3 oder 4) können mehrwertig auf das Spielfeld vorkommen (ein-, zwei- oder dreimal), je nach die Schwierigkeit und Größe des Levels.
  • Gleiche (oder auch ungleiche) Gespenst-Type können »aneinanderkleben«, wenn sie einander nahe sind, und der Vorderste der Pac Man auf Sicht hat. Sie folgen dann alle Pac Man, bis er um die Ecke verschwindet, danach benehmen sie sich wie zuvor. Dieser Eigenschaft der Gespenster ist aber nur für die Pac-Man-Cracks und nicht für Anfänger; es sollte nur in höhsten Level passieren. Es dient dazu soviel wie mögliche Gespenster in der Nahe von Pac Man zu kriegen, so daß er nicht zu einen ruhigeren Platz im Spielfeld flüchten kann.

(Webredaktion-RvB)

 
  Lexikon für Einsteiger  
Bitmap
Eine Bitmap ist eine Computergrafik, bei der man Punkt für Punkt setzen oder löschen kann. Diese Punkte nennt man Pixel. Bei dem normalen Textbildschirm, der nach dem Einschalten des C64 in Aktion tritt, kann man den Inhalt des Schirmes nur zeichenweise verändern (8 x 8 Pixel). Jedem Pixel in einer Bitmap ist ein Bit in einem Speicherbereich zugeordnet. Durch Setzen oder Löschen eines Bit wird das zugehörige Pixel gesetzt oder gelöscht.
Scrolling
Unter Scrolling versteht man das Verschieben eines ganzen Bildschirminhalts, so daß es wie ein bewegter Hintergrund aussieht. Dies funktioniert nur mit einem Textbildschirm, da hier nur das Video-RAM (1K) umkopiert werden muß. Eine Bitmap erstreckt sich über einen Speicherbereich von 8 KByte. Diese 8 KByte umzukopieren, dauert auch in Assembler einfach zu lange.
Sprites
Ein Sprite ist ein auf dem Bildschirm verschiebbares Bildchen. Man kann mit einigen einfachen POKE-Befehlen den Standort dieses Bildchens verändern. Der VIC (Videochip) überlagert es mit dem Hintergrund, so daß beim Verschieben des Sprites deutlich der Effekt von Vordergrund Hintergrund zustande kommt. Sprites eignen sich durch ihre einfache Handhabung hervorragend zur Programmierung von Computerspielen.
Video-RAM
Das Video-RAM beim C64 ist ein 1000 Byte langer Speicherbereich, der normalerweise von Adresse 1024 bis Adresse 2023 liegt. Durch POKEn in diesen Speicherbereich läßt sich der Inhalt des Bildschirms verändern. Schreiben Sie zum Beispiel eine 0 in Speicherzelle 1024 (POKE 1024,0), erscheint ein »Klammeraffe« (»@«) in der linken, oberen Ecke. Schreiben Sie eine 0 in Speicherzelle 1025, erscheint ein »Klammeraffe« ein Zeichen weiter rechts. Das Video-RAM ist zeilenweise aufgebaut. Die ersten 40 Speicherzellen (1024-1063) repräsentieren die erste Bildschirmzeile, die nächsten 40 Speicherzellen die darauf folgende Zeile usw.