VB.NET Game – Dein Eigenes TicTacToe Spiel

VB.NET Game erstellen

Du möchtest Dein eigenes VB.NET Game entwickeln und dies sauber und mit Spaß umsetzen?

Dann bist Du mit deiner Recherche genau auf den richtigen Beitrag meiner Seite gestoßen!

Vielleicht interessieren Dich später auch noch die folgenden Beiträge: VB.NET Slot Machine, AddHandler-Anweisung, Sleep-Timer.

Vorschau auf das Spiel

Hier siehst Du im Sinne einer Vorschau ein kleines Bild von dem fertigen Spiel.

VB.NET Game TicTacToe
VB.NET Game TicTacToe

Vorwort – VB.NET Game

Ich möchte an dieser Stelle direkt anführen, dass ich dieses Beispiel hier so einfach und nachvollziehbar wie möglich gestalten möchte.

Daher werden wir hier für den Anfang bei einem simplen und jedem bekannten Spiel bleiben.

In späteren Beiträgen werde ich aber sehr sehr wahrscheinlich noch andere kleine Spiele vorstellen und erläutern.

Es wird hier also leider nicht das nächste Call of Duty erstellt 😛!

Ich werden hier ausschließlich vorhandene Steuerelemente und verwende auch kein GDI+.

Das kommt eventuell noch in einer verbesserten Version, also für eventuelle fortgeschrittenere Personen.

Ein eigenes Spiel – Der Traum vieler Entwickler

Viele Entwickler – so auch ich damals – finden ihren Weg teilweise gerade durch das Interesse am Gaming zur Softwareentwicklung.

Manchmal haben Sie dabei auch direkt eigene Pläne, vielleicht sogar schon ein konkretes Ziel: „Ich möchte mein eigenes Spiel entwickeln!“.

Häufiger Stand dabei ist leider: Keine Vorerfahrung, mangelnde Grammatik (man weiß nicht wonach man suchen muss) und vor allem so mancher Code der im Netz kreucht und fleucht.

Der Blick zurück

Mannoman, wenn ich so an meine eigenen Anfänge der .NET Entwicklung zurückdenke, dann denke ich vor allem an mein Interesse, einen eigenen Bot zu bauen.

Ich war damals zugegebenermaßen so dämlich, mir eine Fremdsoftware von einem dubiosen Anbieter herunterzuladen und zu verwenden.

Diese führte dann leider dazu, dass ich meinen Account von einem Spiel was ich zu der Zeit gespielt habe, verloren habe – ich wurde gehackt.

Bei diesem Spiel handelte es sich um ein typisches ChinaGrinder, sprich viele Stunden mit leveln zu verbringen und wenige bis kaum Erfolge, bzw. Level-Ups!

Die Hürden – VB.NET Game

Wie ich oben schon angesprochen habe, hat man vor allem am Anfang viele Hürden zu bekämpfen und die vielen Argumente aus Foren wie „Lerne die Basics“ will man natürlich nicht hören.

Natürlich sind diese Argumente mehr als berechtigt und durchaus sinnvoll, aber man möchte dies wie erwähnt nicht hören.

Ich selbst hatte dort schon immer einen Sturkopf und wenn ich nun einmal ein Spiel bauen wollte, dann wollte ich halt ein Spiel bauen..

Dann bekam man auch z. B. noch weitere Kritik zu hören, dass VB.NET nicht dafür geeignet sei, ein Spiel zu programmieren.

GDI+ wäre zu langsam und Prozessor-lastig und und und..

Die traurige Wahrheit ist, dass diese Aussagen erstmal soweit alle im Kern stimmen – im Kern!

Wer allerdings seine ersten Erfahrungen mit kleinen Spielen wie TicTacToe, Schiffe versenken, oder ähnlichen Spielen sammeln möchte – mein Gott, „Do It!“.

Du wirst vermutlich nur nicht das grafische Wunder schlechthin mit VB.NET, GDI+ und Co. bauen können, das muss Dir klar sein.

Los geht’s TicTacToe – VB.NET Game

Jetzt beginnen wir mit unserem kleinen TicTacToe-Game und fangen beim Begrüßungsbildschirm an.

Begrüßungsbildschirm

Wie man es aus Spielen kennt, sieht man am Anfang einen kleinen Begrüßungsbildschirm, Welchen auch wir fix einbauen werden.

Stelle zuerst in den Projekteigenschaften (Projekt->Eigenschaften->Anwendung) den Modus für das Herunterfahren auf „beim schließen des letzten Formulars„.

VB.NET Game TicTacToe Begrüßungsbildschirm
VB.NET Game TicTacToe Begrüßungsbildschirm

Form-Eigenschaften

Dafür ändern wir die Eigenschaften der aktuellen Hauptform (meist Form) auf:

  • Name: frmWelcome
  • DoubleBuffered: True
  • StartPosition: CenterScreen
  • Size: 640; 480
  • FormBorderStyle: None
  • BackgroundImage: bg.jpg

Das Hintergrundbild findest Du als Bestandteil des Beispielprojekts in den Downloads weiter unten.

Füge das Hintergrundbild als Ressource unter Projekt->Eigenschaften->Ressourcen durch z. B. Drag & Drop hinzu.

Danach erscheint das Bild auch im Ressourcen-Ordner im ProjektmappenExplorer.

Titel-Label

Füge als nächstes ein Label für den Titel hinzu und stelle die Eigenschaften wie es Dir passt ein, für mein Beispiel jedoch wie folgt:

  • Name: lblTitle
  • BackColor: Transparent
  • Font: Arial Black; 48pt; style=Bold
  • ForeColor: White
  • Text: TicTacToe

Und positioniere das Label nach deinem Belieben auf der Form.

Buttons

Nimm im nächsten Schritt 2 Buttons zur Hand und stelle diese Eigenschaften ein:

btnStartGame

Ein Knopf zum Starten des Spiels:

  • Name: btnStartGame
  • BackColor: LimeGreen
  • FlatAppearance: BorderColor Black, BorderSize 3
  • FlatStyle: Flat
  • Font: Arial; 12pt; style=Bold
  • Size: 196; 55
  • Text: Start Game
btnExit

Der Knopf zum Beenden des Spiels (vor Beginn).

Hat soweit die gleichen Eigenschaften wie der andere Button, nur eventuell eine andere Farbe inkl. Text.

  • Name: btnExit
  • BackColor: IndianRed
  • Text: Exit

Die Button-Handler

Gehen wir nun z. B. durch einen Doppelklick auf die Buttons, in die Handler der Buttons und fügen folgenden Code ein:

Public Class frmWelcome

    Private Sub btnStartGame_Click(sender As Object, e As EventArgs) Handles btnStartGame.Click
        Dim frmTicTacToe = New frmTicTacToe()
        frmTicTacToe.Show()
        Close()
    End Sub

    Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
        Application.Exit()
    End Sub

End Class

Beim Klicken des Start-Buttons wird die aktuelle, also die Begrüßungsform geschlossen und anschließend die andere Form angezeigt.

Denke hier an den obigen Hinweis, sonst wird sich hier das gesamte Programm schließen.

Danach wird eine neue Instanz der TicTacToe-Form erstellt und angezeigt und schon geht es los.

Der Schließen-Button macht nichts anderes als – wer hätte es gedacht – die Anwendung zu schließen.

Man könnte hier auch einfach Close allein verwenden, da wir vorher kein anderes Formular angezeigt haben.

TicTacToe VB.NET Game Formular

Nun geht es mit dem Formular für das Spiel selbst und der Logik dahinter weiter.

Füge dazu ein weiteres Formular hinzu und setze folgende Eigenschaften:

  • Name: frmTicTacToe
  • StartPosition: CenterScreen
  • Text: TicTacToeExample
  • Size: 640; 480
  • FormBorderStyle: None
  • DoubleBuffered: True

Danach brauchst Du ein SplitContainer, Welcher das Formular in 2 Bereiche einteilt:

  • Name: splcGame
  • Dock: Fill
  • FixedPanel: Panel1
  • IsSplitterFixed: True
  • Size: 640; 480
  • SplitterDistance: 156
  • SplitterWidth: 1

In den rechten Bereich des SplitContainers fügst Du nun ein TableLayoutPanel hinzu:

  • Name: tlpGame
  • BackgroundImage: game_bg
  • BackgroundImageLayout: Stretch
  • ColumnCount: 3
  • RowCount: 3
  • Dock: Fill

Den linken Bereich des SplitContainers, also das Panel1 bekommt folgende Eigenschaften:

  • BackgroundImage: sidebar

Danach füge 4 Labels und 2 Buttons hinzu, wovon 2 Labels als Anzeige dienen (Rest-Zeit und welcher Spieler dran ist) und 2 als Beschreibung dafür.

Die beiden Buttons sind letztendlich für das Schließen der Anwendung und dem Zurückkehren in das Hauptmenü zuständig.

Code des Spiels – VB.NET Game

Eigenschaften

Random

Eine Instanz der Random-Klasse um uns das zufällige Auslosen des Start-Spielers zu ermöglichen.

XImage

Ein Objekt aus den Ressourcen, einmalig abgerufen um den Vergleich und das Setzen der Bilder zu ermöglichen.

CircleImage

Analog zum Bild hier drüber..

PlayerTurn

Ein Boolean, Welcher den aktuellen an der Reihe befindlichen Spieler widerspiegelt.

IsPlayerOnesTurn

Auf PlayerTurn basierender boolischer Wert, Welcher angibt ob Spieler 1 gerade dran ist.

IsPlayerTwosTurn

Analog zum Wert hier drüber, nur für Spieler 2.

Konstruktor

new()

Der Konstruktor instanziiert die Random-Klasse und setzt einmalig die Bilder, Welche später für die Pictureboxen (Spiele-Token) verwendet werden.

Zum Schluss laden wir die Pictureboxen in einer Schleife, um nicht 9 mal das Gleiche Prozedere im Designer machen zu müssen.

Methoden

InitializePictureBoxes

Generiert alle 9 Pictureboxen und fügt Sie dem TableLayoutPanel hinzu.

CreatePictureBox

Erstellt eine Picturebox mit den gewünschten Styles und dem benötigten KlickEreignishandler.

PictureBox_Click

Wenn eine Picturebox geklickt wird, wird die Picturebox mit Typecasting ausgemacht.

Danach wird überprüft, ob das Bild der Picturebox schon gesetzt wurde, wenn ja, wird die Sub durch Early-Return verlassen.

Danach wird das Bild (also das X, oder der Kreis) vom jeweiligen Spieler gesetzt.

Zum Schluss wird geprüft, ob ein Spieler gewonnen hat und wenn ja, gefragt ob man noch ein Match spielen möchte.

Alternativ wird nach einem Unentschieden geschaut und zu guter Letzt die Runde ansonsten einfach an den nächsten Spieler abgegeben.

SetPlayerToken

Je nachdem welcher Spieler dran ist, wird eine weitere Hilfsmethode zum Setzen des jeweiligen SpielerBildes aufgerufen.

SetPlayerOneToken

Setzt das Bild des Spielers 1 in die Picturebox.

SetPlayerTwoToken

Anlog zur vorherigen Methode, nur für Spieler 2.

HasPlayerWon

Überprüft mit weiteren Hilfsmethoden, ob ein Spieler gewonnen hat.

DoPictureBoxesMatch

Sehr hilfreiche Methode, um jeweils die Pictureboxen zu überprüfen, ob Diese alle das gleiche Bild haben und bei dreien dementsprechend gewonnen wurde.

AskForRematch

Fragt den Nutzer nach einem „Rematch“ und startet das Spiel bei „Ja“ neu, oder schließt das Formular.

AskForDrawRematch

Fragt den Nutzer nach einem „Unentschieden-Rematch“ und verfährt analog der obigen Methode.

Restart

Startet ein neues Spiel, indem die Bilder zurückgesetzt werden und einen neuen zufälligen StartSpieler wählt.

ResetImages

Setzt die Bilder zurück.

AllTokensPlaced

Funktion, Welche überprüft, ob alle Spiel-Tokens (alle Bilder) gesetzt wurden.

ChangePlayerTurn

Wechselt das „wer ist gerade dran“-Flag, updatet das Label, Welche den aktuellen Spieler darstellt und startet den Countdown neu.

UpdatePlayerTurnLabel

Setzt den Label-Text, Welcher den aktuellen Spieler anzeigt.

StartCountDown

Setzt das Countdown-Label und startet den Countdown-Timer.

frmTicTacToe_Load

Der „Form-Load“-Ereignishandler, Welcher das Spiel initial startet.

ChooseRandomStartingPlayer

Diese Sub wählt einen zufälligen Spieler, Welcher mit seinem Zug startet.

Dabei generieren wir eine neue zufällige Zahl und prüfen mit dem Modulus-Operator (Wert 2), ob Diese eine gerade Zahl ist.

Wenn die Zahl gerade ist, dann beginnt der Player 1, sonst Player 2.

Danach wird auch hier dargestellt welcher Player dran ist und der Countdown gestartet.

tmrCountDown_Tick

Der Tick-Ereignishandler des Timers, Welcher jede Sekunde nach Start tickt.

Solange der Timer noch nicht das Ende erreicht hat (größer als 0 ist) wird heruntergezählt und das Time-Label aktualisiert.

Wenn das Ende erreicht ist, dann wird der Timer gestoppt und der Spieler gewechselt inkl. Timer neugestartet.

UpdateTimeLabel

Setzt den aktuellen Zeit-Wert in das Time-Label und zeigt somit die verbleibende Zeit für die Runde.

frmTicTacToe_FormClosing

Soll den Timer sauber beenden, falls Dieser zur Zeit des „Form-Closing„-Events noch läuft.

btnBack_Click

Der KlickHandler für den „Zurück-Button“, Welcher die Start-Form erneut anzeigt und die aktuelle Form schließt.

btnExit_Click

Beendet die Anwendung.

Kompletter Code – VB.NET Game

Public Class frmWelcome

    Private Sub btnStartGame_Click(sender As Object, e As EventArgs) Handles btnStartGame.Click
        Dim frmTicTacToe = New frmTicTacToe()
        frmTicTacToe.Show()
        Close()
    End Sub

    Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
        Application.Exit()
    End Sub

End Class

Public Class frmTicTacToe

    Private Property Random As Random

    Private Property XImage As Bitmap

    Private Property CircleImage As Bitmap

    Public Property PlayerTurn As Boolean

    Public ReadOnly Property IsPlayerOnesTurn
        Get
            Return PlayerTurn
        End Get
    End Property

    Public ReadOnly Property IsPlayerTwosTurn
        Get
            Return Not PlayerTurn
        End Get
    End Property

    Sub New()
        InitializeComponent()
        Random = New Random()
        XImage = My.Resources.x
        CircleImage = My.Resources.circle
        InitializePictureBoxes()
    End Sub

    Private Sub InitializePictureBoxes()
        For i = 1 To 9
            Dim pb = CreatePictureBox()
            tlpGame.Controls.Add(pb)
        Next
    End Sub

    Private Function CreatePictureBox() As PictureBox
        Dim pb = New PictureBox()
        With pb
            .BackColor = Color.Transparent
            .Dock = DockStyle.Fill
            .Margin = New Padding(0)
            .BorderStyle = BorderStyle.FixedSingle
            AddHandler pb.Click, AddressOf PictureBox_Click
        End With
        Return pb
    End Function

    Private Sub PictureBox_Click(sender As Object, e As EventArgs)
        Dim pb = CType(sender, PictureBox)

        Dim tokenAlreadySet = pb.BackgroundImage IsNot Nothing
        If tokenAlreadySet Then
            Return
        End If

        SetPlayerToken(pb)

        If HasPlayerWon() Then
            AskForRematch()
        ElseIf AllTokensPlaced() Then
            AskForDrawRematch()
        Else
            ChangePlayerTurn()
        End If
    End Sub

    Private Sub SetPlayerToken(pb As PictureBox)
        If IsPlayerOnesTurn Then
            SetPlayerOneToken(pb)
        Else
            SetPlayerTwoToken(pb)
        End If
    End Sub

    Private Sub SetPlayerOneToken(pb As PictureBox)
        pb.BackgroundImage = XImage
    End Sub

    Private Sub SetPlayerTwoToken(pb As PictureBox)
        pb.BackgroundImage = CircleImage
    End Sub

    Private Function HasPlayerWon() As Boolean
        Dim horizontalMatch = CheckHorizontalMatch()
        If horizontalMatch Then
            Return True
        End If

        Dim verticalMatch = CheckVerticalMatch()
        If verticalMatch Then
            Return True
        End If

        Dim diagonalMatch = CheckDiagonalMatch()
        If diagonalMatch Then
            Return True
        End If

        Return False
    End Function

    Private Function CheckHorizontalMatch() As Boolean
        Return DoPictureBoxesMatch(1, 2, 3) OrElse
            DoPictureBoxesMatch(4, 5, 6) OrElse
            DoPictureBoxesMatch(7, 8, 9)
    End Function

    Private Function CheckVerticalMatch() As Boolean
        Return DoPictureBoxesMatch(1, 4, 7) OrElse
            DoPictureBoxesMatch(2, 5, 8) OrElse
            DoPictureBoxesMatch(3, 6, 9)
    End Function

    Private Function CheckDiagonalMatch() As Boolean
        Return DoPictureBoxesMatch(1, 5, 9) OrElse
            DoPictureBoxesMatch(7, 5, 3)
    End Function

    Private Function DoPictureBoxesMatch(ParamArray pictureBoxNumbers As Integer()) As Boolean
        Dim images = New List(Of PictureBox)
        For Each number In pictureBoxNumbers
            Dim pbIndex = number - 1
            Dim pb = CType(tlpGame.Controls(pbIndex), PictureBox)
            images.Add(pb)
        Next
        If IsPlayerOnesTurn Then
            Return images.All(Function(x) x.BackgroundImage Is XImage)
        Else
            Return images.All(Function(x) x.BackgroundImage Is CircleImage)
        End If
    End Function

    Private Sub AskForRematch()
        Dim playerNumber = IIf(IsPlayerOnesTurn, 1, 2)
        Dim msg = $"Player {playerNumber} won, do you want to restart?"
        Dim wantsToRestart = MessageBox.Show(msg, "Player won", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes
        If wantsToRestart Then
            Restart()
        Else
            Close()
        End If
    End Sub

    Private Sub AskForDrawRematch()
        Dim msg = $"It's a draw, do you want to restart?"
        Dim wantsToRestart = MessageBox.Show(msg, "Draw!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes
        If wantsToRestart Then
            Restart()
        Else
            Close()
        End If
    End Sub

    Private Sub Restart()
        ResetImages()
        ChooseRandomStartingPlayer()
    End Sub

    Private Sub ResetImages()
        For Each pb In tlpGame.Controls.OfType(Of PictureBox)
            pb.BackgroundImage = Nothing
        Next
    End Sub

    Private Function AllTokensPlaced() As Boolean
        Dim countPlaced = 0
        For Each pb In tlpGame.Controls.OfType(Of PictureBox)
            Dim tokenSet = pb.BackgroundImage IsNot Nothing
            If tokenSet Then
                countPlaced += 1
            End If
        Next
        ' or with LINQ:
        ' Dim countPlaced = tlpGame.Controls.OfType(Of PictureBox).Where(Function(pb) pb.BackgroundImage IsNot Nothing)
        Return countPlaced = tlpGame.Controls.Count
    End Function

    Private Sub ChangePlayerTurn()
        PlayerTurn = Not PlayerTurn
        UpdatePlayerTurnLabel()
        StartCountDown()
    End Sub

    Private Sub UpdatePlayerTurnLabel()
        Dim playerNumber = 0
        If IsPlayerOnesTurn Then
            playerNumber = 1
        Else
            playerNumber = 2
        End If
        lblPlayerTurnValue.Text = $"Player {playerNumber}"
    End Sub

    Private Sub StartCountDown()
        lblTimeValue.Text = "10"
        tmrCountDown.Start()
    End Sub

    Private Sub frmTicTacToe_Load(sender As Object, e As EventArgs) Handles Me.Load
        ChooseRandomStartingPlayer()
    End Sub

    Private Sub ChooseRandomStartingPlayer()
        Dim rand = Random.Next(100)
        Dim isEven = rand Mod 2 = 0
        PlayerTurn = isEven
        UpdatePlayerTurnLabel()
        StartCountDown()
    End Sub

    Private Sub tmrCountDown_Tick(sender As Object, e As EventArgs) Handles tmrCountDown.Tick
        Dim time = Convert.ToByte(lblTimeValue.Text)
        If time > 0 Then
            time -= 1
            UpdateTimeLabel(time)
            Return
        End If
        tmrCountDown.Stop()
        ChangePlayerTurn()
        StartCountDown()
    End Sub

    Private Sub UpdateTimeLabel(time As Integer)
        lblTimeValue.Text = time.ToString()
    End Sub

    Private Sub frmTicTacToe_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
        tmrCountDown.Stop()
    End Sub

    Private Sub btnBack_Click(sender As Object, e As EventArgs) Handles btnBack.Click
        Dim frmWelcome = New frmWelcome()
        frmWelcome.Show()
        Close()
    End Sub

    Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
        Application.Exit()
    End Sub

End Class

Downloads

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert