Snake - Ein weiterer Versuch

    • VB.NET

      Snake - Ein weiterer Versuch

      Im Bezug auf diesen Post (Und den darin verlinkten Thread) möchte ich meine Variante von Snake zeigen.

      Meine Variante generiert nicht ein Spielfeld und bringt die Schlange dort unter, sondern generiert eine Schlange und sagt ihr wo sie sich im (nicht existenten) Spielfeld befindet. Das spart bei größeren Spielfeldern Speicher (wobei das mehr oder weniger trivial ist).
      Weiters ist es möglich anstelle der einzelnen Schlange "TheSnake" eine List(Of Snake) anzulegen und so mehrere Schlangen auf dem Spielfeld zu haben. Jede Schlange hat dabei ihre eigenen Tasten zugeordnet. Was noch fehlt ist eine Kollisionsprüfung der Schlangen untereinander und eine Variable, die angibt, ob die Schlange noch im Spiel ist.

      Was neben dem Sourcecode benötigt wird:
      Eine Form "Form_Main" (schön ist ein weißer Hintergrund) mit DoubleBuffered auf True
      Eine neu angelegte Klasse "Snake" (Kann auch in der "Form_Main" Datei sein)
      Framework:
      * Im Framework 4.0 erstellt
      * Ohne Probleme auf 3.5 runterschraubbar
      * Ab 3.0 gibt es Probleme (Liste.First), die sich allerdings einfach lösen lassen (Liste(Liste.Count - 1))
      * 2.0: Das Selbe wie bei 3.0

      Das Ergebnis:


      Der Sourcecode:

      Form_Main:

      VB.NET-Quellcode

      1. Public Class Form_Main
      2. Dim WithEvents Timer_GameTick As New Timer With {.Interval = 70, .Enabled = True}
      3. Dim WithEvents TheSnake As Snake
      4. Dim TargetPoint As Point
      5. Dim FieldsX As Integer = 30
      6. Dim FieldsY As Integer = 30
      7. Dim Rnd As New Random
      8. Private Sub Initialize() Handles MyBase.Load
      9. NewSnake()
      10. TargetPointEaten()
      11. End Sub
      12. 'Schlange erstellen
      13. Private Sub NewSnake()
      14. TheSnake = New Snake(New Point(Rnd.Next(0, FieldsX), Rnd.Next(0, FieldsY)), Keys.Up, Keys.Down, Keys.Left, Keys.Right)
      15. End Sub
      16. 'Das "Fressen" wurde gefressen
      17. Private Sub TargetPointEaten() Handles TheSnake.TargetEaten
      18. 'Wenn die Schlange so lang ist, dass 1/4 aller Felder besetzt ist hat der Spieler gewonnen;
      19. 'Das kann noch angepasst werden
      20. If TheSnake.Lenght >= Math.Floor(FieldsX * FieldsY / 4) Then
      21. Timer_GameTick.Enabled = False
      22. Me.Invalidate()
      23. MessageBox.Show("Gewonnen!")
      24. NewSnake()
      25. Timer_GameTick.Enabled = True
      26. End If
      27. 'Das neue "Fressen" wird so lange neu generiert, bis es sich nicht in der Schlange befindet
      28. 'und es mindestens 4 Felder vom Kopf entfernt ist.
      29. Do
      30. TargetPoint = New Point(Rnd.Next(0, FieldsX), Rnd.Next(0, FieldsY))
      31. Loop While TheSnake.Points.Contains(TargetPoint) OrElse (TargetPoint.X - TheSnake.Points.Last.X) ^ 2 + (TargetPoint.Y - TheSnake.Points.Last.Y) ^ 2 < 4 ^ 2
      32. End Sub
      33. 'Die Schlange hat sich selbst gebissen
      34. Private Sub SelfEaten() Handles TheSnake.SelfEaten
      35. Timer_GameTick.Enabled = False
      36. Me.Invalidate()
      37. MessageBox.Show("Leider verloren!")
      38. NewSnake()
      39. TargetPointEaten()
      40. Timer_GameTick.Enabled = True
      41. End Sub
      42. 'Die Richtung der Schlange setzen;
      43. 'Durch die Angabe der Up, Down, Left und Right Keys der Schlange beim Instanzieren
      44. 'können so mehrere Schlangen auf dem Spielfeld sein (Es müsste eine Kollision untereinander abgefragt werden)
      45. Private Sub MeKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles Me.KeyDown
      46. TheSnake.SetDirection(e.KeyCode)
      47. End Sub
      48. Private Sub MeSizeChanged() Handles Me.SizeChanged
      49. Me.Invalidate()
      50. End Sub
      51. 'Der Zyklus des Spiels
      52. Private Sub GameTick() Handles Timer_GameTick.Tick
      53. TheSnake.Go(TargetPoint, FieldsX, FieldsY)
      54. 'Der Punktestand wird hier über Me.Text ausgegeben;
      55. 'Kann auch zwischengespeichert und manuell gezeichnet werden
      56. Me.Text = "Snake: " & TheSnake.Lenght.ToString & " von " & (CInt(Math.Floor(FieldsX * FieldsY / 4))).ToString & " Punkte"
      57. Me.Invalidate()
      58. End Sub
      59. 'Hier wird gezeichnet;
      60. 'Ein paar Berechnungen sind nötig um das Gezeichnete immer in der Mitte zu halten
      61. Private Sub MePaint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint
      62. Dim FieldWidth As Double
      63. Dim FieldHeight As Double
      64. Dim XOffset As Double = 0
      65. Dim YOffset As Double = 0
      66. If Me.ClientRectangle.Height / Me.ClientRectangle.Width < FieldsY / FieldsX Then
      67. FieldWidth = (Me.ClientRectangle.Height / FieldsY) * FieldsX
      68. FieldHeight = Me.ClientRectangle.Height
      69. XOffset = (Me.ClientRectangle.Width - FieldWidth) / 2
      70. End If
      71. If Me.ClientRectangle.Height / Me.ClientRectangle.Width = FieldsY / FieldsX Then
      72. FieldWidth = Me.ClientRectangle.Width
      73. FieldHeight = Me.ClientRectangle.Height
      74. End If
      75. If Me.ClientRectangle.Height / Me.ClientRectangle.Width > FieldsY / FieldsX Then
      76. FieldWidth = Me.ClientRectangle.Width
      77. FieldHeight = (Me.ClientRectangle.Width / FieldsX) * FieldsY
      78. YOffset = (Me.ClientRectangle.Height - FieldHeight) / 2
      79. End If
      80. Dim BlockSize As Double = FieldWidth / FieldsX 'Die Felder sind Quadratisch, es könnte also auch FieldHeight / FieldsY verwendet werden
      81. 'Rahmen außen
      82. e.Graphics.DrawRectangle(Pens.Black, CSng(XOffset), CSng(YOffset), CSng(FieldWidth - 1), CSng(FieldHeight - 1))
      83. 'Felder der Schlange
      84. For Each i As Point In TheSnake.Points
      85. e.Graphics.FillRectangle(Brushes.DarkGreen, CSng(XOffset + i.X * BlockSize), CSng(YOffset + i.Y * BlockSize), CSng(BlockSize), CSng(BlockSize))
      86. Next
      87. 'Das "Fressen"
      88. e.Graphics.FillEllipse(Brushes.DarkRed, CSng(XOffset + TargetPoint.X * BlockSize), CSng(YOffset + TargetPoint.Y * BlockSize), CSng(BlockSize), CSng(BlockSize))
      89. 'Der Kopf der Schlange wird noch mal mit einem helleren Grün hervorgehoben;
      90. 'Das dient der Erkennbarkeit
      91. e.Graphics.FillRectangle(Brushes.Lime, CSng(XOffset + TheSnake.Points.Last.X * BlockSize), CSng(YOffset + TheSnake.Points.Last.Y * BlockSize), CSng(BlockSize), CSng(BlockSize))
      92. End Sub
      93. End Class


      Snake:

      VB.NET-Quellcode

      1. Public Class Snake
      2. 'Gibt die Richtung an, in die sich die Schlange bewegen soll
      3. Public Enum SnakeDirection As Integer
      4. None = 0
      5. Left = 1
      6. Right = 2
      7. Up = 3
      8. Down = 4
      9. End Enum
      10. 'Die Properties sollten sich weitestgehend selbst erklären
      11. Public Shared Property LenghtIncrement As Integer = 5
      12. Public Event TargetEaten()
      13. Public Event SelfEaten()
      14. Public Property Lenght As Integer = 5
      15. Public Property Points As New List(Of Point)
      16. Dim LastDirection As SnakeDirection = SnakeDirection.None
      17. Dim ActualDirection As SnakeDirection = SnakeDirection.None
      18. 'Die Tasten können für jede Schlange separat angegeben werden;
      19. 'Dadurch können mehrere Schlangen auf dem Spielfeld sein
      20. Public Property UpKey As Keys
      21. Public Property DownKey As Keys
      22. Public Property LeftKey As Keys
      23. Public Property RightKey As Keys
      24. '0815 Konstruktor;
      25. 'Startpunkt hinzufügen und Keys übernehmen
      26. Public Sub New(ByVal StartPoint As Point, ByVal NewUpKey As Keys, ByVal NewDownKey As Keys, ByVal NewLeftKey As Keys, ByVal NewRightKey As Keys)
      27. Points.Add(StartPoint)
      28. UpKey = NewUpKey
      29. DownKey = NewDownKey
      30. LeftKey = NewLeftKey
      31. RightKey = NewRightKey
      32. End Sub
      33. 'Hier wird die neue Richtung der Schlange gesetzt
      34. Public Sub SetDirection(ByVal Key As Keys)
      35. Select Case Key
      36. Case UpKey
      37. 'Die Schlange darf keine 180° Wende machen;
      38. 'Da man zwischen zwei Ticks mehrere Male die Richtung ändern kann
      39. 'wird immer die letzte Richtung überprüft
      40. If Not LastDirection = SnakeDirection.Down Then
      41. ActualDirection = SnakeDirection.Up
      42. End If
      43. Case DownKey
      44. If Not LastDirection = SnakeDirection.Up Then
      45. ActualDirection = SnakeDirection.Down
      46. End If
      47. Case LeftKey
      48. If Not LastDirection = SnakeDirection.Right Then
      49. ActualDirection = SnakeDirection.Left
      50. End If
      51. Case RightKey
      52. If Not LastDirection = SnakeDirection.Left Then
      53. ActualDirection = SnakeDirection.Right
      54. End If
      55. End Select
      56. End Sub
      57. 'Hier "kriecht" die Schlange
      58. Public Sub Go(ByVal TargetPoint As Point, ByVal FieldsX As Integer, ByVal FieldsY As Integer)
      59. 'Die gesetzte Richtung wird übernommen
      60. LastDirection = ActualDirection
      61. 'Falls keine Richtung gesetzt ist (z.B. Am Anfang des Spiels) passiert nichts
      62. If Not ActualDirection = SnakeDirection.None Then
      63. Dim NewPoint As Point
      64. 'Anhand der Richtung wird ein neuer Punkt gesucht
      65. Select Case ActualDirection
      66. Case SnakeDirection.Up
      67. NewPoint = New Point(Points.Last.X, Points.Last.Y - 1)
      68. Case SnakeDirection.Down
      69. NewPoint = New Point(Points.Last.X, Points.Last.Y + 1)
      70. Case SnakeDirection.Left
      71. NewPoint = New Point(Points.Last.X - 1, Points.Last.Y)
      72. Case SnakeDirection.Right
      73. NewPoint = New Point(Points.Last.X + 1, Points.Last.Y)
      74. End Select
      75. 'Auf der einen Seite raus, auf der anderen wieder rein
      76. If NewPoint.X > FieldsX - 1 Then
      77. NewPoint = New Point(0, NewPoint.Y)
      78. ElseIf NewPoint.X < 0 Then
      79. NewPoint = New Point(FieldsX - 1, NewPoint.Y)
      80. End If
      81. If NewPoint.Y > FieldsY - 1 Then
      82. NewPoint = New Point(NewPoint.X, 0)
      83. ElseIf NewPoint.Y < 0 Then
      84. NewPoint = New Point(NewPoint.X, FieldsY - 1)
      85. End If
      86. 'Prüfen, ob sich die Schlange selbst beißt
      87. If Points.Contains(NewPoint) Then
      88. RaiseEvent SelfEaten()
      89. End If
      90. Points.Add(NewPoint)
      91. 'Prüfen, ob die Schlange das "Fressen" frisst
      92. If TargetPoint = NewPoint Then
      93. Lenght += LenghtIncrement
      94. RaiseEvent TargetEaten()
      95. End If
      96. RemoveLast()
      97. End If
      98. End Sub
      99. 'Wenn die Schlange länger ist als das Maximum wird der Schwanz abgeschnitten;
      100. 'Der Kopf ist immer Points.Last und der Schwanz ist immer Points.First
      101. Private Sub RemoveLast()
      102. Do While Points.Count > Lenght
      103. Points.RemoveAt(0)
      104. Loop
      105. End Sub
      106. End Class


      Verbesserungsvorschläge werden immer gerne gelesen ^^
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils

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