Fast jeder wird es kennen, das Windows-Spiel, bei dem man auf Minensuche geht. Die Rede ist von MineSweeper, ein vom Prinzip her recht einfach zu spielendes Spiel. Aber wie lässt sich solch ein Spiel in Visual Basic programmieren? Die Antwort erfahren Sie hier in unserem Tutorial.

Martin Walter 03/2002

Anregungen oder Tipps an Martin Walter
 
  Einleitung voriges Thema [ Top ] nächstes Thema

Wer kennt nicht dieses kleine Spiel, was einem einfach keine Ruhe gibt, bis man es geschafft hat und alle Minen gefunden hat. Dieses Tutorial soll Ihnen helfen, Ihren eigenen Minensucher zu programmieren.

Um das Spiel selbst zu programmieren, müssen wir natürlich zunächst selbst einmal alle Regeln und Äußerlichkeiten (Abb. 1) festhalten.


 Abb.1: Die MineSweeper-Oberfläche

Um zunächst bei den Äußerlichkeiten zu bleiben:
    1. Das Spielfeld ist unterteilt in einzelne Felder 
    2. Das Spielfeld hat eine Mindestbreite von 9 x 9 Feldern 
    3. Es wird die Gesamt-Minenzahl und die Zeit (in Sekunden) angezeigt 
    4. Es gibt eine "Neues Spiel" - Befehlsschaltfläche

Jetzt gehen wir einmal auf die Regeln ein, die wirklich ganz einfach sind. Es gibt ein Spielfeld, auf dem eine bestimmte Anzahl Minen verteilt wird. Die maximale Anzahl wird folgendermaßen berechnet: (Breite - 1) * (Höhe - 1). Diese Formel benötigen wir noch später, wenn wir ein benutzerdefiniertes Spielfeld erlauben.

Jetzt geht es an die Minenverteilung. Die Minen können überall auf dem Spielfeld angeordnet werden, dabei ruhig zwei oder mehr nebeneinander. Um diese Minen herum kommen aber Zahlen. Die Zahl auf diesem Feld wird von der Anzahl der Minen um dieses Feld bestimmt. Ein Beispiel (M = Mine):

1 1 1
1 M 1
1 1 1
Sollten jedoch zwei Minen irgendwie nebeneinander liegen, verändert sich das Bild natürlich:
1 2 2 1
1 M M 1
1 2 2 1

Die Zahl auf dem Feld sagt also aus, wie viele Minen um das Feld herumliegen. Das sind die wichtigsten Dinge, die restlichen Regeln erkläre ich noch beim Programmieren. Um nicht zu theoretisch zu bleiben, fangen wir jetzt auch gleich mal an.

 
  Erstellen der Oberfläche voriges Thema [ Top ] nächstes Thema
Wir erstellen zunächst die Oberfläche. Dazu brauchen wir erst einmal ein Formular und wir benötigen die Ressourcen (die Digital-Zahlen und die einzelnen Bilder für die Felder).

Wir erstellen ein neues Standard-EXE-Projekt und benennen Form1 in frmMain um. Wichtig ist vor allem, dass die ScaleMode-Eigenschaft auf 3 (Pixel) gesetzt wird!

Hier die Ressourcen:

  Abb. 2: Feld-Symbole   Abb. 3: Digital-Zahlen

Diese Bilder laden wir in Bildfelder (PictureBox: picMine und picTime). Jetzt müssen die folgenden Eigenschaften für die Bildfelder gesetzt werden:

  • AutoRedraw = True (das Bild wird im Arbeitsspeicher abgelegt) 

  • Appearance = 0 (keine 3D-Darstellung

  • BorderStyle = 0 (kein Rand) 

  • Visible = False (darf nicht auf dem Formular sichtbar sein

  • AutoSize = True (kann auch manuell gesetzt werden)

Nun platzieren wir ein drittes Bildfeld namens picPlay in die Mitte des Formulars. Für dieses setzen wir die gleichen Eigenschaften wie für picMine und picTime, bis auf Visible, denn wir wollen ja sehen was wir spielen ;-)

picPlay ist unser eigentliches Spielfeld. Auf ihm werden später die Felder gezeichnet. Um schon einmal die Oberfläche für unsere spätere Bedürfnisse anzupassen, fügen wir noch zwei kleinere Bildfelder ein, die folgende Eigenschaften besitzen:

  • AutoRedraw = True

  • Appearance = 0

  • BorderStyle = 0

  • BackColor = 0 (Schwarz)

  • Height = 25

  • Width = 41

Und in die Mitte oben kommt noch unsere Befehlsschaltfläche (CommandButton) cmdNew hinzu. Sie bekommt das Bild eines hübschen kleinen Smileys (im Verzeichnis "Common\Graphics\Icons\Misc\", Datei "FACE02.ICO"). Dabei nicht vergessen, dass eine Befehlsschaltfläche die Eigenschaft Style = 1 braucht, um das Bild anzuzeigen.

Für die spätere Zeitnahme ist auch noch ein Timer nötig. Dieser hat folgende Eigenschaften:
  • Name = tmrTime

  • Enabled = False

  • Interval = 1000 (entspricht 1 Sekunde)

  • BackColor = 0 (Schwarz)

  • Height = 25

  • Width = 41

Ist das alles erledigt sollte das Ganze so aussehen:


 Abb.4: Die MineSweeper-Oberfläche

 
  API-Funktionen: Das Modul mDeclarations voriges Thema [ Top ] nächstes Thema

Nun geht es ans Eingemachte, den CODE!
Zunächst brauchen wir ein Modul mDeclarations. In dieses Modul kommen alle Deklarationen, genauer gesagt alle API-Funktions-Deklarationen:

Option Explicit

 ' Win32-API-Deklarationen
Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC _
   As Long, ByVal X As Long, NyVal Y As Long, ByVal nWidth _
   As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, _
   ByVal xSrc As Long, ByVal ySrc As Long, _
   ByVal dwRop As Long) As Long

Public Declare Function
PlaySound Lib "winmm.dll" _
   Alias "PlaySoundA" (ByVal lpszName As String, _
   ByVal hModule As Long, ByVal dwFlags As Long) As Long

  ' Sound-Konstanten
Public Const SND_RESOURCE = &H40004
Public Const SND_ASYNC = &H1
Public Const SND_MEMORY = &H4
Public Const SND_NODEFAULT = &H2

Die Funktion BitBlt brauchen wir für das Übertragen der Bilddaten aus einem Bildfeld in ein anderes. PlaySound wird benötigt, wenn wir hinterher Sound-Effekte hinzufügen.

Jetzt noch ein paar Aufzählungen (Enumeration) und Typendeklarationen, die wir später brauchen.

 ' Eigene Aufzählungen
Public Enum GRAPHIC_CONSTANTS
   gc_Default  = 0  ' normales Feld
   gc_Free     = 1  ' bereits gecklicktes aber freies Feld
   gc_Flag     = 2  ' Feld mit Fahne
   gc_Question = 3  ' Feld mit Fragezeichen
   gc_Mine     = 4  ' Mine
   gc_RedMine  = 5  ' Mine mit rotem Hintergrund
   gc_One      = 6  ' Feld mit "1"
   gc_Two      = 7  ' Feld mit "2"
   gc_Three    = 8  ' Feld mit "3"
   gc_Four     = 9  ' Feld mit "4"
   gc_Five     = 10 ' Feld mit "5"
   gc_Six      = 11 ' Feld mit "6"
   gc_Seven    = 12 ' Feld mit "7"
   gc_Eight    = 13 ' Feld mit "8"
End Enum

Public Enum VALUE_CONSTANTS
   vc_Free    = 1  ' Freies Feld
   vc_Mine    = 4  ' Mine
   vc_RedMine = 5  ' Mine mit rotem Hintergrund
   vc_One     = 6  ' Feld mit "1"
   vc_Two     = 7  ' Feld mit "2"
   vc_Three   = 8  ' Feld mit "3"
   vc_Four    = 9  ' Feld mit "4" 
   vc_Five    = 10 ' Feld mit "5"
   vc_Six     = 11 ' Feld mit "6"
   vc_Seven   = 12 ' Feld mit "7"
   vc_Eight   = 13 ' Feld mit "8"
End Enum

 ' Eigene Typendeklarationen
Public Type FIELD
   ' Welche Grafik wird für das Feld angezeigt?
  Graphic As GRAPHIC_CONSTANTS
  
   ' Welchen (verborgenen) Wert hat das Feld?
  Value As VALUE_CONSTANTS
  
   ' Wurde schon geklickt?
  Clicked As Boolean
End Type
 
  Funktionen für den Spielablauf voriges Thema [ Top ] nächstes Thema

Für den Spielablauf brauchen wir aber noch ein paar globale Variablen. Hierzu fügen wir unserem Projekt ein weiteres Modul hinzu und nennen es mPlay

Option Explicit

  ' Wieviele Kästchen breit?
Public Fields_X As Integer

  ' Wieviele Kästchen hoch?
Public Fields_Y As Integer

  ' Wieviele Minen gibt es?
Public MineCount As Integer

 ' Wieviele Flaggen wurden gesetzt?
Public FlagCount As Integer

  ' Wieviele Kästchen wurden bereits geklickt?
Public Clicked As Integer

  ' Wieviele Sekunden wurde schon gespielt?
Public Seconds As Integer

  ' Dialog "Benutzerdefinierter" - abgebrochen?
Public UserCancel As Boolean

  ' Array der Felder
Public m_Fields() As FIELD

Als Erstes erstellen wir nun die Prozedur zum Initialisieren des Spielfeldes. Hier werden noch keine Werte für einzelne Felder gesetzt, wohl aber die Grenzen des Spielfeldes und die Anzahl der Minen. Die nachfolgenden Prozeduren erstellen wir ebenfalls im Modul mPlay.

 ' Spielfeld initialisieren (noch keine Werte setzen)
Public Sub InitArea(Hor_Fields As Integer, _
       Ver_Fields As Integer, Mines As Integer)

   ' Breite und Höhe
  Fields_X = Hor_Fields
  Fields_Y = Ver_Fields

   ' Neu dimensionieren
  ReDimArray Hor_Fields * Ver_Fields

   ' Breite des Bildfeldes
  frmMain.picPlay.Width = Hor_Fields * 16
  frmMain.picPlay.Height = Ver_Fields * 16

   ' Minen
  MineCount = Mines
End Sub

 
' Array neu dimensionien
Public Sub ReDimArray(NewLength As Integer)
  Redim Preserve m_Fields(0 To (NewLength - 1))
End Sub

Wie man sieht, wird also ein Array von 0 bis Anzahl der Felder - 1 dimensioniert. Jetzt fügen wir zunächst ein paar Routinen zur Navigation im Array hinzu.

 ' Reihe eines Feldes ermitteln
Public Function GetRow(Index As Integer) As Integer
  GetRow = Int(Index / Fields_Y)
End Function

 ' Spalte eines Feldes ermitteln

Public Function GetCol(Index As Integer) As Integer
  GetCol = Index - (GetRow(Index) * Fields_Y)
End Function

Dies sind die beiden wichtigsten Funktionen. Sie geben Zeile/Reihe (Row) und Spalte (Col) des durch Index gegebenen Feldes an.

Nun brauchen wir noch Funktionen, um die Nachbar-Felder zu ermitteln (also oben links, oben, oben rechts, links, rechts, unten links, unten, unten rechts).

 ' Nachbarfeld (links)
Public Function GetLeftField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index - 1
  GetLeftField = IIf(GetRow(fld) < GetRow(Index), -1, fld)
End Function

 ' Nachbarfeld (rechts)
Public Function GetRightField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index + 1
  GetRightField = IIf(GetRow(fld) <> GetRow(Index) Or _
     fld >= Fields_X * Fields_Y Or _
     GetCol(fld) <> GetCol(Index) + 1, -1, fld)
End Function

  ' Nachbarfeld (oben)
Public Function GetTopField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index - Fields_X
  GetTopField = IIf(fld < 0, -1, fld)
End Function

  ' Nachbarfeld (unten)
Public Function GetBottomField(Index As Integer) As Integer

Dim fld As Integer

  fld = Index + Fields_X
  GetBottomField = IIf(fld > Fields_X * Fields_Y - 1 Or _
     GetCol(fld) <> GetCol(Index), -1, fld)
End Function

  ' Nachbarfeld (oben links)
Public Function GetTopLeftField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index - Fields_X - 1
  GetTopLeftField = IIf(fld < 0 Or _
  GetRow(fld) <= GetRow(Index) - 2, -1, fld)
End Function

  ' Nachbarfeld (oben rechts)
Public Function GetTopRightField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index - Fields_X + 1
  GetTopRightField = IIf((GetCol(fld) <> (GetCol(Index) + 1)) Or _
     (fld < 0), -1, fld)
End Function

  ' Nachbarfeld (unten rechts)
Public Function GetBottomRightField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index + Fields_X + 1
  GetBottomRightField = IIf(fld > Fields_X * Fields_Y - 1 Or _
     GetCol(Index) <> GetCol(fld) - 1, -1, fld)
End Function

 ' Nachbarfeld (unten links)
Public Function GetBottomLeftField(Index As Integer) As Integer

  Dim fld As Integer

  fld = Index + Fields_X - 1
  GetBottomLeftField = IIf(fld > Fields_X * Fields_Y - 1 Or _
     GetCol(Index) <> GetCol(fld) + 1, -1, fld)
End Function

Das sind jetzt erst einmal ziemlich viele Funktionen, mit denen man so ohne den Kontext nichts anfangen kann. Keine Angst, das Verständnis kommt schon noch. Zu beachten ist aber, dass diese Funktionen alle den Wert -1 zurückgeben, falls das gesuchte Feld nicht existiert (bspw. das gesuchte Feld über dem Feld ganz links oben kann nicht existieren).

 
  Spielfeld und Felder zeichnen voriges Thema [ Top ] nächstes Thema
Nun formulieren wir eine Prozedur, die ein einzelnes Feld zeichnet und eine, die alle Felder zeichnet.

Hierzu fügen wir unserem Projekt wiederum ein neues Modul hinzu und nennen es mDraw.

Option Explicit

  ' Ein Feld zeichnen
Public Sub DrawField(Index As Integer, _
       Optional ByVal ReDraw As Boolean = True)

  BitBlt frmMain.picPlay.hDC, GetCol(Index) * 16, _
     GetRow(Index) * 16, 16, 16, frmMain.picMine.hDC, _
     0, m_Fields(Index).Graphic * 16, vbSrcCopy

  If ReDraw Then frmMain.picPlay.Refresh

End Sub

Hier verwenden wir die API-Funktion BitBlt, um die Bilddaten zu übertragen. Wir ermitteln also die Spalte und die Zeile des Feldes. Multipliziert mit 16 ergibt das dann jeweils die x- und y-Position zum Zeichnen in picPlay. Bei der Quelle nutzen wir unseren selbst definierten Datentyp FIELD aus. Er hat eine Variable Graphic, die die Information speichert, welches Bild für dieses Feld gezeichnet werden soll. Wie der Wert dafür zustande gekommen ist, kommt später. Im Moment soll einfach nur blind die Zeichen-Funktion programmiert werden. Wir nutzen die konstante Höhe eines Feldes aus, die genau 16 Pixel beträgt. Multipliziere ich also die Variable Graphic mit 16, so erhalte ich die y-Position zum Blitten aus der Quell-Bitmap. Der Parameter ReDraw legt fest, ob das Bildfeld picPlay neu gezeichnet werden soll, wenn die Operation abgeschlossen ist.

Nun aber zur Funktion um alle Felder zu zeichnen. Da wir genau die Grenzen kennen ist es nur allzu logisch eine For-Schleife zu verwenden.

 ' Alle Feld zeichnen
Public Sub DrawArea()
  Dim X As Integer
  Dim Y As Integer
  Dim produkt As Integer
  Dim fld As Integer

  produkt = Fields_X * Fields_Y
  For Y = 0 To Fields_Y
    For X = 0 To Fields_X
      fld = Y * Fields_X + X
      If fld < produkt Then mDraw.DrawField fld, False
    Next
X
  Next Y

  frmMain.picPlay.Refresh

End Sub

Nur um das Zeichnen zu komplettieren nun noch folgende Funktion, die sich wohl von selbst erklärt - wir erneuern die Ansicht des Bildfeldes:

' Bildfeld neu zeichnen
Public Sub ReDraw()
  frmMain.picPlay.Refresh
End Sub
 
  Die FIELD-Struktur voriges Thema [ Top ] nächstes Thema

Um jetzt mit den spannenden Routinen zu beginnen, schauen wir uns den Datentyp FIELD noch einmal etwas genauer an.

Public Type FIELD
   Graphic As GRAPHIC_CONSTANTS
   Value   As VALUE_CONSTANTS
   Clicked As Boolean
End Type

Die Variable Graphic sagt nur aus, welche Grafik verwendet wird (Freies Feld, Eins, Zwei, Fahne, usw.). Um es einfacher zu machen hat diese Variable den Datentyp GRAPHIC_CONSTANTS. Dies ermöglicht, dass wir nicht umständlich schreiben müssen "Graphic = 5" und uns dabei immer merken müssen, welchen Wert 5 eigentlich repräsentiert, sondern wir sagen "Graphic = gc_One", so dass sofort klar ist, dass damit die Eins gemeint ist.

Die Variable Value repräsentiert den eigentlichen Wert, der sich "unter dem Feld verbirgt". Dies kann "Frei", "Mine", "Rote Mine" (also eine Mine auf die man gerade geklickt hat) oder eine Zahl sein.

Die Variable Clicked gibt lediglich an, ob dieses Feld bereits angeklickt wurde, denn dann muss nicht extra noch einmal der ganze Aufruf von Funktionen durchlaufen werden, wie wir ihn gleich sehen werden.

Nun benötigen wir noch ein paar Prozeduren, um diese Eigenschaften vereinfacht zu setzen, so dass sich z.B. beim Klicken eines Feldes automatisch das Bild ändert.

 ' Wert eines Feldes setzen
Public Sub SetValue(Index As Integer, New_Value As VALUE_CONSTANTS)
  m_Fields(Index).Value = New_Value
End Sub

  ' Status "geklickt" setzen
Public Function SetClicked(Index As Integer, _
     Optional ByVal New_Clicked As Boolean = True) As Boolean

  SetClicked = False
  
  With
m_Fields(Index)
    If .Clicked = False Then
      .Clicked = New_Clicked
      If New_Clicked Then mPlay.Clicked = mPlay.Clicked + 1
    Else
      .Clicked = New_Clicked
     End If

     If Clicked = Fields_X * Fields_Y - MineCount Then
      ' Gewonnen ;-)
      SetClicked = True
     End If
  End With
End Function

  ' Grafik ermitteln
Public Function GetGraphic(Index As Integer) As GRAPHIC_CONSTANTS
  GetGraphic = m_Fields(Index).Graphic
End Function

  ' Grafik  setzen (für Flag und Fragezeichen)
Public Sub SetGraphic(Index As Integer, NewGraphic As _
     GRAPHIC_CONSTANTS, Optional ByVal ReDraw As Boolean = True)

  m_Fields(Index).Graphic = NewGraphic
  DrawField Index, ReDraw
End Sub

Der Parameter ReDraw gibt immer an, ob das Bildfeld picPlay neugezeichnet werden soll oder nicht.

Jetzt kommen wir unserem Ziel immer näher. Es geht nun darum, die Funktion zu schreiben, die ein Feld mit einer Zahl belegt, weil im Nachbarfeld eine Mine ist.

 ' Wenn keine Mine, um eins erhöhen
 ' bzw. beim ersten Eintrag auf vb_One setzen

Public Sub IncreaseField(fld As Integer)
  If fld <> -1 Then
    With m_Fields(fld)
      If .Value <> vc_Mine Then
        If
.Value >= vc_One Then
          .Value = .Value + 1
        Else
          .Value = vc_One
        End If
      End If
    End With
  End If
End Sub

IncreaseField heißt soviel wie "Feld erhöhen" und macht aus freien Feldern Felder mit einer 1. Bei Feldern, die bereits eine Zahl haben wird diese um 1 erhöht. Der maximale Wert ist, wie wir bereits wissen, 8. Dies geschieht aber nur, wenn dieses Feld keine Mine besitzt (m_Fields(fld).Value <> vc_Mine).

Nun folgt die Prozedur zum Verteilen der Minen. Im Prinzip ist sie ganz einfach, aber trotzdem etwas länger auf Grund der Funktionsaufrufe:

 ' Minen verteilen
Public Sub InitMines()
  Dim n As Integer
  Dim
fld As Integer

  For
n = 1 To MineCount
    Do
      fld = Int(Fields_X * Fields_Y * Rnd)
    Loop While m_Fields(fld).Value = vc_Mine
    m_Fields(fld).Value = vc_Mine
  Next n

  For
n = 0 To Fields_X * Fields_Y - 1
    With m_Fields(n)
      If .Value = vc_Mine Then
         ' Nachbarfelder setzen
        IncreaseField GetTopLeftField(n)
        IncreaseField GetTopField(n)
        IncreaseField GetTopRightField(n)
        IncreaseField GetLeftField(n)
        IncreaseField GetRightField(n)
        IncreaseField GetBottomLeftField(n)
        IncreaseField GetBottomField(n)
        IncreaseField GetBottomRightField(n)
      ElseIf .Value = 0 Then
        .Value = vc_Free
      End If
    End With
  Next
n
End Sub

Zuerst werden in einer For-Schleife alle Minen verteilt. Es wird so lange ein Index durch eine Zufallszahl ermittelt, bis keine Mine gefunden wird, damit nicht zwei Minen auf ein Feld gesetzt werden.

 
  Reagieren auf Mausereignisse voriges Thema [ Top ] nächstes Thema

Jetzt fügen wir noch die Prozedur hinzu, die beim Klicken auf das Spielfeld ausgelöst wird. Wen die Details interessieren, der kann sie sich noch genauer anschauen. Es wird lediglich jedes Nachbarfeld überprüft. Hierbei ist aber zu beachten, dass wenn das Ausgangs-Feld frei ist, ein rekursiver Aufruf stattfindet. Dieselbe Prozedur namens ClickField wird mit dem Nachbarfeld-Index aufgerufen. Dies setzt sich so lange fort, bis kein angrenzendes freies Feld mehr abgedeckt ist. Hierbei wird noch eine andere Regel beachtet, nämlich die Fahne und das Fragezeichen.

Die Fahne (Wert gc_Flag) wird vom Benutzer mit der rechten Maustaste platziert. Sie bedeutet, dass der Spieler darunter eine Mine versteckt sieht, weshalb dieses Feld auch nicht durch unsere Prozedur aufgedeckt werden darf. Ein Fragezeichen hingegen wird einfach "plattgemacht".

Hier nun die Prozedur:

 ' Es wird auf ein Feld geklickt
Public Sub ClickField(Index As Integer, _
   Optional ByVal ReDraw As Boolean = True)

  Dim fld As Integer

  With m_Fields(Index)
    If .Clicked = False Then
      If
.Value = vc_Mine Then .Value = vc_RedMine
       .Graphic = m_Fields(Index).Value
       DrawField Index, ReDraw

      If SetClicked(Index, True) Then
        Win 'Gewonnen ;-)
        Exit Sub
      End If

      If
m_Fields(Index).Value = vc_RedMine Then
        Lose ' Verloren ;-(
        Exit Sub
      End If


        ' Nachbarfelder
       If .Value = vc_Free Then
          ' Oben links
        fld = GetTopLeftField(Index)
        FieldAction fld

          ' Oben
        fld = GetTopField(Index)
        FieldAction fld

          ' Oben rechts
        fld = GetTopRightField(Index)
        FieldAction fld

          ' Links
        fld = GetLeftField(Index)
        FieldAction fld

          ' Rechts
        fld = GetRightField(Index)
        FieldAction fld

          ' Unten links
        fld = GetBottomLeftField(Index)
        FieldAction fld

          ' Unten
        fld = GetBottomField(Index)
        FieldAction fld

          ' Unten rechts
        FieldAction fld
       End If
    End If
  End With
End Sub


 ' Aktion ausführen (nach Klick)

Private Sub FieldAction(ByVal iFeld As Integer)
  If iFeld <> -1 Then
    With
m_Fields(iFeld)
      If Not .Graphic = gc_Flag Then
        If
.Value = vc_Free Then
          ClickField iFeld, False
        ElseIf
.Value >= vc_One Then
          SetClicked iFeld, True
          .Graphic = .Value
          DrawField iFeld, False
        End If

      End If
    End With
  End If
End Sub

So. Alles geschafft? Das Programm ist natürlich noch nicht fertig. Es fehlen noch ein paar Dinge. So z.B. die Prozedur zum Abfragen der Maus-Ereignisse.

Die Abfrage findet natürlich nicht im Modul, sondern in der Hauptform frmMain statt:

 ' Die Maus wird gedrückt
Private Sub picPlay_MouseDown(Button As Integer, _
    Shift As Integer, X As Single, Y As Single)

  Dim fld As Integer

  If tmrTime.Enabled = False Then tmrTime.Enabled = True
  fld = Int(X / 16) + Int(Y / 16) * Fields_X
  If Button = 1 Then
     ' linke Maustaste
    mDraw.ClickField (fld), False
    picPlay.Refresh
  ElseIf Button = 2 Then
     ' rechte Maustaste
    Select Case GetGraphic(fld)
      Case gc_Default
        If FlagCount < MineCount Then
          SetGraphic fld, gc_Flag
          FlagCount = FlagCount + 1
          DrawMineCount
        Else
          SetGraphic fld, gc_Question
        End If
      Case
gc_Flag
        SetGraphic fld, gc_Question
        FlagCount = FlagCount - 1
        DrawMineCount
      Case gc_Question
        SetGraphic fld, gc_Default
    End Select
  End If
End Sub

Bei einem Klick mit der linken Maustaste wird das Feld aufgedeckt (ClickField-Prozedur), bei einem Rechtsklick wird zunächst überprüft, ob das Feld bereits eine Fahne / ein Fragezeichen hat oder ob es frei ist. Je nachdem was davon zutrifft, wird dann eine neue Grafik über SetGraphic gesetzt. Ebenfalls hier vermerkt: der Timer. Der Timer darf ja auch erst starten, wenn der Benutzer das 1. Mal auf das Spielfeld klickt.

 
  Beispielprojekt zum Downloaden voriges Thema [ Top ]

Wer den Quellcode herunterlädt, der kann sich noch einmal in Ruhe alle Funktionen (auch die, die hier nicht weiter erwähnt sind) anschauen und nachvollziehen.

Zudem sind in dem Projekt noch Menüs, verschiedene Spielfelder u.v.m. integriert.


 Abb. 5: MineSweeper in VB - Leider verloren ;-)

Anmerkung:

Sound ist zwar im Menü schon vorgesehen, ist aber noch nicht in einer Resourcen-Datei im Projekt inbegriffen.

  Download
MineSweeper.zip
 (23 kB)
Downloadzeit: ca. 1 Min. - 28.8k / ca. 2 Min. - ISDN Downloads bisher: [ 7048 ]

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: Montag, 23. Dezember 2002