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:
Snake:
Verbesserungsvorschläge werden immer gerne gelesen
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
- Public Class Form_Main
- Dim WithEvents Timer_GameTick As New Timer With {.Interval = 70, .Enabled = True}
- Dim WithEvents TheSnake As Snake
- Dim TargetPoint As Point
- Dim FieldsX As Integer = 30
- Dim FieldsY As Integer = 30
- Dim Rnd As New Random
- Private Sub Initialize() Handles MyBase.Load
- NewSnake()
- TargetPointEaten()
- End Sub
- 'Schlange erstellen
- Private Sub NewSnake()
- TheSnake = New Snake(New Point(Rnd.Next(0, FieldsX), Rnd.Next(0, FieldsY)), Keys.Up, Keys.Down, Keys.Left, Keys.Right)
- End Sub
- 'Das "Fressen" wurde gefressen
- Private Sub TargetPointEaten() Handles TheSnake.TargetEaten
- 'Wenn die Schlange so lang ist, dass 1/4 aller Felder besetzt ist hat der Spieler gewonnen;
- 'Das kann noch angepasst werden
- If TheSnake.Lenght >= Math.Floor(FieldsX * FieldsY / 4) Then
- Timer_GameTick.Enabled = False
- Me.Invalidate()
- MessageBox.Show("Gewonnen!")
- NewSnake()
- Timer_GameTick.Enabled = True
- End If
- 'Das neue "Fressen" wird so lange neu generiert, bis es sich nicht in der Schlange befindet
- 'und es mindestens 4 Felder vom Kopf entfernt ist.
- Do
- TargetPoint = New Point(Rnd.Next(0, FieldsX), Rnd.Next(0, FieldsY))
- Loop While TheSnake.Points.Contains(TargetPoint) OrElse (TargetPoint.X - TheSnake.Points.Last.X) ^ 2 + (TargetPoint.Y - TheSnake.Points.Last.Y) ^ 2 < 4 ^ 2
- End Sub
- 'Die Schlange hat sich selbst gebissen
- Private Sub SelfEaten() Handles TheSnake.SelfEaten
- Timer_GameTick.Enabled = False
- Me.Invalidate()
- MessageBox.Show("Leider verloren!")
- NewSnake()
- TargetPointEaten()
- Timer_GameTick.Enabled = True
- End Sub
- 'Die Richtung der Schlange setzen;
- 'Durch die Angabe der Up, Down, Left und Right Keys der Schlange beim Instanzieren
- 'können so mehrere Schlangen auf dem Spielfeld sein (Es müsste eine Kollision untereinander abgefragt werden)
- Private Sub MeKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles Me.KeyDown
- TheSnake.SetDirection(e.KeyCode)
- End Sub
- Private Sub MeSizeChanged() Handles Me.SizeChanged
- Me.Invalidate()
- End Sub
- 'Der Zyklus des Spiels
- Private Sub GameTick() Handles Timer_GameTick.Tick
- TheSnake.Go(TargetPoint, FieldsX, FieldsY)
- 'Der Punktestand wird hier über Me.Text ausgegeben;
- 'Kann auch zwischengespeichert und manuell gezeichnet werden
- Me.Text = "Snake: " & TheSnake.Lenght.ToString & " von " & (CInt(Math.Floor(FieldsX * FieldsY / 4))).ToString & " Punkte"
- Me.Invalidate()
- End Sub
- 'Hier wird gezeichnet;
- 'Ein paar Berechnungen sind nötig um das Gezeichnete immer in der Mitte zu halten
- Private Sub MePaint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint
- Dim FieldWidth As Double
- Dim FieldHeight As Double
- Dim XOffset As Double = 0
- Dim YOffset As Double = 0
- If Me.ClientRectangle.Height / Me.ClientRectangle.Width < FieldsY / FieldsX Then
- FieldWidth = (Me.ClientRectangle.Height / FieldsY) * FieldsX
- FieldHeight = Me.ClientRectangle.Height
- XOffset = (Me.ClientRectangle.Width - FieldWidth) / 2
- End If
- If Me.ClientRectangle.Height / Me.ClientRectangle.Width = FieldsY / FieldsX Then
- FieldWidth = Me.ClientRectangle.Width
- FieldHeight = Me.ClientRectangle.Height
- End If
- If Me.ClientRectangle.Height / Me.ClientRectangle.Width > FieldsY / FieldsX Then
- FieldWidth = Me.ClientRectangle.Width
- FieldHeight = (Me.ClientRectangle.Width / FieldsX) * FieldsY
- YOffset = (Me.ClientRectangle.Height - FieldHeight) / 2
- End If
- Dim BlockSize As Double = FieldWidth / FieldsX 'Die Felder sind Quadratisch, es könnte also auch FieldHeight / FieldsY verwendet werden
- 'Rahmen außen
- e.Graphics.DrawRectangle(Pens.Black, CSng(XOffset), CSng(YOffset), CSng(FieldWidth - 1), CSng(FieldHeight - 1))
- 'Felder der Schlange
- For Each i As Point In TheSnake.Points
- e.Graphics.FillRectangle(Brushes.DarkGreen, CSng(XOffset + i.X * BlockSize), CSng(YOffset + i.Y * BlockSize), CSng(BlockSize), CSng(BlockSize))
- Next
- 'Das "Fressen"
- e.Graphics.FillEllipse(Brushes.DarkRed, CSng(XOffset + TargetPoint.X * BlockSize), CSng(YOffset + TargetPoint.Y * BlockSize), CSng(BlockSize), CSng(BlockSize))
- 'Der Kopf der Schlange wird noch mal mit einem helleren Grün hervorgehoben;
- 'Das dient der Erkennbarkeit
- e.Graphics.FillRectangle(Brushes.Lime, CSng(XOffset + TheSnake.Points.Last.X * BlockSize), CSng(YOffset + TheSnake.Points.Last.Y * BlockSize), CSng(BlockSize), CSng(BlockSize))
- End Sub
- End Class
Snake:
VB.NET-Quellcode
- Public Class Snake
- 'Gibt die Richtung an, in die sich die Schlange bewegen soll
- Public Enum SnakeDirection As Integer
- None = 0
- Left = 1
- Right = 2
- Up = 3
- Down = 4
- End Enum
- 'Die Properties sollten sich weitestgehend selbst erklären
- Public Shared Property LenghtIncrement As Integer = 5
- Public Event TargetEaten()
- Public Event SelfEaten()
- Public Property Lenght As Integer = 5
- Public Property Points As New List(Of Point)
- Dim LastDirection As SnakeDirection = SnakeDirection.None
- Dim ActualDirection As SnakeDirection = SnakeDirection.None
- 'Die Tasten können für jede Schlange separat angegeben werden;
- 'Dadurch können mehrere Schlangen auf dem Spielfeld sein
- Public Property UpKey As Keys
- Public Property DownKey As Keys
- Public Property LeftKey As Keys
- Public Property RightKey As Keys
- '0815 Konstruktor;
- 'Startpunkt hinzufügen und Keys übernehmen
- Public Sub New(ByVal StartPoint As Point, ByVal NewUpKey As Keys, ByVal NewDownKey As Keys, ByVal NewLeftKey As Keys, ByVal NewRightKey As Keys)
- Points.Add(StartPoint)
- UpKey = NewUpKey
- DownKey = NewDownKey
- LeftKey = NewLeftKey
- RightKey = NewRightKey
- End Sub
- 'Hier wird die neue Richtung der Schlange gesetzt
- Public Sub SetDirection(ByVal Key As Keys)
- Select Case Key
- Case UpKey
- 'Die Schlange darf keine 180° Wende machen;
- 'Da man zwischen zwei Ticks mehrere Male die Richtung ändern kann
- 'wird immer die letzte Richtung überprüft
- If Not LastDirection = SnakeDirection.Down Then
- ActualDirection = SnakeDirection.Up
- End If
- Case DownKey
- If Not LastDirection = SnakeDirection.Up Then
- ActualDirection = SnakeDirection.Down
- End If
- Case LeftKey
- If Not LastDirection = SnakeDirection.Right Then
- ActualDirection = SnakeDirection.Left
- End If
- Case RightKey
- If Not LastDirection = SnakeDirection.Left Then
- ActualDirection = SnakeDirection.Right
- End If
- End Select
- End Sub
- 'Hier "kriecht" die Schlange
- Public Sub Go(ByVal TargetPoint As Point, ByVal FieldsX As Integer, ByVal FieldsY As Integer)
- 'Die gesetzte Richtung wird übernommen
- LastDirection = ActualDirection
- 'Falls keine Richtung gesetzt ist (z.B. Am Anfang des Spiels) passiert nichts
- If Not ActualDirection = SnakeDirection.None Then
- Dim NewPoint As Point
- 'Anhand der Richtung wird ein neuer Punkt gesucht
- Select Case ActualDirection
- Case SnakeDirection.Up
- NewPoint = New Point(Points.Last.X, Points.Last.Y - 1)
- Case SnakeDirection.Down
- NewPoint = New Point(Points.Last.X, Points.Last.Y + 1)
- Case SnakeDirection.Left
- NewPoint = New Point(Points.Last.X - 1, Points.Last.Y)
- Case SnakeDirection.Right
- NewPoint = New Point(Points.Last.X + 1, Points.Last.Y)
- End Select
- 'Auf der einen Seite raus, auf der anderen wieder rein
- If NewPoint.X > FieldsX - 1 Then
- NewPoint = New Point(0, NewPoint.Y)
- ElseIf NewPoint.X < 0 Then
- NewPoint = New Point(FieldsX - 1, NewPoint.Y)
- End If
- If NewPoint.Y > FieldsY - 1 Then
- NewPoint = New Point(NewPoint.X, 0)
- ElseIf NewPoint.Y < 0 Then
- NewPoint = New Point(NewPoint.X, FieldsY - 1)
- End If
- 'Prüfen, ob sich die Schlange selbst beißt
- If Points.Contains(NewPoint) Then
- RaiseEvent SelfEaten()
- End If
- Points.Add(NewPoint)
- 'Prüfen, ob die Schlange das "Fressen" frisst
- If TargetPoint = NewPoint Then
- Lenght += LenghtIncrement
- RaiseEvent TargetEaten()
- End If
- RemoveLast()
- End If
- End Sub
- 'Wenn die Schlange länger ist als das Maximum wird der Schwanz abgeschnitten;
- 'Der Kopf ist immer Points.Last und der Schwanz ist immer Points.First
- Private Sub RemoveLast()
- Do While Points.Count > Lenght
- Points.RemoveAt(0)
- Loop
- End Sub
- 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
-- Brady in Wonderland, 23. Februar 2015, 1:56
Desktop Pinner | ApplicationSettings | OnUtils
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Niko Ortner“ ()