Mensch ärger dich nicht von A-Z

    • VB.NET

    Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von bsHobbit.

      Mensch ärger dich nicht von A-Z

      Wichtig Keine Kommentare in diesem Thread, Kommentare bitte in diesen Thread posten:
      Kommentare Mensch ärger dich nicht

      Hallo Community,

      da bei sehr vielen der Wunsch ein Spiel zu programmieren groß ist und ich es langsam leid bin immer wieder die selben Antworten zu geben, werde ich jetzt ein kleinen Leitfaden für die Anfänge der Spieleprogrammierung schreiben.

      Ich schreibe so etwas zum ersten mal und bin selber sicher auch kein perfekter "Spiele"-Programmierer, daher bitte ich Rücksicht mit mir zu nehmen und mich auf evtl auftretende Fehler oder einen unschönen Programmierstil einfach hinzuweisen, ich werde mich dann ggf. korrigieren.

      Ich werde versuchen auf die Rechtschreibung zu achten, kann aber für nichts garantieren ;).

      Kapitel 1: Aller Anfang ist schwer

      Was man als erstes benötigt bevor man einfach drauf los programmiert ist ein Konzept, über das Spiel welches man Programmieren möchte. In diesem Fall ist es sehr einfach, ich habe mich dazu entschieden einen einfachen "Mensch ärgere dich nicht" Klon zu erstellen.
      Ich kenne somit bereits alle Spielregeln, den Spielablauf und das Spielziel. Sogar das Spielfeld ist mir bereits bekannt. Wenn das alles noch nicht vorhanden ist, dann sollte man sich erst einmal hinsetzen und ein Design-Konzept entwickeln. Je nach Umfang des Projekts kann das Design-Konzept 20-200+ Seiten umfassen.

      Nachdem das Spiel komplett durchdacht ist kann man endlich mit dem Programmieren anfangen....

      Aber halt... Wie programmiert man denn nun eigentlich ein Spiel ?

      Ich hab da schonmal was von DirectX und OpenGL gehört. Ist das zwingend notwendig, muss ich das lernen ? Und welche Programmiersprache ist am besten für ein Spiel geeignet, ich les eigentlich immer nur C/C++ muss ich das jetzt lernen ?

      Wenn mein Ziel ein Optisch perfektes und Komplexes 3D Spiel ist, dann wird man wohl nicht um C++ + DirectX/OpenGL oder C# + XNA rum kommen. Aber es ist durchaus möglich ein Spiel - welches auch Spaß macht zu spielen - ohne DirectX, OpenGL oder XNA.
      Und das sogar noch in der Programmiersprache meiner Wahl.

      Ich habe mich jetzt mal für Visual Basic 2008 mit Hilfe des GDI+ entschieden, weil ich grad nur das Installiert habe.

      Jetzt kenne ich mein Spiel und die mittel die mir zur verfügung stehen um das Spiel in bunte Bilder auf den Monitor zu bringen.


      Auf gehts!

      Kapitel 2: Projektstart

      Als erstes wird mal ein neues Projekt unter Visual Basic 2008 erstellt:


      Diesem Projekt habe ich den Namen "MenschAergerDichNicht" gegeben. Ich finde eigentlich sollte man sich beim Programmieren an die Englische Sprache halten doch muss ich gestehen, das mir für dieses Spiel kein vernünftiger englischer Name eingefallen ist.

      Nach der erstellung des Projekts seh ich auch schon mein Hauptformular, welchem ich erstmal einen vernünftigen Namen verpasse.


      Was sieht man als erstes, wenn man ein Spiel startet ? Ein "Menü" welches einem z.B. die Möglichkeiten bietet ein neues Spiel zu starten, ein altes wieder aufzunehmen oder das Spiel zu beenden.

      Das ganze soll grafisch natürlich nicht nach einem Windows Formular aussehen, ich habe das nackte Spielfeld als Hintergrund überlegt. Aber wie bekommt man jetzt die Grafik in das Menü?

      Kapitel 3: Das Hauptmenü

      Es heisst zwar "Function follows form" aber ich denke es schadet nicht, wenn man sich direkt um die Visualisierung kümmert, so kommt auch ein wenig abwechslung in die ganze Sache rein, aber jeder muss für sich selber entscheiden was für ihn am produktivsten ist.

      Wir wollen nun ein Bild als Hintergrund unseres Hauptmenü anzeigen. Das kann man ganz einfach tun indem man die Eigenschaft "BackgroundImage" bearbeitet und sich sein gewünschten Hintergrund auswählt.

      Aber zuerst muss das Bild irgendwie in das Programm.

      Hier kommt der Ressource manager des Visual Studios zum einsatz. Hier kann man alle seine Spielrelevanten Daten abspeichern. Für den Anfang wollen wir hier das Hintergrundbild ablegen:



      nun kann ich das Bild als Hintergrundbild meines Formulars auswählen. Die Eigenschaft "FormBorderStyle" eignet sich gut um den Rahmen der Windows Form einfach auszublenden. Nachdem ich das Bild als hintergrundbild ausgewählt habe und den BorderStyle auf None gestellt habe sehe ich nach dem Kompilieren folgendes Ergebnis:



      Moment! Das sieht aber nicht nach einem fertigen Menü aus ! Die Grafik die ich dort verwendet hat, wird später auch als Spielbrett dienen, da ich aber nicht für jede Situation eine neue 1024x768 große Grafik erstellen möchte, habe ich alle Grafikinhalte in viele kleine Grafiken aufgeteilt.

      Für das eigentliche Menü habe ich zum Beispiel folgende Grafik erstellt:


      Aber wie bekommt man das zusätzlich auf das Formular ?

      Da hilft die Paint-Methode des Formulars. In der Paint-Methode muss das Menü gerendert werden.

      Doch erstmal muss auch die Menügrafik in den Ressourcemanager geladen werden.


      Jetzt wollen wir doch mal sehen wie wir das Menü auf den Bildschirm bekommen...

      Wir öffnen unsere Paint methode und fügen folgenden Code ein:

      VB.NET-Quellcode

      1. Private Sub frmMain_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
      2. e.Graphics.DrawImageUnscaled(My.Resources.menu, New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      3. End Sub


      Was passiert genau ?
      Mittels der Methode DrawImageUnscaled können wir ein Bild unskaliert auf unserem Formular zeichnen. Diese Methode hat mehrere Überlagerungen, eine davon erwartet genau zwei Parameter:

      VB.NET-Quellcode

      1. image as System.Drawing.Image 'Bild welches gezeichnet werden soll
      2. point as System.Drawing.Point 'Koordinaten der Oberen linken ecke

      Glücklicherweise stellt der Ressourcemanager unsere hinzugefügten Bild als System.Drawing.Image zur verfügung und wir können mit

      VB.NET-Quellcode

      1. My.Resources.[RESOURCENAME]
      auf unser "menu-Bild" zugreifen.
      Beim zweiten parameter berechnen wir die Koordinaten so, dass das Menü immer in der Mitte des Bildes gerendert wird.



      Doch so ein Menü besteht ja aus mehr als einem einfachen Rahmen. In das Menü gehören auch Menüitems. Es gibt vielleicht auch mehr als nur ein Einziges Menü. Also muss ein Ansatz her, der es ermöglicht ein beliebiges Menü an einer beliebigen stelle zu Rendern.

      Doch wie erreicht man so etwas ohne den Überblick zu verlieren?

      Kapitel 4: Klassen

      Wir brauchen erstmal eine Klasse, welches es uns ermöglicht ein Menü-Rahmen an einer Position zu zeichnen. Dazu fügen wir unserem Projekt eine neue Klasse mit dem Namen "Menu.vb" hinzu.


      Was muss alles über ein Menü gespeichert werden ?

      Als erstes mal wollen wir uns darum kümmern, das wir das Menü an einer bestimmten Position rendern können.

      VB.NET-Quellcode

      1. Public Class Menu
      2. Private m_Position As New Point
      3. Public Sub New(ByVal Position As Point)
      4. m_Position = Position
      5. End Sub
      6. Public Sub Draw(ByVal spriteBatch As System.Drawing.Graphics)
      7. spriteBatch.DrawImageUnscaled(My.Resources.menu, m_Position)
      8. End Sub
      9. End Class


      Dazu brauchen wir nur eine Variable, welche die Aktuelle Position des Menus speichert und eine Methode, welche das Menu zeichnet.

      Da in diesem Spiel nur eine Art von Menü benötigt wird, nehme ich immer das "menu" bild zum rendern der Grafik.

      Des weiteren habe ich ein Modul zu dem Projekt hinzugefügt, welches nur dafür zuständig ist, die einzelnen Menüs zu speichern, das Modul heisst "Menus.vb" und hat bis jetzt folgenden inhalt:

      VB.NET-Quellcode

      1. Module Menus
      2. Public MainMenu As Menu
      3. End Module


      Jetzt müssen wir noch dafür sorgen, dass das Menü auch initialisiert wird, das geschieht in der Load methode unseres Hauptformulars:

      VB.NET-Quellcode

      1. Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      2. MainMenu = New Menu(New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      3. End Sub


      Unsere Paint Methode benötigt auch noch ein Kleines Update:

      VB.NET-Quellcode

      1. Private Sub frmMain_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
      2. MainMenu.Draw(e.Graphics)
      3. End Sub


      Wenn wir das Projekt jetzt starten sollte sich rein Visuell nichts verändert haben.

      Jetzt benötigt noch jedes Menü ein paar Einträge, denn ohne Menüeinträge würde das ganze keinen Sinn ergeben.
      Also brauchen wir noch eine Klasse für einen Menüeintrag:


      Auch diese Klasse wollen wir nun mit Informationen füttern.

      VB.NET-Quellcode

      1. Public Class MenuItem
      2. Private m_idleImage As Bitmap
      3. Private m_overImage As Bitmap
      4. Private m_Position As Point
      5. Private m_bMouseIsOver As Boolean = False
      6. Public Property Position() As Point
      7. Get
      8. Return m_Position
      9. End Get
      10. Set(ByVal value As Point)
      11. m_Position = value
      12. End Set
      13. End Property
      14. Public ReadOnly Property ImageHeight()
      15. Get
      16. Return m_idleImage.Height
      17. End Get
      18. End Property
      19. Public ReadOnly Property ImageWidth()
      20. Get
      21. Return m_idleImage.Width
      22. End Get
      23. End Property
      24. Public Sub New(ByVal idleImage As Bitmap, ByVal overImage As Bitmap)
      25. m_idleImage = idleImage
      26. m_overImage = overImage
      27. End Sub
      28. Public Sub Draw(ByVal spriteBatch As System.Drawing.Graphics)
      29. Dim tmpImage As Bitmap = m_idleImage
      30. If m_bMouseIsOver Then tmpImage = m_overImage
      31. spriteBatch.DrawImageUnscaled(tmpImage, m_Position)
      32. End Sub
      33. End Class


      Nicht gleich erschlagen fühlen, es handelt sich hierbei eigentlich nur um ein paar Information die einfach notwendig sind ;)
      In der Klasse werden erstmal nur die Bilder für den Menüeintrag gespeichert und eine Variable die Speichert ob die Maus über dem Item ist und selbstverständlich die Position, an der der Eintrag gerendert werden soll. Wozu die Eigenschaften verwendet werden ergibt sich später.

      Jetzt wollen wir unser MainMenu mal mit Daten füllen.
      Dafür müssen wir erstmal die Klasse "Menu" anpassen, denn in dieser Klasse müssen nun alle Einträge, die dieses Menü besitzen sollen gespeichert werden. Dazu erweitern wir die klasse um folgendes:

      VB.NET-Quellcode

      1. Private m_Items As New List(Of MenuItem)
      2. Public Sub AddMenuItem(ByVal MenuItem As MenuItem)
      3. m_Items.Add(MenuItem)
      4. MenuItem.Position = New Point(m_Position.X + (My.Resources.menu.Width / 2) - (MenuItem.ImageWidth / 2), m_Position.Y + 20 + ((m_Items.Count - 1) * (MenuItem.ImageHeight + 10)))
      5. End Sub



      Nun wollen wir eine Methode schreiben, welches unser MainMenu mit Daten füttert. Dazu wird das Modul "Menus" um folgende Methode erweitert:

      VB.NET-Quellcode

      1. Public Sub create_MainMenu()
      2. MainMenu.AddMenuItem(New MenuItem(My.Resources.newgame, My.Resources.newgame_over))
      3. End Sub



      Die zwei Grafiken stellen später einen einfachen Button zum anklicken dar, hier die Grafiken die ich erstellt habe:

      Jetzt muss die Funktion, welche das Menü erstellt auch noch bei Programmstart aufgerufen werden:

      VB.NET-Quellcode

      1. Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      2. create_MainMenu()
      3. End Sub


      Halt ! Wir haben ja ganz vergessen, dass diese MenuItems auch noch gerendert werden müssen. Dazu müssen wir in der Klasse "Menu" die Methode Draw um folgende Zeilen erweitern:

      VB.NET-Quellcode

      1. For i As Integer = 0 To m_Items.Count - 1
      2. m_Items(i).Draw(spriteBatch)
      3. Next i


      Wenn alles richtig gelaufen ist, dann sieht euer Menü nun ungefähr so aus:



      Man kann jetzt noch folgende Menüpunkte hinzufügen:
      • Spiel laden
      • Spiel beenden

      Danach könnte es ungefähr so aussehen (nach Änderung eines bestimmten Wertes ;))

      Kapitel 5: Input

      Der Input ist eine ganz wichtige Sache bei der Spieleprogrammierung, ohne Input kein Spiel ;)

      Was ich in diesem Kapitel angehen werde ist der Input in einem Menü. Alles was man dazu benötigt ist eine Funktion die überprüft, wo sich die Maus befindet. Das ganze natürlich in der "menu"-Klasse.

      Dazu erweiteren wir die Klasse um folgende Methode:

      VB.NET-Quellcode

      1. Public Function updateMouse(ByVal mousePos As Point, ByVal mouseDown As Boolean) As Boolean
      2. Dim r1 As Rectangle = New Rectangle(mousePos.X, mousePos.Y, 1, 1)
      3. Dim r2 As Rectangle = New Rectangle(m_Position.X, m_Position.Y, My.Resources.menu.Width, My.Resources.menu.Height)
      4. If r1.IntersectsWith(r2) Then
      5. For i As Integer = 0 To m_Items.Count - 1
      6. If m_Items(i).updateMouse(mousePos, mouseDown) Then
      7. Exit For
      8. End If
      9. Next i
      10. Return True
      11. End If
      12. Return False
      13. End Function


      Diese Methode erstellt zwei Rechtecke, eins um die Maus und eins um das Menü, danach wird geprüft, ob die Rechtecke eine Schnittmenge haben, wenn ja, dann ist die Maus schonmal innerhalb des Menüs, danach wird noch für jeden einzelnen Menüeintrag die Schnittmenge mit der Maus überprüft, das geschieht natürlich in der "menuItem"-Klasse:

      VB.NET-Quellcode

      1. Public Function updateMouse(ByVal mousePos As Point, ByVal MouseDown As Boolean) As Boolean
      2. m_bMouseIsOver = False
      3. Dim r1 As Rectangle = New Rectangle(mousePos.X, mousePos.Y, 1, 1)
      4. Dim r2 As Rectangle = New Rectangle(m_Position.X, m_Position.Y, ImageWidth, ImageHeight)
      5. If r1.IntersectsWith(r2) Then
      6. m_bMouseIsOver = r1.IntersectsWith(r2)
      7. End If
      8. Return m_bMouseIsOver
      9. End Function


      Wo genau kommen nun aber die Maus-Koordinaten her ?
      Die kommen aus der MouseMove Methode des Hauptformulars:

      VB.NET-Quellcode

      1. Private Sub frmMain_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
      2. Select Case activeState
      3. Case GameStates.Menu
      4. If MainMenu.updateMouse(New Point(e.X, e.Y)) Then Me.Refresh()
      5. End Select
      6. End Sub


      Hier sieht man unter anderem einen aufzählungstyp activeState.
      Ich habe mittlerweile ein GameState Modul eingeführt, welches einfach folgende Information enthält:

      VB.NET-Quellcode

      1. Module Gamestate
      2. Public Enum GameStates
      3. Menu
      4. Game
      5. PlayerSelect
      6. Pause
      7. End Enum
      8. Public activeState As GameStates = GameStates.Menu
      9. End Module


      Diesen Status verwende ich jetzt auch in der Paint-Methode:

      VB.NET-Quellcode

      1. Private Sub frmMain_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
      2. Select Case activeState
      3. Case GameStates.Menu
      4. MainMenu.Draw(e.Graphics)
      5. End Select
      6. End Sub


      Dieser gameState hat verschiedene praktische Gründe:
      • Performenceverbesserungen
      • Übersichtlichkeit
      • Erweiterbarkeit
      Wenn man nun mit der Maus über einen Menüeintrag "fährt" sollte man folgendes Ergebnis bestaunen können:



      Das war es erstmal zu MouseMove, als nächstes schauen wir uns mal einen Mausklick an.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „bsHobbit“ ()

      Kapitel 6: Input Teil 2 (Delegates)

      Der Benutzer soll nun die Möglichkeit bekommen auch mal eines der Menüeinträge anzuklicken.
      Es gibt ja bereits eine Methode zum Prüfen ob die Maus sich auf einem der Einträge befindet, diese wird nun erweitert.

      Als erstes erweitern wir die Methode in der "menu"-Klasse:

      VB.NET-Quellcode

      1. Public Function updateMouse(ByVal mousePos As Point, ByVal mouseDown As Boolean) As Boolean
      2. Dim r1 As Rectangle = New Rectangle(mousePos.X, mousePos.Y, 1, 1)
      3. Dim r2 As Rectangle = New Rectangle(m_Position.X, m_Position.Y, My.Resources.menu.Width, My.Resources.menu.Height)
      4. If r1.IntersectsWith(r2) Then
      5. For i As Integer = 0 To m_Items.Count - 1
      6. If m_Items(i).updateMouse(mousePos, mouseDown) Then
      7. Exit For
      8. End If
      9. Next i
      10. Return True
      11. End If
      12. Return False
      13. End Function


      Es ist hier einfach ein weiterer Parameter hinzugekommen, der beinhaltet ob die Maus gedrückt wurde oder nicht.
      Diese Information machen wir uns in der Klasse "menuItem" zu nutze.

      VB.NET-Quellcode

      1. Public Function updateMouse(ByVal mousePos As Point, ByVal MouseDown As Boolean) As Boolean
      2. m_bMouseIsOver = False
      3. Dim r1 As Rectangle = New Rectangle(mousePos.X, mousePos.Y, 1, 1)
      4. Dim r2 As Rectangle = New Rectangle(m_Position.X, m_Position.Y, ImageWidth, ImageHeight)
      5. If r1.IntersectsWith(r2) Then
      6. m_bMouseIsOver = r1.IntersectsWith(r2)
      7. If MouseDown Then Call m_delegate()
      8. End If
      9. Return m_bMouseIsOver
      10. End Function


      Hier wird jetzt geprüft ob die Maus gedrückt wurde oder nicht, falls ja wird die Funktion, welche sich hinter dem Delegate befindet aufgerufen.

      Wo kommt dieser Delegate her und was macht er ?

      Ich habe ein Modul erstellt welches ich "Delegates.vb" getauft habe, in diesem Modul steht nichts weiter drin als folgendes:

      VB.NET-Quellcode

      1. Public Module Delegates
      2. Public Delegate Sub del_menuClick()
      3. End Module


      del_menuClick ist jetzt quasi ein Zeiger, der auf eine Methode verweist welche keine Parameter und kein Rückgabewert besitzt.

      Jetzt müssen wir diesen Delgate irgendwie in unsere Menüitems bekommen.

      Dazu muss die "menuItem"-Klasse um folgendes erweitert werden:

      VB.NET-Quellcode

      1. Private m_delegate As del_menuClick
      2. Public Sub New(ByVal idleImage As Bitmap, ByVal overImage As Bitmap, ByVal del As del_menuClick)
      3. m_idleImage = idleImage
      4. m_overImage = overImage
      5. m_delegate = del
      6. End Sub


      Jetzt wird auch schon der Kompiler an dem Modul "Menus" zu meckern haben, das die Parameter nicht mehr stimmen, deswegen wird auch diese Funktion erweitert:

      VB.NET-Quellcode

      1. Public Sub create_MainMenu(ByVal delNewGame As del_menuClick, ByVal delLoadGame As del_menuClick, ByVal delQuitGame As del_menuClick)
      2. MainMenu.AddMenuItem(New MenuItem(My.Resources.newgame, My.Resources.newgame_over, delNewGame))
      3. MainMenu.AddMenuItem(New MenuItem(My.Resources.loadgame, My.Resources.loadgame_over, delLoadGame))
      4. MainMenu.AddMenuItem(New MenuItem(My.Resources.quitgame, My.Resources.quitgame_over, delQuitGame))
      5. End Sub


      Und nun noch der letzte Schritt:

      Wir müssen die Methoden implementieren, die durch den Delegaten aufgerufen werden sollen.
      Dies geschieht (wer hätte es gedacht) in der "frmMain"-Klasse.
      Dort fügen wir erstmal folgende Methoden ein:

      VB.NET-Quellcode

      1. Private Sub NewGame()
      2. activeState = GameStates.PlayerSelect
      3. Me.Refresh()
      4. End Sub
      5. Private Sub QuitGame()
      6. Me.Close()
      7. End Sub
      8. Private Sub LoadGame()
      9. activeState = GameStates.LoadGame
      10. Me.Refresh()
      11. End Sub


      Jetzt stellt der Compiler schon wieder fest, das der Aufruf von create_MainMenu() nicht mehr stimmt, was ja auch korrekt ist, denn diese Methode wurde ja um drei Parameter erweitert, nämlich um unsere delegaten.

      Dann wollen wir diese Parameter mal mit unseren eben erstellten Methoden füllen:

      VB.NET-Quellcode

      1. Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      2. MainMenu = New Menu(New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      3. create_MainMenu(AddressOf NewGame, AddressOf LoadGame, AddressOf QuitGame)
      4. End Sub


      Jetzt hat sich zwar Optisch an dem Spiel nichts getan, aber wir sind einen großen Schritt weiter ;) denn nun ist der Benutzer in der Lage das Menü auch tatsächlich zu benutzen.

      Kapitel 7: Weitere Menüs

      Jetzt wollen wir die möglichkeit auszusuchen mit wieviel Spielern man das Spiel spielen möchte. Ich verzichte komplett auf die Möglichkeit gegen den Computer zu spielen, da es eh keinen Spaß macht ein Brettspiel gegen den Computer zu spielen ;) das könnte ich mir höchstens als erweiterung des tutorials denken.

      Das neue menü soll dem Benutzer folgende Möglichkeiten bieten:

      • 2Spieler
      • 3Spieler
      • 4Spieler
      • Zurück
      dazu muss man natürlich das modul "Menus" erweitern:

      VB.NET-Quellcode

      1. Public Sub create_PlayerMenu(ByVal del2Player As del_menuClick, ByVal del3Player As del_menuClick, ByVal del4Player As del_menuClick, ByVal delBack As del_menuClick)
      2. PlayerMenu.AddMenuItem(New MenuItem(My.Resources.twoplayer, My.Resources.twoplayer_over, del2Player))
      3. PlayerMenu.AddMenuItem(New MenuItem(My.Resources.threeplayer, My.Resources.threeplayer_over, del3Player))
      4. PlayerMenu.AddMenuItem(New MenuItem(My.Resources.fourplayer, My.Resources.fourplayer_over, del4Player))
      5. PlayerMenu.AddMenuItem(New MenuItem(My.Resources.back, My.Resources.back_over, delBack))
      6. End Sub


      Die Grafiken kann ich grad nicht hochladen ;(

      in der "frmMain"-Klasse muss natürlich auch ein wenig was angepasst werden:

      VB.NET-Quellcode

      1. Private Sub frmMain_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
      2. Select Case activeState
      3. Case GameStates.Menu
      4. MainMenu.Draw(e.Graphics)
      5. Case GameStates.PlayerSelect
      6. PlayerMenu.Draw(e.Graphics)
      7. End Select
      8. End Sub
      9. Private Sub frmMain_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseMove
      10. Select Case activeState
      11. Case GameStates.Menu
      12. If MainMenu.updateMouse(New Point(e.X, e.Y), False) Then Me.Refresh()
      13. Case GameStates.PlayerSelect
      14. If PlayerMenu.updateMouse(New Point(e.X, e.Y), False) Then Me.Refresh()
      15. End Select
      16. End Sub
      17. Private Sub frmMain_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles MyBase.MouseDown
      18. Select Case activeState
      19. Case GameStates.Menu
      20. MainMenu.updateMouse(New Point(e.X, e.Y), True)
      21. Case GameStates.PlayerSelect
      22. PlayerMenu.updateMouse(New Point(e.X, e.Y), True)
      23. End Select
      24. End Sub
      25. Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      26. MainMenu = New Menu(New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      27. PlayerMenu = New Menu(New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      28. create_MainMenu(AddressOf NewGame, AddressOf LoadGame, AddressOf QuitGame)
      29. create_PlayerMenu(AddressOf NewGame, AddressOf NewGame, AddressOf NewGame, AddressOf NewGame)
      30. End Sub


      Hier ignoriert ihr jetzt einfach mal, das bei create_PlayerMenu überall der selbe Parameterwert angegeben wird ;) Es gibt einfach noch keine Konkreten Methoden für diese Funktionen.

      Das ganze sollte nachdem man auf "Neues Spiel" geklickt hat wie folgt aussehen:


      So einfach ist es nun dank der kleinen vorarbeit neue Menüs zu erstellen.:)

      Kapitel 8: Das Spielfeld

      Das Spielfeld von Mensch äregere dich nicht ist denke ich jedem bekannt. Dieses Spielfeld muss nun irgendwie erzeugt und gerendert werden. Dazu habe ich eine Klasse erstellt, welche sich nur mit dem Spielfeld befassen soll:

      VB.NET-Quellcode

      1. Public Class gameField
      2. Private m_StartposYellow As New Point(10, 10)
      3. Private m_StartposGreen As New Point(900, 10)
      4. Private m_StartposRed As New Point(10, 640)
      5. Private m_StartposBlue As New Point(900, 640)
      6. Private m_Field(39) As Point
      7. Private m_finishRed(3) As Point
      8. Private m_finishYellow(3) As Point
      9. Private m_finishGreen(3) As Point
      10. Private m_finishBlue(3) As Point

      VB.NET-Quellcode

      1. Private Sub createField()
      2. Dim pH As Integer = My.Resources.fieldWhite.Height
      3. Dim pW As Integer = My.Resources.fieldWhite.Width
      4. Dim offX As Integer = 25
      5. Dim offY As Integer = 25
      6. m_Field(0) = New Point(400, 640 + My.Resources.fieldRed.Height + 15)
      7. m_Field(1) = New Point(m_Field(0).X, m_Field(0).Y - pH - offY)
      8. m_Field(2) = New Point(m_Field(1).X, m_Field(1).Y - pH - offY)
      9. m_Field(3) = New Point(m_Field(2).X, m_Field(2).Y - pH - offY)
      10. m_Field(4) = New Point(m_Field(3).X, m_Field(3).Y - pH - offY)
      11. m_Field(5) = New Point(m_Field(4).X - pW - offX, m_Field(4).Y)
      12. m_Field(6) = New Point(m_Field(5).X - pW - offX, m_Field(5).Y)
      13. m_Field(7) = New Point(m_Field(6).X - pW - offX, m_Field(6).Y)
      14. m_Field(8) = New Point(m_Field(7).X - pW - offX, m_Field(7).Y)
      15. m_Field(9) = New Point(m_Field(8).X, m_Field(8).Y - pH - offY)
      16. m_Field(10) = New Point(m_Field(9).X, m_Field(9).Y - pH - offY)
      17. m_Field(11) = New Point(m_Field(10).X + pW + offX, m_Field(10).Y)
      18. m_Field(12) = New Point(m_Field(11).X + pW + offX, m_Field(11).Y)
      19. m_Field(13) = New Point(m_Field(12).X + pW + offX, m_Field(12).Y)
      20. m_Field(14) = New Point(m_Field(13).X + pW + offX, m_Field(13).Y)
      21. m_Field(15) = New Point(m_Field(14).X, m_Field(14).Y - pH - offY)
      22. m_Field(16) = New Point(m_Field(15).X, m_Field(15).Y - pH - offY)
      23. m_Field(17) = New Point(m_Field(16).X, m_Field(16).Y - pH - offY)
      24. m_Field(18) = New Point(m_Field(17).X, m_Field(17).Y - pH - offY)
      25. m_Field(19) = New Point(m_Field(18).X + pW + offX, m_Field(18).Y)
      26. m_Field(20) = New Point(m_Field(19).X + pW + offX, m_Field(19).Y)
      27. m_Field(21) = New Point(m_Field(20).X, m_Field(20).Y + pH + offY)
      28. m_Field(22) = New Point(m_Field(21).X, m_Field(21).Y + pH + offY)
      29. m_Field(23) = New Point(m_Field(22).X, m_Field(22).Y + pH + offY)
      30. m_Field(24) = New Point(m_Field(23).X, m_Field(23).Y + pH + offY)
      31. m_Field(25) = New Point(m_Field(24).X + pW + offX, m_Field(24).Y)
      32. m_Field(26) = New Point(m_Field(25).X + pW + offX, m_Field(25).Y)
      33. m_Field(27) = New Point(m_Field(26).X + pW + offX, m_Field(26).Y)
      34. m_Field(28) = New Point(m_Field(27).X + pW + offX, m_Field(27).Y)
      35. m_Field(29) = New Point(m_Field(28).X, m_Field(28).Y + pH + offY)
      36. m_Field(30) = New Point(m_Field(29).X, m_Field(29).Y + pH + offY)
      37. m_Field(31) = New Point(m_Field(30).X - pW - offX, m_Field(30).Y)
      38. m_Field(32) = New Point(m_Field(31).X - pW - offX, m_Field(31).Y)
      39. m_Field(33) = New Point(m_Field(32).X - pW - offX, m_Field(32).Y)
      40. m_Field(34) = New Point(m_Field(33).X - pW - offX, m_Field(33).Y)
      41. m_Field(35) = New Point(m_Field(34).X, m_Field(34).Y + pH + offY)
      42. m_Field(36) = New Point(m_Field(35).X, m_Field(35).Y + pH + offY)
      43. m_Field(37) = New Point(m_Field(36).X, m_Field(36).Y + pH + offY)
      44. m_Field(38) = New Point(m_Field(37).X, m_Field(37).Y + pH + offY)
      45. m_Field(39) = New Point(m_Field(38).X - pW - offX, m_Field(38).Y)
      46. m_finishRed(0) = New Point(m_Field(39).X, m_Field(39).Y - pH - offY)
      47. m_finishRed(1) = New Point(m_finishRed(0).X, m_finishRed(0).Y - pH - offY)
      48. m_finishRed(2) = New Point(m_finishRed(1).X, m_finishRed(1).Y - pH - offY)
      49. m_finishRed(3) = New Point(m_finishRed(2).X, m_finishRed(2).Y - pH - offY)
      50. m_finishYellow(0) = New Point(m_Field(9).X + pW + offX, m_Field(9).Y)
      51. m_finishYellow(1) = New Point(m_finishYellow(0).X + pW + offX, m_finishYellow(0).Y)
      52. m_finishYellow(2) = New Point(m_finishYellow(1).X + pW + offX, m_finishYellow(1).Y)
      53. m_finishYellow(3) = New Point(m_finishYellow(2).X + pW + offX, m_finishYellow(2).Y)
      54. m_finishGreen(0) = New Point(m_Field(19).X, m_Field(19).Y + pH + offX)
      55. m_finishGreen(1) = New Point(m_finishGreen(0).X, m_finishGreen(0).Y + pH + offX)
      56. m_finishGreen(2) = New Point(m_finishGreen(1).X, m_finishGreen(1).Y + pH + offX)
      57. m_finishGreen(3) = New Point(m_finishGreen(2).X, m_finishGreen(2).Y + pH + offX)
      58. m_finishBlue(0) = New Point(m_Field(29).X - pW - offX, m_Field(29).Y)
      59. m_finishBlue(1) = New Point(m_finishBlue(0).X - pW - offX, m_finishBlue(0).Y)
      60. m_finishBlue(2) = New Point(m_finishBlue(1).X - pW - offX, m_finishBlue(1).Y)
      61. m_finishBlue(3) = New Point(m_finishBlue(2).X - pW - offX, m_finishBlue(2).Y)
      62. End Sub
      63. Public Sub New()
      64. createField()
      65. End Sub

      VB.NET-Quellcode

      1. Public Sub draw(ByVal spriteBatch As System.Drawing.Graphics)
      2. 'Startbereich gelb
      3. spriteBatch.DrawImageUnscaled(My.Resources.fieldYellow, m_StartposYellow)
      4. spriteBatch.DrawImageUnscaled(My.Resources.fieldYellow, New Point(m_StartposYellow.X + My.Resources.fieldYellow.Width + 15, m_StartposYellow.Y))
      5. spriteBatch.DrawImageUnscaled(My.Resources.fieldYellow, New Point(m_StartposYellow.X + My.Resources.fieldYellow.Width + 15, m_StartposYellow.Y + My.Resources.fieldYellow.Height + 15))
      6. spriteBatch.DrawImageUnscaled(My.Resources.fieldYellow, New Point(m_StartposYellow.X, m_StartposYellow.Y + My.Resources.fieldYellow.Width + 15))
      7. 'startbereich grün
      8. spriteBatch.DrawImageUnscaled(My.Resources.fieldGreen, m_StartposGreen)
      9. spriteBatch.DrawImageUnscaled(My.Resources.fieldGreen, New Point(m_StartposGreen.X + My.Resources.fieldGreen.Width + 15, m_StartposGreen.Y))
      10. spriteBatch.DrawImageUnscaled(My.Resources.fieldGreen, New Point(m_StartposGreen.X + My.Resources.fieldGreen.Width + 15, m_StartposGreen.Y + My.Resources.fieldGreen.Height + 15))
      11. spriteBatch.DrawImageUnscaled(My.Resources.fieldGreen, New Point(m_StartposGreen.X, m_StartposGreen.Y + My.Resources.fieldGreen.Width + 15))
      12. 'startbereich rot
      13. spriteBatch.DrawImageUnscaled(My.Resources.fieldRed, m_StartposRed)
      14. spriteBatch.DrawImageUnscaled(My.Resources.fieldRed, New Point(m_StartposRed.X + My.Resources.fieldRed.Width + 15, m_StartposRed.Y))
      15. spriteBatch.DrawImageUnscaled(My.Resources.fieldRed, New Point(m_StartposRed.X + My.Resources.fieldRed.Width + 15, m_StartposRed.Y + My.Resources.fieldRed.Height + 15))
      16. spriteBatch.DrawImageUnscaled(My.Resources.fieldRed, New Point(m_StartposRed.X, m_StartposRed.Y + My.Resources.fieldRed.Width + 15))
      17. 'startbereich blau
      18. spriteBatch.DrawImageUnscaled(My.Resources.fieldBlue, m_StartposBlue)
      19. spriteBatch.DrawImageUnscaled(My.Resources.fieldBlue, New Point(m_StartposBlue.X + My.Resources.fieldBlue.Width + 15, m_StartposBlue.Y))
      20. spriteBatch.DrawImageUnscaled(My.Resources.fieldBlue, New Point(m_StartposBlue.X + My.Resources.fieldBlue.Width + 15, m_StartposBlue.Y + My.Resources.fieldBlue.Height + 15))
      21. spriteBatch.DrawImageUnscaled(My.Resources.fieldBlue, New Point(m_StartposBlue.X, m_StartposBlue.Y + My.Resources.fieldBlue.Width + 15))
      22. For i As Integer = 0 To m_Field.Length - 1
      23. spriteBatch.DrawImageUnscaled(My.Resources.fieldWhite, m_Field(i))
      24. Next i
      25. For i As Integer = 0 To m_finishRed.Length - 1
      26. spriteBatch.DrawImageUnscaled(My.Resources.fieldRed, m_finishRed(i))
      27. spriteBatch.DrawImageUnscaled(My.Resources.fieldYellow, m_finishYellow(i))
      28. spriteBatch.DrawImageUnscaled(My.Resources.fieldGreen, m_finishGreen(i))
      29. spriteBatch.DrawImageUnscaled(My.Resources.fieldBlue, m_finishBlue(i))
      30. Next i
      31. End Sub
      32. End Class


      Wie man sieht, sehr sehr viel schreibarbeit, ich habe für jedes Spielfeld die Koordinate einzeln angegeben, weil mir ehrlich gesagt grad nichts besseres eingefallen ist ;)

      Jetzt hab ich in der "frmMain"-Klasse noch folgende Änderungen eingebaut:

      VB.NET-Quellcode

      1. Private gField As New gameField
      2. Private Sub _2Player()
      3. activeState = GameStates.Game
      4. Me.Refresh()
      5. End Sub
      6. Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      7. MainMenu = New Menu(New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      8. PlayerMenu = New Menu(New Point((Me.Width / 2) - (My.Resources.menu.Width / 2), (Me.Height / 2) - (My.Resources.menu.Height / 2)))
      9. create_MainMenu(AddressOf NewGame, AddressOf LoadGame, AddressOf QuitGame)
      10. create_PlayerMenu(AddressOf _2Player, AddressOf NewGame, AddressOf NewGame, AddressOf NewGame)
      11. End Sub
      12. Private Sub frmMain_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
      13. Select Case activeState
      14. Case GameStates.Menu
      15. MainMenu.Draw(e.Graphics)
      16. Case GameStates.PlayerSelect
      17. PlayerMenu.Draw(e.Graphics)
      18. Case GameStates.Game
      19. gField.draw(e.Graphics)
      20. End Select
      21. End Sub


      Und danach sollte das Spiel nach auswahl von zwei Spieler wie folgt aussehen: