VB.NET Game – Dein Eigenes TicTacToe Spiel
Inhaltsverzeichnis
- 1 VB.NET Game erstellen
- 2 Vorschau auf das Spiel
- 3 Vorwort – VB.NET Game
- 4 Ein eigenes Spiel – Der Traum vieler Entwickler
- 5 Der Blick zurück
- 6 Die Hürden – VB.NET Game
- 7 Los geht’s TicTacToe – VB.NET Game
- 8 Code des Spiels – VB.NET Game
- 8.1 Eigenschaften
- 8.2 Konstruktor
- 8.3 Methoden
- 8.3.1 InitializePictureBoxes
- 8.3.2 CreatePictureBox
- 8.3.3 PictureBox_Click
- 8.3.4 SetPlayerToken
- 8.3.5 SetPlayerOneToken
- 8.3.6 SetPlayerTwoToken
- 8.3.7 HasPlayerWon
- 8.3.8 DoPictureBoxesMatch
- 8.3.9 AskForRematch
- 8.3.10 AskForDrawRematch
- 8.3.11 Restart
- 8.3.12 ResetImages
- 8.3.13 AllTokensPlaced
- 8.3.14 ChangePlayerTurn
- 8.3.15 UpdatePlayerTurnLabel
- 8.3.16 StartCountDown
- 8.3.17 frmTicTacToe_Load
- 8.3.18 ChooseRandomStartingPlayer
- 8.3.19 tmrCountDown_Tick
- 8.3.20 UpdateTimeLabel
- 8.3.21 frmTicTacToe_FormClosing
- 8.3.22 btnBack_Click
- 8.3.23 btnExit_Click
- 9 Kompletter Code – VB.NET Game
- 10 Downloads
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.
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 China–Grinder, 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„.
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 Projektmappen–Explorer.
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 Klick–Ereignishandler.
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 Spieler–Bildes 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 Start–Spieler 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 Klick–Handler 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