Schiebepuzzle

Fast alle 2D-Spiele für den PC, bei denen flächenfüllende Hintergründe benutzt werden, verwenden rechteckige Kacheln, aus denen sich komplexe Grafiken zusammensetzen lassen. Anhand eines Schiebepuzzles soll nun demonstriert werden, dass es auch anders geht; nämlich mit sechseckigen Bildelementen (Waben).

B. Olaf Rasch 03/2003

 Anregungen oder Tipps an B. Olaf Rasch
 
Einführung voriges Thema [ Top ] nächstes Thema

Einzelbilder gut in einem Editor entwerfen und abspeichern, zum anderen lassen sich auch Maus-Klicks eines Benutzers schnell lokalisieren. Abgesehen davon sind die Speicherorganisation und die Rastergrafik-Befehle eines Computers geradezu prädestiniert für den Umgang mit rechteckigen Bildelementen.

Das Spiel selbst ist relativ simpel: Ziel ist es, die nummerierten Waben in die richtige Reihenfolge zu bringen. Ein Feld lässt sich durch Maus-Klick verschieben, wenn es an das Leerfeld grenzt. Zur besseren Orientierung ist die Nummer von bereits korrekt positionierten Waben blau gefärbt; stimmt die Position noch nicht, ist die Nummer rot.

 

Um die Optik etwas interessanter zu machen, sorgen wir für eine regenbogenfarbene Titelleiste. Außerdem soll auch die Form nicht rechteckig sein, sondern sich an die Wabenstruktur anpassen

Da der Sourcecode nicht sonderlich umfangreich ist und auch nicht schwer zu verstehen sein dürfte, werde ich weiter unten auch nur auszugsweise auf ihn eingehen. Zunächst möchte ich daher auf die Verwaltung von wabenförmigen Feldern eingehen.
 
Spielpläne mit hexagonalen Feldern voriges Thema [ Top ] nächstes Thema

Das Hauptproblem bei der Organisation von Wabenfeldern ist der horizontale Versatz der Zeilen (bei stehenden Waben. Haben wir es mit liegenden Waben zu tun, existiert ein waagrechter Versatz in den Spalten).

Nehmen wir als Beispiel eine Gruppe von stehenden Waben und betrachten die Nachbarschaft eines beliebigen Feldes

Abb.1
Spielplan: optische Darstellung

Wie du siehst, verwende ich hier bereits ein X/Y-Koordinatensystem, um die einzelnen Felder definieren zu können. Das funktioniert, weil das Koordinatensystem 'schräg' ausgerichtet ist.

In ein rechteckiges Raster übertragen, hätte das ganze dann folgendes Aussehen:

Abb.2
Map: Speicherorganisation

Man erkennt, dass nicht alle Felder benutzt werden; rechts oben und links unten befinden sich Bereiche, die für die Verwaltung irrelevant sind. Bei größeren Strukturen verschenkt man durch die schräge Ausrichtung der Spalten eventuell Speicherplatz.

Es ist jedoch auch eine zweite Art der Koordinatenzuteilung möglich. Anstatt die Spalten durchweg auf einer schrägen Linie zu betrachten, kann man die Spaltenfelder auch im 'Zickzack' betrachten; dadurch wird gerade bei rechteckigen Wabenstrukturen relativ wenig Speicherplatz verschenkt.
Sehen wir uns das Beispiel an, bei dem die Felder 'A' und 'B' beide in der Spalte X=1 liegen:

Abb.3
Optik: Zickzack-Spalten

Hier sähe die Speicherorganisation dann so aus:


r

Abb.4
Map: 'A' und 'B' untereinander

Bisher haben wir nur (auf der Spitze) stehende Waben untersucht, das ganze klappt aber analog auch mit liegenden Waben. Die Felder 'A' und 'B' befinden sich hier beide in der Zeile Y=1:

Abb.5
Optik: Zickzack-Reihen

Und auch dazu wieder die Darstellung im Speicher:

Abb.6
Map: 'A' und 'B' nebeneinander

Während bei rechteckigen Kacheln jedes Feld 8 Nachbarn besitzt, haben Wabenfelder lediglich 6 Nachbarn, d.h. man darf in der Map (Spielplan im Speicher) nicht alle Felder bei der Nachbarschaftsprüfung mit einbeziehen.

Wie in Abb.6 zu sehen, gibt es zwei Arten von Feldern. Typ 'A': Eine Spielfigur, die hier steht, darf auf alle Nachbarfelder gehen außer nach links und rechts unten. Typ 'B': Eine Spielfigur, die hier steht, darf auf alle Nachbarfelder gehen außer nach links und rechts oben.

Auch im Speicher von stehenden Waben (siehe Abb.4) müssen wir beide Arten unterscheiden. Typ 'A': Eine Spielfigur, die hier steht, darf auf alle Nachbarfelder gehen außer nach oben und unten rechts. Typ 'B': Eine Spielfigur, die hier steht, darf auf alle Nachbarfelder gehen außer nach oben und unten links.

Diese Einschränkungen muss man natürlich berücksichtigen, wenn man den Bewegungsalgorithmus für Spielfiguren codet.

Ob man nun für ein Spiel stehende oder liegende Waben wählt, ist letztlich vom persönlichen Geschmack des Programmierers abhängig; in der optischen Wirkung und der Speicherverwaltung sind beide Varianten wohl gleichwertig.

Sollen Spielfiguren jedoch auch mit der Tastatur (Cursortasten) steuerbar sein, dann sind liegende Waben zu empfehlen (siehe Abb.5): nach oben und unten geht's wie gehabt mit CursorUp/CursorDown, seitlich (nach unten) geht's mit CursorLeft/CursorRight und seitlich nach oben gehts mit Shift CursorLeft/CursorRight.

Ein Joystick ist da wesentlich unproblematischer, weil sich dessen schräge Auslenkung relativ einfach abfragen lässt.

 
  Fenster mit benutzerdefiniertem Umriss voriges Thema [ Top ] nächstes Thema

Um gewisse Teilflächen einer Form unsichtbar bzw. transparent erscheinen zu lassen, bedienen wir uns der API, genauer gesagt der Funktion SetWindowRgn.

Wie ihr Name schon andeutet, wird dem Fenster mitgeteilt, dass es nur die Flächen darstellen soll, die innerhalb einer bestimmten Region liegen. Diese Region müssen wir also zuerst einmal definieren.
Da es sich in unserem Fall um ein Polygon handelt (es gibt auch kreisförmige und rechteckige Regionen), stellen wir dieses Polygon in der Sub GetGameForm her: wir füllen ein POINT-Array mit den benötigten X/Y-Koordinaten, die den Umriss unseres Fensters definieren.

Daraus erzeugen wir mithilfe der API-Funktion CreatePolygonRgn eine temporäre Region, die wir dann beim Aufruf von SetWindowRgn übergeben können.

 
  Sechseckige Bildelemente voriges Thema [ Top ] nächstes Thema

Eigentlich kann man getrost alle Grafiken, die keine rechteckige Form haben, als Sprites bezeichnen. Solche Bilder sind zwar ebenfalls als rechteckige Rastergrafik im Speicher abgelegt, sie besitzen jedoch Pixel einer bestimmten Farbe X (meistens RGB 0), die später bei der Darstellung transparent gezeichnet (d.h. gar nicht erst in Erscheinung treten) sollen.

Grundsätzlich lassen sich solche Sprites beliebiger Größe am einfachsten mit der .PaintPicture-Methode eines Form- oder Picture-Objektes realisieren (Einzelheiten/Parameter siehe VB-Hilfe).

Hierzu wird auf der Zielfläche innerhalb eines rechteckigen Bereichs zuerst die Form des Sprites 'ausmaskiert', d.h. es wird durch eine logische Operation (AND) 'Platz' geschaffen, indem alle Pixel, auf denen das Sprite erscheinen soll, auf 0 gesetzt werden.

In einem zweiten Schritt wird durch eine logische Operation (OR) die eigentliche Sprite-Grafik 'eingesetzt'. Durch diese OR-Verknüpfung wird die sichtbare Spritefläche dargestellt (0 OR Sprite-Pixel = Sprite-Pixel), während an den 'transparenten' Stellen der Zielbereich unverändert bleibt (Hintergrund-Pixel OR 0 = Hintergrund-Pixel).

Dieser Sachverhalt lässt sich wohl besser nachvollziehen, wenn man sich im Projekt in der IDE die Form (frmMain) von HexPuzzle betrachtet:

Abb.7
Sprite-Elemente auf der Form

Hier erkennt man links die Maske, mit der man den Raum 'ausstanzt', an dem später das Sprite erscheinen soll. Die (transparenten) Bereiche in den Ecken müssen &HFFFFFF (weiß) sein, damit durch die AND-Operation die Zielgrafik dort unverändert bleibt (Hintergrund-Pixel AND -1 = Hintergrund-Pixel).

In der Mitte und rechts haben wir schließlich die beiden benötigten Sprites (Leerfeld und Wabe).

 
  Realisierung einer modifizierten Titelleiste voriges Thema [ Top ] nächstes Thema

Wenn man, wie in unserem Fall, eine Titlebar simulieren will, sollte man darauf achten, dass die Titelleiste einen Randabstand von 4 Pixeln hat.

Der Farbverlauf entsteht durch senkrechte Linien, deren RGB-Komponenten sukzessive von der linken Farbe zu der rechten Farbe übergeblendet werden. In HexPuzzle wird von Blau (RGB = 0000FF) nach Gelb (RGB = FFFF00) geblendet.

Hier der verallgemeinerte Code:

 'x, y, b und h sind die Maße der Titelleiste
Dim i As Long, j As Long, d As Single

j = b - 1
d = 255 / j
For i = 0 To j
  frmMain.Line (x + i, y)- (x + i, y + h - 1), _
         RGB2Col(CSng(i) * d, CSng(i) * d, 255 - CSng(i) * d)
Next

Die Funktion RGB2Col erledigt die Konvertierung der Farbanteile in einen von der .Line-Methode benötigten Long-Wert:

Private Function RGB2Col(ByVal rcol As Long, ByVal gcol As Long, _
        ByVal bcol As Long) As Long

  'liefert die RGB-Farbe als Long-Wert
 RGB2Col = bcol * &H10000 + gcol * &H100 + rcol
End Function

Da es der Benutzer gewohnt ist, mithilfe der Titelzeile das Fenster verschieben zu können, müssen wir auch diese Funktionalität nachbilden. Dies geschieht über die Events Form_MouseDown, Form_MouseMove und Form_MouseUp. Hier der verallgemeinerte Code:

Private Dragging As Boolean 'Flag: Fenster wird bewegt
Private DownX As Long, DownY As Long

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, _
        X As Single, Y As Single)
  If (<X,Y in Titlebar-Bereich>) Then '---- Titelleiste
    DownX = X: DownY = Y
    Dragging = True
  End If
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, _
        X As Single, Y As Single)
  If (Dragging) Then
    '---- Fenster nachziehen
    Move Left + (X - DownX) * Screen.TwipsPerPixelX, _
         Top + (Y - DownY) * Screen.TwipsPerPixelY
  End If
End Sub
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, _
        X As Single, Y As Single)

  Dragging = False

End Sub

Da das Schiebepuzzle mit Zahlenfeldern nicht gerade eine geistige Herausforderung darstellt, wäre es auch denkbar, eine Grafik zu verwenden, die der Benutzer korrekt zusammensetzen muss. Hierbei kann man dann sogar auf das 'Zeichnen' der Feldrahmen verzichten und lässt die Sprites direkt aneinander stoßen. Die einzelnen Bildteile wären dann jedoch besser in einer ImageList aufgehoben, sie haben ja ohnehin alle die selbe Größe.

Zumindest konnte ich dir hoffentlich zeigen, dass sich bereits mit VB-Bordmitteln und wenigen API-Aufrufen Spiele mit ansprechender Optik schreiben lassen.

 
Download des Projekts voriges Thema [ Top ]   

Zum Abschluss können Sie sich hier das fertige Projekt herunterladen

  Download
SchiebePuzzle.zip
 (18,7 kB)
Downloadzeit: <1 Min. - 28.8k / <1 Min. - ISDN Downloads bisher: [ 5636 ]

Startseite | VB/VBA-Tipps | Projekte | Tutorials | API-Referenz | Komponenten | Bücherecke | VB-/VBA-Forum | VB.Net-Forum | DirectX | DirectX-Forum | Foren-Archiv | VB.Net | Chat | Links | Suchen | Stichwortverzeichnis | Feedback | Impressum

Seite empfehlen Bug-Report

Letzte Aktualisierung: Samstag, 15. März 2003