Dieser Text
war ursprünglich als weiteres Spielegerüst für das Buch „Spiele programmieren
mit Blitz Basic“ von René Meyer vorgesehen. Aus Platz- und Zeitgründen wurden
weder das Kapitel noch das Spiel beendet. Der aktuelle Status verdeutlicht
aber, wie das Prinzip umgesetzt wird, und läßt sich leicht erweitern.
Kaum ein Spiel
wurde häufiger umgesetzt als „Tetris“. Obwohl es erst Mitte der 80er Jahre
entstand, also viel später als Pioniere wie „Pong“ und „Space Invaders“,
erfüllt es die Voraussetzungen für ein echtes Kultspiel - blitz(basic)schnell
zu erlernen und doch lange fesselnd.
Ich rate Ihnen
nicht unbedingt, ein „Tetris“-Spiel zu veröffentlichen. Es gibt weit über
hundert Versionen allein für den PC, so daß eine Neuerscheinung kaum jemanden
interessieren wird. (Falls Sie es doch tun - nennen Sie Ihr Werk nicht
„Tetris“; der Name ist geschützt.) Als Fingerübung ist die Spielidee jedoch
interessant und leicht umzusetzen.
Die Regeln
dürften klar sein: In einen Schacht fallen von oben unregelmäßig geformte
Klötzer. Die Aufgabe ist es, sie so zu drehen und nach links und rechts zu
bewegen, daß sie komplette Linien ergeben. Ist eine Linie komplett, also
durchgängig gefüllt, verschwindet sie.
Es bietet sich
an, den Schacht in einem Dim-Feld
abzulegen. Er hat eine feste Größe von 10 Feldern waagerecht und 20 Feldern
senkrecht:
Dim Schacht(10,
20)
Als Hintergrund
laden wir ein Bild:
bild =
LoadImage("racetrack.bmp")
ResizeImage
bild, 1024, 768
·
Steinformen
In
„Tetris“ gibt es sieben verschiedene Steine. Jeder Stein besteht aus vier
Quadraten, die jeweils anders angeordnet sind - als langer Strich, als „T“, als
„L“, als umgekehrtes „L“, als „S“, als „Z“ und als großes Viereck.
Da
wir die Formen der Steine mit dem jeweiligen Zustand des Schachtes abgleichen müssen,
um Zusammenstöße und Überschreitungen des Randes zu vermeiden, bietet es sich
an, keine fertigen Graphiken zu verwenden, sondern jeden Stein aus seinen vier
Quadraten zusammenzusetzen.
Jeder
Stein ist maximal vier Quadrate breit und zwei hoch. Um uns das Programmieren
zu erleichtern, legen wir die Steine in 4x2 große Areale ab. Für den „T“-Stein
etwa so:
0100
1110
Auch
hier bietet es sich an, die Form der sieben Steine in einem Dim-Feld abzulegen. Wir wählen dazu
ein 7 x 4 x 2 großes Dim-Feld.
Wir legen die Formen der Steine in Data-Zeilen ab ...
;
Steinformen definieren
Data
0,0,1,0, 1,1,1,0 ; L
Data
1,0,0,0, 1,1,1,0 ; umgekehrtes L
Data
0,1,1,0, 1,1,0,0 ; S
Data
1,1,0,0, 0,1,1,0 ; Z
Data
0,1,0,0, 1,1,1,0 ; T
Data
1,1,0,0, 1,1,0,0 ; Quadrat
Data
1,1,1,1, 0,0,0,0 ; I
...
und lesen sie in das Feld ein:
;
Steinformen einlesen
Dim
Steinform(7, 4, 2)
For
X=1 To 7
Read Steinform(X, 1, 1)
Read Steinform(X, 2, 1)
Read Steinform(X, 3, 1)
Read Steinform(X, 4, 1)
Read Steinform(X, 1, 2)
Read Steinform(X, 2, 2)
Read Steinform(X, 3, 2)
Read Steinform(X, 4, 2)
Next
(Wir
hätten zwei verschachtelte For-Schleifen
verwenden können, doch das hätte nur wenig Aufwand gespart - die Arbeit für den
Computer sogar erhöht.)
·
Steinfarben
Zum
Darstellen der Steine würden wir normalerweise Graphikquadrate einlesen. Da wir
einfarbige Steine verwenden, gehen wir anders vor: Wir erstellen die
sieben Graphiken zur Laufzeit mit CreateImage(). Dann setzen wir den Bildpuffer nacheinander auf die Graphiken
und färben sie ein:
;
Farbquadrate erstellen
Dim
Stein(7)
For
I=1 To 7
Stein(I) = CreateImage(32, 32)
SetBuffer ImageBuffer(Stein(I))
Read R,G,B
Color R,G,B
Rect 0, 0, 32, 32, 1
Next
Die
Farbcodes lesen wir aus Data-Zeilen:
;
Farben der Steine definieren
Data
153,51,204 ; L - violett
Data
255,255,255 ; umgekehrtes L - weiß
Data
0,255,0 ; S - grün
Data
0,255,255 ; Z - hellblau
Data
127,127,0 ; T - olivgrün
Data
0,0,127 ; Quadrat - blau
Data
255,0,0 ; I - Rot
Die
Kombination ist nicht sonderlich schön; ich habe mich an dem Originalspiel von
1986 orientiert. Für ein Spiel, das veröffentlicht werden soll, ist das
Verwenden von „ordentlichen“ Graphikquadraten unbedingt empfehlenswert.
Mit
diesen Vorbereitungen haben wir den Zahlen 1-7 eine Steinform und eine Steinfarbe
zugewiesen. Zeit für einen Probelauf.
·
Spielschleife
Wir
schreiben eine kleine Spielschleife. Sie druckt das Hintergrundbild und den
Rahmen für den Schacht. Na gut, in weiser Voraussicht können wir auch gleich
den Inhalt des Schachts drucken:
;
Schacht zeichnen
For
I=1 To 10
For J=1 To 20
If Schacht(I,J) <> 0 Then DrawImage
Stein(Schacht(I,J)), 318 + 32*I, 30 + 32*J
Next
Next
Der
ist freilich noch leer, doch wenn wir einige Felder mit Testdaten (1-7) füllen
...
Schacht(10,
20) = 7
...
würden, könnten wir an eine beliebige Stelle Quadrate plazieren. So werden wir
es später mit den Steinen machen, die unten gelandet sind und sich nicht mehr
bewegen.
Ein
bißchen Interaktivität fehlt noch. Wir tragen einige Codezeilen probehalber
ein. Mit den Tasten [1] bis [7] wählen wir einen Stein aus, ...
A
= GetKey()
If
(A>48) And (A<56) Then Farbe = A - 48
...
der an einer festen Position angezeigt wird.
For
I=1 To 4
If Steinform(Farbe, I, 1) <> 0 Then
DrawImage Stein(Farbe), 286 + 32*X + 32*I, 30 + 32*Y
If Steinform(Farbe, I, 2) <> 0 Then
DrawImage Stein(Farbe), 286 + 32*X + 32*I, 62 + 32*Y
Next
Damit
bereiten wir uns auf Folgeabschnitte vor:
1.
Wir stellen sicher, daß wir die Formen der sieben Steine richtig festgelegt
(codiert) haben.
2.
Wir stellen sicher, daß die Steine korrekt und an der richtigen Stelle
angezeigt werden.
Das
ganze Programm:
;
TETRIS1.BB
Graphics
1024,768
bild
= LoadImage("c:\basic\racetrack.bmp")
ResizeImage
bild, 1024, 768
Dim
Schacht(10, 20)
;
Steinformen definieren
Data
0,0,1,0, 1,1,1,0 ; L
Data
1,0,0,0, 1,1,1,0 ; umgekehrtes L
Data
0,1,1,0, 1,1,0,0 ; S
Data
1,1,0,0, 0,1,1,0 ; Z
Data
0,1,0,0, 1,1,1,0 ; T
Data
1,1,0,0, 1,1,0,0 ; Quadrat
Data
1,1,1,1, 0,0,0,0 ; I
;
Steinformen einlesen
Dim
Steinform(7, 4, 2)
For
X=1 To 7
Read Steinform(X, 1, 1)
Read Steinform(X, 2, 1)
Read Steinform(X, 3, 1)
Read Steinform(X, 4, 1)
Read Steinform(X, 1, 2)
Read Steinform(X, 2, 2)
Read Steinform(X, 3, 2)
Read Steinform(X, 4, 2)
Next
;
Farben der Steine definieren
Data
153,51,204 ; L - violett
Data
255,255,255 ; umgekehrtes L - weiß
Data
0,255,0 ; S - grün
Data
0,255,255 ; Z - hellblau
Data
127,127,0 ; T - olivgrün
Data
0,0,127 ; Quadrat - blau
Data
255,0,0 ; I - Rot
;
Farbquadrate erstellen
Dim
Stein(7)
For
I=1 To 7
Stein(I) = CreateImage(32, 32)
SetBuffer ImageBuffer(Stein(I))
Read R,G,B
Color R,G,B
Rect 0, 0, 32, 32, 1
Next
SetBuffer
BackBuffer()
Farbe
= 1 ; Test
X
= 1 ; Test
Y
= 1 ; Test
Repeat
; *** Spielschleife ***
; Hintergund zeichnen
Color 0, 0, 30
DrawImage bild, 0, 0
Rect 350, 62, 320, 640, 1
; Schacht zeichnen
For I=1 To 10
For J=1 To 20
If Schacht(I,J) <> 0 Then DrawImage
Stein(Schacht(I,J)), 318 + 32*I, 30 + 32*J
Next
Next
;Test = Stein per Tastendruck festlegen
A = GetKey()
If (A>48) And (A<56) Then Farbe = A -
48
; Stein zeichnen
For I=1 To 4
If Steinform(Farbe, I, 1) <> 0 Then
DrawImage Stein(Farbe), 286 + 32*X + 32*I, 30 + 32*Y
If Steinform(Farbe, I, 2) <> 0 Then
DrawImage Stein(Farbe), 286 + 32*X + 32*I, 62 + 32*Y
Next
Flip
Until
KeyHit(1)
·
Nächsten Stein ausdenken
Zu Spielbeginn
muß ein Stein „erstellt“ werden. Aber auch, wenn ein Stein unten gelandet ist,
muß ein neuer erscheinen. Wir führen die Variable Aktiv ein. Sie merkt sich, ob und
welcher Stein derzeit im Rennen ist. Hat sie den Wert 0, muß ein Stein erzeugt
werden. Es liegt nahe, eine zufällige Auswahl zu treffen. Doch beim
Originalspiel erscheinen manche Steine mehr und manche weniger häufig. Michael
Gonzales, der Verfasser einer „Tetris FAQ“, hat's mühsam gezählt:
Stein |
Code |
Häufigkeit |
L |
1 |
11% |
umgekehrtes L |
2 |
11% |
S |
3 |
19% |
Z |
4 |
10% |
T |
5 |
19% |
Quadrat |
6 |
18% |
I |
7 |
12% |
(Übrigens: Die
FAQ enthält auch eine genaue Aufschlüsselung der Punkteverteilung, um die wir
uns hier nicht kümmern. Der Text ist unter anderem auf der Seite www.mogelpower.de zu finden.)
Das lösen wir auf
diese Weise:
; Stein
"ausdenken"
If Aktiv = 0
Then
Zufall = Rand(1,100)
Aktiv = 1
If Zufall > 11 Then Aktiv = 2
If Zufall > 11 + 11 Then Aktiv = 3
If Zufall > 11 + 11 + 19 Then Aktiv = 4
If Zufall > 11 + 11 + 19 + 10 Then Aktiv =
5
If Zufall > 11 + 11 + 19 + 10 + 19 Then
Aktiv = 6
If Zufall > 11 + 11 + 19 + 10 + 19 + 18
Then Aktiv = 7
EndIf
Solche Tests
sollten getrost in einem separaten Programm ablaufen (PROZENTTEST.BB), um sicherzustellen, daß die
Auswahl wirklich korrekt arbeitet. Nichts ist peinlicher, als ein
„Tetris“-Spiel zu veröffentlichen, bei dem wegen eines Fehlers ein Stein nie
erscheint.
Als weiteren Test
lassen wir im Sekundentakt einen Stein am oberen Rand erscheinen:
Farbe = Aktiv ;
Test
Delay 1000 ;
Test
Aktiv = 0 ;
Test
Folgende Schritte
müssen noch programmiert werden:
1. Drehen der Steine auf Tastendruck. Dafür ist es
empfehlenswert, das Aussehen jeder der vier Positionen in Data-Zeilen zu speichern. Beim
Drehen ist eine Kollision mit dem Rand und mit anderen Steinen zu vermeiden.
2. Herunterfallen des aktuellen
Steins. Ist er
unten angekommen, folgt der nächste. Die Kollision mit den Steinen am Boden
sollte bereits in Schritt 1 verhindert worden sein.
3. Das Überprüfen und Löschen vollständiger
Linien. Eine For-Schleife testet jede Linie, ob
alle Felder besetzt sind. Ist das der Fall, werden alle darüberliegenden Linien
um eine Zeile verschoben.