Ein eigenes Onlinespiel - Basis

    • VB.NET

    Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von Epic.

      Ein eigenes Onlinespiel - Basis

      Hallo Leute,

      ich habe kurzer Hand entschlossen das Tutorial von kevin89 ([VB.NET] Multiserver (TCP)) in ein kleines "Onlinespiel" umzubauen.

      Vorwarnung: Ein echtes, großes Onlinespiel zu bauen, ist weit mehr Arbeit und gar nicht einfach, dass hier soll nur eine kleine Grundbasis sein.

      Zuallererst müssen wir wissen, wie ein Onlinespiel funktioniert, das habe ich einmal in zwei Teile aufgeteilt:


      Client + Server

      Der Client ist das eigentliche Spiel, in dem das Spiel angezeigt wird und ausgeführt.

      Client:

      • Zeigt die Spieloberfläche (Spieler, Objekte, Landschaft) an
      • Nimmt die Informationen des Servers auf
      • Schickt Informationen an den Server
      währen der Server "nur"
      • Daten aufnimmt & "speichert"
      • (Eventuell sendet er auch noch Informationen zurück, zum Beispiel beim Aufheben von Items)
      Aber trotzdem ist der Server wohl einer der wichtigsten Elemente für ein Onlinespiel.
      Das schauen wir uns einmal näher an.

      Wenn du zum Beispiel die "Vorwärts"-Taste in einem Onlinespiel (kürzen wir es nun mit OP ab), wird nicht nur der Player bewegt, sondern auch eine "Information" an den Server gesendet. So eine Information kann beliebig gross oder klein ausfallen, in unserem Beispiel senden wir aber nur einen String an den Server, der dann von den Clienten "geparst" wird:

      NameDesSpieler, X-Position, Y-Position

      Das kann dann so aussehen:

      Tobi,10,50

      Wichtig! Keine Leerzeichen!

      Dieser String - wie gesagt - wird an den Server gesendet und bei einer neuen Information von allen Clients heruntergeladen und "geparst". Das "Parsen" (wie ich es gerne nenne), teilt einfach jedes Wort oder jede Zahl nach jedem Komma aus und speichert es in eine Liste, das würde dann so aussehen:

      NameDesSpielers
      X-Position
      Y-Position

      Wir speichern da sganze - ich weiss, unschöne Methode, aber so geht es am besten - in ein ListViewControl. Das schöne an einer ListView ist die Sache mit den Spalten, den dort werde unsere aktuellen Daten abgespeichert:

      NameDesSpieler X-Position Y-Position
      Tobi 10 50


      Und nur durch die ListView wird es angezeigt, dass heißt, du könntest auch offline Daten einspeichern und dann werden auch nur die Daten aus der ListView mit GDI gezeichnet, denn es ist immer wichtig, Server und Client zu trennen!

      Das heißt, wir haben einen Anzeigebereich und den Serverbereich - in meinem sehr winzigen Beispiel teile ich das ganze nicht in Klassen unter.

      Kurz zusammengefasst: Wenn man eine Taste im Spiel drückt, senden wir einen Informationscode an den Server, bei einer neuen Information laden sich alle Clienten die neue Information herunter, heißt: Alles ist synchron (außer wenn ein Spieler vorher schon etwas gemacht hat wird es nicht mehr bei einem neuen Spieler angezeigt). Immer in 2 Teile deinen Clienten aufteilen: Anzeigen & Spiel, Server.

      Ich hoffe dass du diese Sache verstanden hast, den nun können wir weiter machen!
      Zuerst: Wie ich bereits gesagt habe, machen wir hier nur ein kleines Beispiel, mit dem 127.0.0.1 Server. Heißt: Noch nicht online.


      Nun, du bennötigst einen Server. Denn kannst du in VisualBasic entwickeln, kevin89 hat schon ein Beispiel gepostet (siehe: [VB.NET] Multiserver (TCP)). Du kannst den Server schon starten wenn du willst.


      Nun, erstelle ein neues VisualBasic-Projekt, mit einer Picturebox (alles Namen nach dem Schema: Control1) und einer Listview.

      Für die ListView gilt:

      • View: Details
      • (optional) Visible: False
      • Spalte Name
      • Spalte x
      • Spalte y
      Code für Form1:

      (ich weiss, der Code ist schrecklich, aber es funktioniert ;))

      VB.NET-Quellcode

      1. Imports System.Net.Sockets
      2. Imports System.IO
      3. Public Class Form1
      4. Private stream As NetworkStream
      5. Private streamw As StreamWriter
      6. Private streamr As StreamReader
      7. Private client As New TcpClient
      8. Private t As New Threading.Thread(AddressOf Listen)
      9. Private Delegate Sub DAddItem(ByVal s As String)
      10. Private nick As String = "unknown"
      11. Dim myx As Integer = -5
      12. Dim myy As Integer = 10
      13. Private Sub AddItem(ByVal s As String)
      14. Dim arr = s.Split(",")
      15. Dim list = arr.ToList
      16. Dim i = ListView1.Items.Add(list.Item(0))
      17. Dim one As String = list.Item(1)
      18. Dim two As String = list.Item(2)
      19. i.SubItems.Add(one)
      20. i.SubItems.Add(two)
      21. PictureBox1.Refresh()
      22. End Sub
      23. Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
      24. If e.KeyCode = Keys.Right Then
      25. myx += 5
      26. End If
      27. If e.KeyCode = Keys.Left Then
      28. myx -= 5
      29. End If
      30. If e.KeyCode = Keys.Up Then
      31. myy -= 5
      32. End If
      33. If e.KeyCode = Keys.Down Then
      34. myy += 5
      35. End If
      36. *C
      37. streamw.Flush()
      38. End Sub
      39. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
      40. Dim posx_rnd As New Random
      41. Dim posy_rnd As New Random
      42. Dim posx = posx_rnd.Next(15, Me.Width - 15)
      43. Dim posy = posy_rnd.Next(15, Me.Height - 15)
      44. myx = posx
      45. myy = posy
      46. nick = InputBox("Gib einen Spielernamen ein!")
      47. Me.KeyPreview = True
      48. Try
      49. client.Connect("127.0.0.1", 8000)
      50. If client.Connected Then
      51. stream = client.GetStream
      52. streamw = New StreamWriter(stream)
      53. streamr = New StreamReader(stream)
      54. streamw.WriteLine(nick)
      55. streamw.Flush()
      56. t.Start()
      57. Else
      58. MessageBox.Show("Verbindung zum Server nicht möglich!")
      59. Application.Exit()
      60. End If
      61. Catch ex As Exception
      62. MessageBox.Show("Verbindung zum Server nicht möglich!")
      63. Application.Exit()
      64. End Try
      65. End Sub
      66. Private Sub Listen()
      67. While client.Connected
      68. Try
      69. Me.Invoke(New DAddItem(AddressOf AddItem), streamr.ReadLine)
      70. Catch
      71. MessageBox.Show("Verbindung zum Server nicht möglich!")
      72. Application.Exit()
      73. End Try
      74. End While
      75. End Sub
      76. Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
      77. For Each item As ListViewItem In ListView1.Items
      78. Dim rnd1 As New Random
      79. Dim rnd2 As New Random
      80. Dim rnd3 As New Random
      81. Dim clr1 = rnd1.Next(1, 255)
      82. Dim clr2 = rnd2.Next(1, 255)
      83. Dim clr3 = rnd2.Next(1, 255)
      84. Dim nm_ As String = item.ToString
      85. nm_ = nm_.Replace("ListViewItem: {", "")
      86. nm_ = nm_.Replace("}", "")
      87. Dim nm As String = nm_
      88. Dim x_ As String = item.SubItems(1).ToString
      89. x_ = x_.Replace("ListViewSubItem: {", "")
      90. x_ = x_.Replace("}", "")
      91. Dim x As Single = x_
      92. Dim y_ As String = item.SubItems(2).ToString
      93. y_ = y_.Replace("ListViewSubItem: {", "")
      94. y_ = y_.Replace("}", "")
      95. Dim y As Single = y_
      96. Dim player As New Rectangle(x, y, 15, 15)
      97. e.Graphics.DrawRectangle(Pens.Transparent, player)
      98. e.Graphics.FillRectangle(New SolidBrush(Color.FromArgb(clr1, clr2, clr3)), player)
      99. Next
      100. End Sub
      101. End Class


      Zuerst: Dieser Code stellt eine Art Schlange dar, und jeder kann online mit anderen seine Schlange zeichnen (oder so...), keine Extras, das könntest du machen:

      • Chatbox
      • Onlinebox
      • Namenanzeigen (einfach)
      • Kollissionabfrage
      So, zum Code.

      Unteranderem habe ich nun eine Codezeile rausgeschnitten, damit du kein C&P machst, diese Codezeile gehört ins KeyDown-Event bei "*C" durch "streamw.WriteLine(nick & "," & myx & "," & myy)" ersetzt ;).

      Ich werde hier nur die neuen Sachen erklären, den Rest kann man in Vincents (auch bekannt unter kevin89 ;)) Tutorial nachlesen. Ich gehe Zeilenweise den Code durch.
      Zuerst gibt's die Variablen

      VB.NET-Quellcode

      1. Dim myx As Integer = -5
      2. Dim myy As Integer = 10


      Das sit die aktuelle Position des Spielers (x,y).

      Die Funktion AddItem - das wichtigste wohl - habe ich erweitert, hier ist der kleine "Parser" xD:

      VB.NET-Quellcode

      1. Private Sub AddItem(ByVal s As String)
      2. Dim arr = s.Split(",")
      3. Dim list = arr.ToList
      4. Dim lsv_item = ListView1.Items.Add(list.Item(0))
      5. Dim x As String = list.Item(1)
      6. Dim y As String = list.Item(2)
      7. lsv_item.SubItems.Add(x)
      8. lsv_item.SubItems.Add(y)
      9. PictureBox1.Refresh()
      10. End Sub
      (es gibt vermutlich bessere Varianten, dass war aber die erste Lösung die mir eingefallen ist)

      Erklärung:

      VB.NET-Quellcode

      1. Dim arr = s.Split(",")

      Wir splitten ("teilen") den Code in ein Array auf, es wird bei jedem Koma ein Eintrag hinzugefügt.

      VB.NET-Quellcode

      1. Dim list = arr.ToList

      VB.NET-Quellcode

      1. Dim lsv_item = ListView1.Items.Add(list.Item(0))

      Wir erstellen einen neuen ListView-Eintrag und fügen gleich das erste "gesplitterte" Wort hinzu, also den Namen.

      VB.NET-Quellcode

      1. Dim x As String = list.Item(1)
      2. Dim y As String = list.Item(2)

      Hier erstellen wir die Untereinträge ("SubItems") zu unserem Haupteintrag - aus dem Index der neuen Liste - hinzu.

      VB.NET-Quellcode

      1. lsv_item.SubItems.Add(x)
      2. lsv_item.SubItems.Add(y)

      Hier fügen wir diese nun der ListView hinzu.

      VB.NET-Quellcode

      1. PictureBox1.Refresh()

      Wir aktualisieren unsere Picturebox.
      Nächstes:

      VB.NET-Quellcode

      1. Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
      2. If e.KeyCode = Keys.Right Then
      3. myx += 5
      4. End If
      5. If e.KeyCode = Keys.Left Then
      6. myx -= 5
      7. End If
      8. If e.KeyCode = Keys.Up Then
      9. myy -= 5
      10. End If
      11. If e.KeyCode = Keys.Down Then
      12. myy += 5
      13. End If
      14. streamw.WriteLine(nick & "," & myx & "," & myy)
      15. streamw.Flush()
      16. End Sub


      VB.NET-Quellcode

      1. Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
      2. If e.KeyCode = Keys.Right Then
      3. myx += 5
      4. End If
      5. If e.KeyCode = Keys.Left Then
      6. myx -= 5
      7. End If
      8. If e.KeyCode = Keys.Up Then
      9. myy -= 5
      10. End If
      11. If e.KeyCode = Keys.Down Then
      12. myy += 5
      13. End If
      14. End Sub

      Wir setzten mit den Pfeiltasten unseren aktuellen Spieler.

      VB.NET-Quellcode

      1. streamw.WriteLine(nick & "," & myx & "," & myy)
      2. streamw.Flush()

      Wir senden die Informationen an unseren Server nach dem Schema:
      Nickname,x,y
      selbstverständlich säuberlich mit Kommas getrennt.

      Nächstes:

      VB.NET-Quellcode

      1. Dim posx_rnd As New Random
      2. Dim posy_rnd As New Random
      3. Dim posx = posx_rnd.Next(15, Me.Width - 15)
      4. Dim posy = posy_rnd.Next(15, Me.Height - 15)
      5. myx = posx
      6. myy = posy
      7. nick = InputBox("Gib einen Spielernamen ein!")
      8. Me.KeyPreview = True


      VB.NET-Quellcode

      1. Dim posx_rnd As New Random
      2. Dim posy_rnd As New Random
      3. Dim posx = posx_rnd.Next(15, Me.Width - 15)
      4. Dim posy = posy_rnd.Next(15, Me.Height - 15)

      Wir erstellen eine zufällige Position für unsere Spieler, massgeschneidert, so das der Spieler nicht vom Bildschirm verschwindet. (Das Rechteck das wir bald zeichen werden ist 15 px breit und hoch)

      VB.NET-Quellcode

      1. myx = posx
      2. myy = posy

      Wir setzten unsere generierten Positionen.

      VB.NET-Quellcode

      1. nick = InputBox("Gib einen Spielernamen ein!")

      Eine Eingabebox fordert - was woll sonst - zur Eingabe auf, hier den gewünschten Spiernamen.

      VB.NET-Quellcode

      1. Me.KeyPreview = True

      Nachdem man ja auch sein Objekt mit den Maustasten steuern kann und nicht außversehen in der ListView scrollt, setzten wir unsere Form als "Steuerungsmodul" ;).

      Nächstes (der folgende Code ist sehr unsauber und gehört in den Paint-Handler der Picturebox, wir zeichnen mit Gdi)

      VB.NET-Quellcode

      1. Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
      2. For Each item As ListViewItem In ListView1.Items
      3. Dim rnd1 As New Random
      4. Dim rnd2 As New Random
      5. Dim rnd3 As New Random
      6. Dim clr1 = rnd1.Next(1, 255)
      7. Dim clr2 = rnd2.Next(1, 255)
      8. Dim clr3 = rnd2.Next(1, 255)
      9. Dim nm_ As String = item.ToString
      10. nm_ = nm_.Replace("ListViewItem: {", "")
      11. nm_ = nm_.Replace("}", "")
      12. Dim nm As String = nm_
      13. Dim x_ As String = item.SubItems(1).ToString
      14. x_ = x_.Replace("ListViewSubItem: {", "")
      15. x_ = x_.Replace("}", "")
      16. Dim x As Single = x_
      17. Dim y_ As String = item.SubItems(2).ToString
      18. y_ = y_.Replace("ListViewSubItem: {", "")
      19. y_ = y_.Replace("}", "")
      20. Dim y As Single = y_
      21. Dim player As New Rectangle(x, y, 15, 15)
      22. e.Graphics.DrawRectangle(Pens.Transparent, player)
      23. e.Graphics.FillRectangle(New SolidBrush(Color.FromArgb(clr1, clr2, clr3)), player)
      24. Next
      25. End Sub


      (Schnellzusammenfassung).

      Wir haben eine Schleife, die jedes listViewItem - dass wir schon geladen haben (vom Server nämlich) - an die Position des ListveiwItemx und Listviewitemy zeichnet. Ich ersetzte dann auch noch die ListViewItem.ToString, wahrscheinlich gibt es auch 'ne andere Methode.

      Tschuldigung für die schlampige Zusammenfassung aber mein Akku geht gleich leer ;).

      Dazu noch ein Beispielprojekt im Anhang.

      Hoffe es hat euch gefallen ;).

      mfg
      Epic
      Dateien
      • Onlinegame.rar

        (80,05 kB, 683 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Epic“ ()

      Hi Epic, das ist gut.
      Du hast beschrieben das der Server nur Daten aufnimmt, aber das stimmt nicht in allen fällen.
      Große Server Emulatoren senden auch wieder Daten.

      Sowas würde mich auch sehr interessieren, falls du davon mal ein Tutorial machen könntest.
      Von

      Client:
      User Sammelt Definiertes Item auf, ID wird an Server gesendet.

      Server:
      Server bekommt ID, sendet Bestätigung, oder was auch immer.

      LG.L
      Hey -
      da hast du schon recht, habe ich gerade nachgeschaut ;).

      Sowas wäre eine interessante Idee, könnte ich mal machen,
      wenn ich mal wieder Zeit habe.

      Kevin89's Code beinhaltet ja sowas schon, Funktion "SendToAllClients",
      du müsstest dann halt nur überprüfen, wie gesagt, werde ich mal machen.

      Mfg
      Epic
      Hallo.

      Scheint recht nützlich für Anfänger zu sein, was du da geschrieben hast.
      Allerdings würde ich Dinge wie zum Beispiel die Positionsübermittlung eher binär übertragen, also einen BinaryWriter verwenden.
      So sind die Datenpakete normalerweise kleiner und man erspart sich das Parsen.
      nett, nett, auch wenn ein paar schreckliche Fehler drinnen sind. Ich entwickle auch so etwa ähnliches.
      hab aber ein Problem:
      Ich möchte, dass der Server zum Client sagt, ein neuer Spieler hat sich eingeloggt.

      im moment sind aber schon 2 spieler online(ich und irgendwer und jetzt soll ein dritter hinzukommen):

      es gibt die Variablen posX1, posY1(meine X- und YKoordinaten), posX2 und posY2(und die Koordinaten von einem anderen Spieler). Und jetzt soll ein neuer Spieler "erstellt"(im Client) werden. Im Clienten soll geschaut werden, wieviele Spieler es schon gibt und dann sollen die Variablen XZahl(der sovielte Spieler der jetzt dazukommt) und YZahl(der sovielte Spieler der jetzt dazukommt) erstellt werden.und dann natürlich gezeichnet werden.

      der client bekommt also den Befehl: NewPlayer(nick):0:0, und soll daraufhin die Variablen erstellen und den Punkt zeichnen.



      doch wie erstelle ich da die neuen Variablen(ich möchte nämlich nicht im Clientcode Dim posX1,posX2, posX3,... as String, stehen haben)
      Vll könntest du dein Tutorial um diesen Part erweitern, würde mich echt freuen!
      @VBCoder:

      Das Stichwort zu deinem Problem ist: Objekte

      Du erstellst dir eine Klasse mit ein paar Properties die einen
      Spieler abbildet. Dann hast du noch eine List<> vom Typen der Klasse.

      Wenn ein Spieler dem Spiel beitritt erstellst du eine Neues Objekt und fügst
      es an die List<> dran. Du könntest Sogar das Objekt im Server erstellen das
      seralisieren und dann im Client wie zum Objekt deserialisieren.

      Lies dich da mein ein bisschen ein, weil das ist schon eine wichtige Sache. So
      Datenhaltung in Programmen etc.

      MFG
      Werewolve

      powachill schrieb:

      Humpf..
      Also folgendes:
      Bevor ihr den Multiserver bei euch selbst so im großem einbastelt, benutzt doch lieba die EasyTCPLib von kevin89

      Was meinst du? Warum redest du aufeinmal von Multiserver? Das sit doch nur ein Beispielmultiserver, der auch in kevin`s Tutorial - so wie auch der eigentliche Code - verwendet wurde ...
      Supiduuuuupi tutorial ! hast du wirklich Gut gemacht ... Nur eine Frage noch : wie erstelle ich mir einen server ? :D
      Ich hab früher mal gameserver gemacht aber weiß jetzt nichtmal mehr wie die gehen ... wo kann ich mir ein eigenen lokalen server downloaden ? (sollte auch möglichst leicht zu bedienen sein)
      Was meinst du mit Gameserver?

      Benutz das Game doch wie im Tutorial beschrieben.

      Multiserver starten --> z.B. eigener PC
      Client starten --> Eigener PC und andere (LAN, INTERNET)
      Verbindung herstellen.
      Spielen!

      Also wozu brauchste nen Gameserver?

      PS: Wenn du Admin Tools haben möchtest musst die nach deinen Vorstellungen schreiben, das ist ja nicht so schwer.

      MFG
      Yonda
      Hat er eigentlich das Durchgelesen? Nocth, oder? Den ich habe absichtlich einen Fehler verstekct, denn man nur lösen kann, wenn man sich das durchliest. Und zum "Gameserver": Da steht doch, dass es der Gameserver von kevin89 ist. GANZ OBEN: