Grafik und einfache Sprites mit QBASIC


Dieses Kapitel können Sie überspringen, falls Sie nur die Programmierprinzipien lernen wollen und zu den weiteren Teilen dieser Einführung streben. Falls Sie jedoch schon ganz eifrig mit QBASIC programmieren und bedauern, bisher keine Zeichnungen, Kurven, grafischen Spiele oder ähnliches erstellen zu können, dann bekommen Sie hier eine kleine Einführung in die Programmierung der "hochauflösenden" Grafik unter DOS.

Es wird Ihnen allerdings auch bei grundsätzlich gedämpften Interesse an Grafikprogrammierung empfohlen, mal hier reinzuschnuppern, da im Kapitel "Sprites" einige Beispielprogramme kommen, an denen Sie den "neuen" Programmierstil und die Möglichkeiten sehen können, die Ihnen eine modernere Programmiersprache als das C16-BASIC bietet.

Zur Orientierung in Bezug auf "Grafikprogrammierung auf PCs":

Bildschirmmodi

Genereller Hinweis: Sie können durch Alt-Enter das DOS-Fenster auf den s.g. Vollbildmodus schalten. In der Regel wird das der PC aber automatisch machen, wenn Sie unter Windows in deinem DOS-Fenster den Grafikmodus verwenden. Also nicht erschrecken. Mit Alt-Enter kommen Sie wieder zurück in den Fenster-Modus!

Wie beim C16 muss man den Bildschirm unter DOS für die Grafikprogrammierung erstmal vom Textmodus auf den Grafikmodus umschalten. Der Vorteil gegenüber dem C16 ist, dass der herkömmliche PRINT-Befehl auch in den Grafikmodi funktioniert.

Die verfügbaren Grafikmodi in QBASIC spiegeln die Urgeschichte der Grafikkarten wieder. Die beiden Uralt-Grafikkartenstandards CGA und EGA lassen wir hier beiseite. Der VGA-Standard von ca. 1990 hingegen hat noch eine kleine Bedeutung darin, dass auch heutige Linux- und Windows-XP-Systeme in einem VGA-Grafikmodus starten. Es seien hier nur die beiden wichtigsten Grafikmodi aufgezählt:

Zwischen Text- und Grafikmodi schaltet man mit dem SCREEN-Befehl um. Für ein grafikorientiertes Programm wird man dies nur einmal und am Anfang machen, da die Grafikmodi gegenüber den Textmodi bei der Darstellung von Text keine Nachteile bieten (sieht man von der geringeren Auflösung des 13h-Modus ab).

SCREEN 0 Textmodus
SCREEN 12 640x480x16-Modus
SCREEN 13 320x200x256-Modus

Kleines Schmunzeln am Rande: Schauen Sie mal in der Hilfe unter Screen-Anweisung-> Bildschirmmodi nach. Ganz unten steht ein kleines Beispielprogramm und der Hinweis "Nur für Systeme mit Farbgrafikadapter". "Grafikadapter" ist der alte Name für "Grafikkarte". Und der Hinweis soll heissen: Wenn Ihr PC nur eine Textmodus- Karte und keine Grafikkarte hat, funktioniert das Beispiel nicht...

Einfache Grafikbefehle

Grundsätzlich: Wir werden uns hier nur mit 1-Seiten-Grafikprogrammierung beschäftigen. Was heisst "1-Seiten-Programmierung"? Können Sie sich an das Ufo-Beispiel im Grafik-Kapitel des C16-BASIC-Teils erinnern? Das hoppelte doch so komisch über den Bildschirm. Sah nicht wirklich gut aus. Das lag daran, dass wir versucht haben, mit nur einer s.g. Bildschirmseite Bewegung zu erzeugen. Das klappt nicht. Selbst, wenn wir einen sehr schnellen PC haben. Der Grund ist einfach: Das Auge schaut dem Computer beim Zeichnen der Figur zu. Auch wenn der Computer sehr schnell ist, werden wir im Mittel nur die Hälfte der Figur sehen, wenn der Computer die Figur gleich wieder löscht, sobald er sie gezeichnet hat. Den Effekt abmildern können wir also nur, wenn wir mindestens die zehnfache Zeit warten, die gebraucht wird, um die Figur zu zeichnen, bevor sie wieder gelöscht (und am nächsten Punkt wieder gezeichnet) wird. Das ist aber schlecht, weil wir auf diese Weise extrem Geschwindigkeit verlieren. Insofern ist der Shape-Befehl des C16 nur für sehr grobe Bewegungsraster zu gebrauchen, bei der die Figur sozusagen hüpft statt gleitet.

Die Lösung des Problems, eine Figur gleiten zu lassen, heisst: Mehrere Bildschirmseiten. Das heisst, wir stellen zusätzlichen Speicherplatz bereit, um den Bildschirm zweimal abzuspeichern. Das nennen wir die beiden "Bildschirmseiten". (Wenn Sie jetzt über das Thema "Grafik und Speicherplatz" stolpern, dann schauen Sie nochmal unter C16: Hochauflösende Grafik im unteren Teil des Kapitels "Grafik und Speicher" nach.) Einstweilen begnügen wir uns mit "Stehgrafiken" oder "Hüpf-Figuren" und brauchen nur 1 Bildschirmseite.

Übersicht über die Grafikbefehle

Befehl Syntax Gebrauch/Bedeutung
CLS CLS "Clear Screen". Löscht den Bildschirm, egal in welchem Screen-Modus.
PSET Einfach: PSET (x,y) Zeichnet einen Bildschirmpunkt an der Koordinate x,y. Beispielprogramm:
	'Sternenhimmel
	SCREEN 13
	FOR i = 1 TO 1000
	  x = RND * 320
	  y = RND * 200
	  PSET (x, y)
	  NEXT i
     

Ausführliche Syntax: PSET STEP (x,y),Farbe

Das Schlüsselwort "STEP" kann weggelassen werden. Wenn es dabei ist, werden x und y als Schrittweite relativ zur letzten gezeichneten PSET- Position gewertet. PSET (50,50) PSET STEP (0,20) PSET STEP (-20,0) PSET STEP (0,-20) PSET STEP (20,0) zeichnet also ein Viereck von der Ecke mit den Absolutkoordinaten (50,50) aus.

LINE LINE (x1,y1)-(x2,y2) Zeichnet eine Linie vom Punkt (x1,y1) bis zum Punkt (x2,y2). Funktioniert wie DRAW 1,X1,Y1 TO X2,Y2 beim C16.
Rechtecke und gemusterte Linien mit LINE zeichnen LINE (x1,y1)-(x2,y2),farbe,B

LINE (x1,y1)-(x2,y2),farbe,BF

LINE (x1,y1)-(x2,y2),farbe,B,musterwert

LINE (x1,y1)-(x2,y2),farbe,B zeichnet Rechteck von der linken oberen Ecke (x1,y1) bis zur unteren Ecke (x2,y2).

LINE (x1,y1)-(x2,y2),farbe,BF Rechteck ist ausgefüllt.

Kleines Beispielprogramm

        'Modern Art in Vierecken
        SCREEN 12
	xscreensize = 640
	yscreensize = 480
	FOR i = 1 TO 100
	  farbe = RND * 16
	  x1 = (RND - .1) * xscreensize
	  y1 = (RND - .1) * yscreensize
	  xsize = RND * xscreensize / 2
	  ysize = RND * yscreensize / 2
	  x2 = x1 + xsize
	  y2 = y1 + ysize
	  LINE (x1, y1)-(x2, y2), farbe, B,&hFF00
	  'Verzoegerungsschleife. Wert fuer 500 MHz-PIII. 
	  'Bei schnellerem PC anpassen.
	  FOR j = 1 TO 10000: NEXT j
	  'Auskommentiert: Nach jedem Viereck auf Tastendruck warten.
	  'WHILE INKEY$ = l>"": WEND
	  NEXT i
       
Wie Sie sehen, füllt sich der Bildschirm mit gestrichelten farbigen Vierecken. Die Strichelungen kommt durch die Angabe des letzten Werts zustande. Die Linie wird Abschnitte mit jeweils 16 Pixel eingeteilt und die 16-Bit-Zahl sagt, welche Pixel gesetzt sein sollen: Ist ein Bit auf 1, wird das Pixel gesetzt. Im Beispiel sind also immer die ersten 8 Pixel gesetzt und die zweiten 8 Pixel nicht. Eine Angabe von &HF0F0 würde die Strichlänge auf 4 Pixel verkürzen, &HCCCC auf 2 Pixel und &HAAAA auf ein Pixel. (Gelt, das ist jetzt ein bisserl Denksport???)
CIRCLE (Kreise, Ellipsen) Einfach: CIRCLE (x,y),rad,farbe Einfache Syntax: Zeichnet eine Kreis mit rad Radius um den Punkt x,y. Zum Zeichnen von Ellipsen und Kreissegmenten gibt es eine ähnlich längliche Syntax wie beim C16. Das kann man aber gut in der Online-Hilfe nachschlagen.
Farben, Farbpalette PALETTE USING - Befehl Wahrscheinlich haben Ihnen die 16, bzw. 256 Farben, die Sie mit dem Color-Befehl (siehe Kap. 26) hervorrufen können, noch nicht so ganz gefallen. Die Vorbelegung der 256 Farben ist eigentlich zu nichts zu gebrauchen.

Die 16, bzw. 256 auf dem Bildschirm gleichzeitig verfügbaren Farben nennt man auch die Farbregister. (In der QBASIC-Hilfe heissen sie reichlich unverständlich "Attribute"). Welche Farbe ein Farbregister anzeigt, kann man mit dem PALETTE-Befehl definieren. Und zwar aus 64 x 64 x 64 = 256K möglichen Farben. Die Auswahlfarben sind zusammengemischt aus Rot-, Grün- und Blau-Komponenten. Man nennt das das RGB-Farbmodell, das an vielen Orten in der Computergrafik seine Verwendung findet, auch bei den modernen Grafikkarten und Bildschirmmodi. Auch hier werden die Farben durch ihre RGB-Werte beschrieben, allerdings können hier alle Farben in der Regel gleichzeitig auf dem Bildschirm dargestellt werden, die Arbeit mit Farbregistern ist hier nicht mehr notwendig.

Alle Farbregister zusammen heissen auch die "Palette" (daher der QBASIC-Befehl). Jede der 16, bzw. 256 Farbregister der Palette wird also durch 3 64-Bit-Zahlen beschrieben. Jeder Farbwert (R, G oder B) wird allerdings in einem extra Byte abgelegt, so dass wir es bei der Definition der Farbe mit einer 3-Byte-Zahl zu tun haben. Es wäre natürlich nun einfach, wenn der PALETTE-Befehl von QBASIC einfach PALETTE,Farbregister,R-Wert, G-Wert,B-Wert heissen würde. Das wäre viel zu einfach. Microsoft hat es an dieser Stelle richtig schön kompliziert gemacht, so dass Sie bei den ersten Versuchen so gut wie nie mehr als eine Fehlermeldung auf den Bildschirm bekommen werden..

Am Besten, Sie versuchen das folgende Programm, das Ihnen eine Reihe von Blauwerten auf den Bildschirm malt:

        ' Blautoene
	DIM farb(256) AS LONG

	SCREEN 12
	PALETTE 0, 0
	f(0) = 0
	FOR i = 1 TO 15
	  farb(i) = 65536 * i * 4
	  NEXT i
	PALETTE USING farb(0)
	GOSUB zeichne
	END


	zeichne:
	CLS
	FOR i = 0 TO 15
	  LINE (1, 1 + i * 20)-(320, 19 + i * 20), i, BF
	  NEXT i
	RETURN
     

Wenn alles klappt, bekommen Sie 15 dunkel- bis hellblaue Querbalken.

Was passiert hier? Nun, als erstes kommt die Deklaration eines Arrays. Die Ergänzung "as long" kennen wir noch nicht. Sie ist aber wichtig. Systematisch werden wir uns mit solchen Ergänzungen später noch beschäftigen, einstweilen soll uns genügen, dass dies ein speziell "formatiertes" Array ist. Ohne diese Formatierung hagelt es nur Fehlermeldungen.

In farb() werden die Farbwerte für die 16 Farbregister gespeichert. Der Palette Using-Befehl weiter unten übernimmt dann die Werte dieses Arrays in die Farbregister. Allerdings stellt sich die Frage, warum farb() dann 256 Zellen enthält und nicht nur 16. Wieder eine böse Falle. Aus welchen technischen Gründen auch immer, der PALETTE-Befehl braucht für die VGA-Modi mindestens 128 Zellen. Da SCREEN 13 ohnehin 256 Zellen braucht, sollte das Paletten-Array am Besten immer 256 Zellen enthalten.

Der erste Palette-Befehl 0,0 sichert nur ab, dass Farbregister 0 weiterhin "Schwarz" ist, da dieses ja für Rand und Hintergrund verwendet wird. Dann geht's mit der Palettendefinitionsschleife los. Wie gesagt: Jeder Farbwert ein Byte, das Ganze eine 3-Byte-Zahl. In Hex ist das eine Zahl zwischen 0 und FFFFFFh. Wollen wir z.B. Knallrot, dann setzen wir den Rotwert auf FFh, die Grün- und Blauwerte auf null. Der Rotwert ist das niedrigste Byte, also ist das Ganze 0000FFh. Entsprechend ist Knallgrün 00FF00h und Knallblau FF0000h. FFFF00h wäre Türkis und 00FFFFh Gelb. FF00FFh ist Magenta, FFFFFFh Weiss. 777777h ist Grau, da hier alle Farbwerte gleich stark sind, aber unterhalb des Maximums. Genauso ist 770000h eben ein dunkles Rot.

Leider können wir aufgrund der 16-Bit-Restriktion der Hex-Verarbeitung von QBASIC das Ganze nicht einfach in Hexzahlen angeben. Also geben wir es in Form von 030201h = 256*256*3 + 256*2 + 1 an. Soweit so schön. Im Programm steht aber 65536*3 + 256*2 +1. Das ist doch das Gleiche, oder? Wieder Falle. Intern erzeugt 256*256 bei QBASIC einen Überlauf! Um QBASIC also dazu zu bewegen, einen 3-Byte-Wert zu erzeugen, muss man es ihm gleich einen 3-Byte-Wert explizit vor die Nase setzen und das ist 65536 = 10000h. Und damit klappt's dann. Puh!

Weil's so schön war, im SCREEN13 noch ein Beispielprogrämmchen:

	DIM farb(256) AS LONG

	SCREEN 13
	PALETTE 0, 0
	farb(0) = 0
	FOR i = 1 TO 63
	  'Magenta-Toene
	  farb(i) = 65536 * i + i
	  NEXT i
	FOR i = 64 TO 127
	  'Tuerkis-Toene
	  farb(i) = 65536 * (i - 64) + 256 * (i - 64)
	  NEXT i
	FOR i = 128 TO 191
	  'Grau-Toene
	  farb(i) = 65536 * (i - 128) + 256 * (i - 128) + i - 128
	  NEXT i
	PALETTE USING farb(0)
	GOSUB zeichne
	END


	zeichne:
	CLS
	FOR i = 0 TO 191
	  LINE (1, i)-(320, i), i, BF
	  NEXT i
	RETURN



     

Sprites

Das Stichwort "Sprites" war der Stromstoss für den Spieleprogrammierer vor 15 Jahren. Es handelt sich um kleine bewegte Bildchen, die über den Bildschirm gleiten oder hüpfen. Das beste Beispiel für ein "Sprite" ist der Mauscursor. Wahrscheinlich haben Sie sich noch keine Gedanken darüber gemacht, wie Sie einen Mauscursor programmieren würden, aber wenn Sie das Problem näher anschauen, wird es Ihnen nicht mehr so ganz einfach vorkommen:

Wie wir gesehen haben, müsste dieser ganze Vorgang so schnell passieren, dass er kürzer dauert, als ein Bruchteil der Verweildauer des Zeigers an der neuen Position. Ansonsten kann man den Ab- und Aufbau des Zeigers mit dem Auge mitverfolgen und das Bild "hoppelt". Oder man verwendet eben einen Extra-Speicherbereich, der sehr schnell in das aktuelle Bild ein- und ausgeblendet werden kann.

In den Zeiten des C16 konnte dieses schnelle Ein- und Ausblenden eigentlich nur mit Zusatzhardware realisiert werden, den s.g. Spritegeneratoren. Der beliebteste Homecomputer der damaligen Zeit, der C64, zeichnete sich durch seine leistungsfähigen schon eingebauten Spritegeneratoren aus.

In QBASIC auf einem heutigen PC sind "C64-like" Sprites überhaupt kein Problem. Das eingangs dieses Teils bei der Installation von QBASIC empfohlene Spiel "Mooncrap" illustriert das eindrucksvoll. Aber auch ist Sprite-Programmierung kein ganz einfaches Kapitel.

Entwurf von Sprites

Am Besten wäre es natürlich, man könnte Sprites in einem Malprogramm zeichnen und dann in QBASIC verwenden. Das geht auch. Aber wie man gängige Pixelgrafikdateiformate aus Programmiersprachen heraus beherrscht, das gehört noch nicht zu unserem Repertoire. Also werden wir uns einstweilen darauf beschränken, die Pixel per Programm zu speichern oder zu zeichnen.

Möglichkeiten:

  1. Wir zeichnen das Sprite mit den Zeichenbefehlen PSET, LINE und CIRCLE
  2. Wir speichern die Farbangaben pixelweise in DATA-Zeilen ab und zeichnen diese dann auf den Bildschirm

Die erste Vorgehensweise wurde in "kap115.htm">C16-Hochauflösende Grafik im Bereich "Beispielprogramm zu SSHAPE/GSHAPE" schon demonstriert. Die zweite Möglichkeit ist auch nicht schwer. Nehmen wir ein kleines Bild mit 8 x 10 Pixeln. Jedes Pixel hat einen Farbwert zwischen 0 und 255. Jeder Farbwert ist die Nummer des Paletteneintrags. Tipp: Um Ihr erstes Sprite mittels DATA zu entwerfen, schreiben Sie sich zuerst ein kleines Progrämmchen, das Ihnen übersichtlich die Farbe der voreingestellten Paletteneinträge zeigt, falls Sie sich nicht jedes Mal die PALETTE selbst vordefinieren wollen.

Sie müssen die nun folgenden Programme nicht abtippen. Sie können auch einfach die Programmtexte in Ihrem Browser markieren und in einer Textdatei abspeichern, die Sie dann mit QBASIC wieder öffnen.

Meine Version:


'Zeigt Screen13-Defaultpalette an von 0 bis 100 SCREEN 13 FOR i = 0 TO 100 COLOR i PRINT i MOD 10; IF i MOD 10 = 0 THEN PRINT " "; i NEXT i

Nun kanns los gehen. Meine Zeichnung sieht so aus:


'----------------------------------------
'Beispielprogramm zum Generieren von Sprites 
'mit DATA-Werten
'----------------------------------------

GOTO Main

'Zeigt Screen13-Defaultpalette an
ZeigePalette:
SCREEN 13
FOR i = 0 TO 100
COLOR i
PRINT i MOD 10;
IF i MOD 10 = 0 THEN PRINT "   "; i
NEXT i
END

Spritedaten:
DATA 0,0,0,0,0,0,0,0
DATA 0,0,0,1,1,0,0,0
DATA 0,0,1,1,1,1,0,0
DATA 0,1,2,1,1,2,1,0
DATA 0,1,1,2,1,1,1,0
DATA 0,1,1,2,1,1,1,0
DATA 0,1,4,1,1,4,1,0
DATA 0,0,1,4,4,1,0,0
DATA 0,0,0,1,1,0,0,0
DATA 0,0,0,0,0,0,0,0
xsize = 8
ysize = 10
RETURN

Zeichnesprite:
RESTORE Spritedaten
FOR iy = y TO y + ysize - 1
  FOR ix = x TO x + xsize - 1
    READ farbe
    PSET (ix, iy), farbe
    'PRINT ix, iy, farbe
    NEXT ix
   NEXT iy
WHILE (INKEY$ = ""): WEND
RETURN

Main:
SCREEN 13
x = 100
y = 100
GOSUB Spritedaten
GOSUB Zeichnesprite
END

Ich habe im Unterprogramm "ZeichneSprite" eine Zeile dringelassen, die auskommentiert ist, die ich aber zum Debuggen verwendet habe. So. Und wie sieht Ihr Sprite aus?

Bewegung der Sprites auf schwarzem Hintergrund

Das Programm kann man leicht erweitern, um das Sprite herumzubewegen. Prinzipiell. wir berücksichtigen dabei allerdings nicht, was unter dem Sprite eventuell angezeigt war und nun von ihm überschrieben wird. Wir schreiben noch eine zweite Routine namens "LoescheSprite" und rufen beide dann wechselnd auf.

Hier mein Sprite, wie es als Spielball zwischen den Bildschirmrändern hin- und herfliegt:


'----------------------------------------
'Beispielprogramm zum einfachen Bewegen von 
'Sprites durch Neuzeichnen
'----------------------------------------

GOTO Main


'Zeigt Screen13-Defaultpalette an
ZeigePalette:
SCREEN 13
FOR i = 0 TO 100
COLOR i
PRINT i MOD 10;
IF i MOD 10 = 0 THEN PRINT "   "; i
NEXT i
END

Spritedaten:
DATA 0,0,0,0,0,0,0,0
DATA 0,0,0,1,1,0,0,0
DATA 0,0,1,1,1,1,0,0
DATA 0,1,2,1,1,2,1,0
DATA 0,1,1,2,1,1,1,0
DATA 0,1,1,2,1,1,1,0
DATA 0,1,4,1,1,4,1,0
DATA 0,0,1,4,4,1,0,0
DATA 0,0,0,1,1,0,0,0
DATA 0,0,0,0,0,0,0,0
xsize = 8
ysize = 10
RETURN

Zeichnesprite:
RESTORE Spritedaten
FOR iy = y TO y + ysize - 1
  FOR ix = x TO x + xsize - 1
    READ farbe
    PSET (ix, iy), farbe
    'PRINT ix, iy, farbe
    NEXT ix
   NEXT iy
RETURN

LoescheSprite:
LINE (x, y)-(x + xsize - 1, y + ysize - 1), 0, BF
RETURN

Warte:
FOR j = 1 TO 200: z = SIN(1!): NEXT j
RETURN


Main:
SCREEN 13
x = 1
y = 1
xdir = 1
ydir = 1
GOSUB Spritedaten
WHILE (INKEY$ = "")
  GOSUB Zeichnesprite
  GOSUB Warte
  GOSUB LoescheSprite
  x = x + xdir
  y = y + ydir
  IF (x > 320 OR x < 0) THEN xdir = -xdir
  IF (y > 200 OR y < 0) THEN ydir = -ydir
  WEND
END

Wenn Sie nichts sehen sollten, es zu langsam oder zu schnell geht, dann müssen Sie die Zahl "200" in der Routine "Warte" anders einstellen.

Sie sehen, dass das Hauptprogramm "Main" ganz einfach geblieben ist - dank Unterprogramme.

Bewegung der Sprites auf beliebigem Hintergrund

Natürlich ist das noch kein gutes Spriteprogramm. Das Sprite löscht auf seiner Spur alles weg und es bewegt sich zwar recht schnell, aber etwas ruckelig. Als Nächstes lernen wir, wie das Sprite sich den Hintergrund merkt. Dazu gibt es eine einfache Anweisung:
GETSyntax: GET (x1,y1)-(x2,y2),buf
buf ist ein Array der Grösse xsize*ysize, das ganz am Anfang des Programms mit DIM deklariert werden muss. GET speichert nun den Bildschirmbereich (x1,y1)-(x2,y2) in buf.
PUTSyntax: PUT (x,y),buf,flag
Malt den Bildausschnitt von buf an die Stelle (x,y). "flag" ist ein Schlüsselwort, das den Malmodus bezeichnet. Die möglichen Modi sind: AND, OR, PSET, PRESET oder XOR. Weitere Informationen finden Sie dazu in der Online-Hilfe von Qbasic.

Für unseren Zweck ist bei PUT nur der Modus PSET relevant, der einfach das neue Bild über den alten Hintergrund drübermalt und sich nicht drum schert, was da schon steht. Die anderen Modi verknüpfen in irgendeiner Form die Farben des Hintergrunds mit den Farben von buf.

Übung 1: Bauen Sie eine Routine "BewegeSprite" mit den Befehlen Get, Put und der schon bekannten Routinen "ZeichneSprite". Die Routine "LoescheSprite" werden Sie nicht mehr brauchen. Die Routine "BewegeSprite" soll das Sprite von (x1,y1) nach (x2,y2) bewegen. folgendes ist zu tun:

Viel Spass!

Meine Lösung lautet:


'-------------------------------------------------
'Beispielprogramm zum einfachen Bewegen von
'Sprites ueber einen Hintergrund durch Neuzeichnen
'-------------------------------------------------

DIM BHG(9 * 11 + 1)

GOTO Main


'Zeigt Screen13-Defaultpalette an
ZeigePalette:
SCREEN 13
FOR i = 0 TO 100
COLOR i
PRINT i MOD 10;
IF i MOD 10 = 0 THEN PRINT "   "; i
NEXT i
END

Spritedaten:
DATA 0,0,0,0,0,0,0,0
DATA 0,0,0,1,1,0,0,0
DATA 0,0,1,1,1,1,0,0
DATA 0,1,2,1,1,2,1,0
DATA 0,1,1,2,1,1,1,0
DATA 0,1,1,2,1,1,1,0
DATA 0,1,4,1,1,4,1,0
DATA 0,0,1,4,4,1,0,0
DATA 0,0,0,1,1,0,0,0
DATA 0,0,0,0,0,0,0,0
xsize = 8
ysize = 10
RETURN

ZeichneSprite:
RESTORE Spritedaten
FOR iy = y TO y + ysize - 1
  FOR ix = x TO x + xsize - 1
    READ farbe
    IF farbe = 1 THEN farbe = 53
    IF farbe = 2 THEN farbe = 46
    PSET (ix, iy), farbe
    'PRINT ix, iy, farbe
    NEXT ix
   NEXT iy
RETURN

LoescheSprite:
LINE (x, y)-(x + xsize - 1, y + ysize - 1), 0, BF
RETURN

Warte:
FOR j = 1 TO 200: z = SIN(1!): NEXT j
RETURN

ZeichneHintergrund:
FOR ix = 0 TO 320 STEP 20
  LINE (ix, 1)-(ix, 200), 6
  NEXT ix
RETURN

SpeichereBHG:
' BHG = BildHinterGrund
rspeich = 0
IF (x > 0 AND y > 0 AND x + xsize - 1 < 320 AND y + ysize - 1 < 200) THEN
  GET (x, y)-(x + xsize - 1, y + ysize - 1), BHG
  rspeich = 1
  END IF
BHGinit = 1'Zeigt, dass BHG sinnvolle Werte enthaelt
RETURN

MaleBHG:
IF (x > 0 AND y > 0 AND x + xsize - 1 < 320 AND y + ysize - 1 < 200) THEN
  PUT (x, y), BHG, PSET
  END IF
RETURN

BewegeSprite:
'...von (x1,y1) nach (x2,y2)
x = x1: y = y1
IF (BHGinit = 1) THEN GOSUB MaleBHG
'LOCATE 20, 20: PRINT 1;
'WHILE (INKEY$ = al>""): WEND
x = x2: y = y2
GOSUB SpeichereBHG
'LOCATE 20, 20: PRINT 2;
'WHILE (INKEY$ = al>""): WEND
IF rspeich = 1 THEN GOSUB ZeichneSprite
'LOCATE 20, 20: PRINT 3;
'WHILE (INKEY$ = al>""): WEND
RETURN


Main:
SCREEN 13
BHGinit = 0
GOSUB ZeichneHintergrund
x0 = 1
y0 = 1
xdir = 1
ydir = 1
x1 = x0: y1 = y0
GOSUB Spritedaten
WHILE (INKEY$ = "")
  x2 = x0
  y2 = y0
  GOSUB BewegeSprite
  x1 = x0: y1 = y0 'Zwischenspeichern der alten Koordinaten --> BHG!
  GOSUB Warte
  IF (x0 + xdir > 320 - xsize OR x0 + xdir < 0) THEN xdir = -xdir
  IF (y0 + ydir > 200 - ysize OR y0 + ydir < 0) THEN ydir = -ydir
  x0 = x0 + xdir
  y0 = y0 + ydir
  WEND
END

Ich habe hier wieder ein paar auskommentierte Debug-Zeilen dringelassen. "Main" habe ich etwas schöner gestaltet, indem nun die Grenzenprüfung die Grösse des Sprites berücksichtigt. Beachten Sie, dass "SpeichereBHG" einen Rückgabewert rspeich liefert, der informiert, ob der Hintergrund überhaupt abgespeichert werden konnte. Ist dies nicht der Fall, dann darf auch nichts Neues gezeichnet werden. Das sichert ab, dass nicht plötzlich die Ordnung von "Speichern" und "Zeichnen" durcheinander kommt.

Übung 2: Modifizieren Sie das Programm von oben (oder ihr eigenes!) so, dass das schwarze Viereck um das eigentliche Sprite herum verschwindet. Das ist ganz einfach und erfordert nur eine Halbzeile!

Übung 3: Etwas schwieriger, aber nicht so schwierig, wie es scheint: Schreiben Sie ein Programm, das mehrere identische Sprites gleichzeitig über den Bildschirm fliegen lässt!

Übung 4: Modifizieren Sie das Programm so, dass Sie die Sprites nicht immer neu mit PSET auf den Bildschirm pixeln, sondern ebenfalls mit PUT übertragen. Gewinnt das Programm an Geschwindigkeit?

Übung 5: Fügen Sie nun ein zweites, andersartiges Sprite ein, dessen Bewegung Sie mit den Cursortasten steuern können. Nennen wir dieses Sprite "Janosch". Und die anderen Sprites sind Fliegen. Geben Sie einen Beep aus, (Anweisung BEEP), wenn eine Fliege Janosch trifft. Hurra! Wir haben ein richtiges Action Game!

Ausblick

Wenn Sie sich mehr für Grafikprogrammierung mit QBASIC, insbesondere Sprites, interessieren: Es gibt eine Menge guter Literatur zum Thema auf qbasic.de

.