Hallo,
mein Name ist Jonas und ich bin 17 Jahre alt. Mit Java habe ich schon seit längerer Zeit experimentiert, aber nie die Zeit und Lust für größere Projekte gefunden. Jetzt habe ich mich dazu entschlossen, ein Multiplayerspiel mit dem Titel “Schiffeversenken Extreme” zu programmieren. Wie der Name bereits sagt, wird sich das Spiel um den Klassiker Schiffeversenken drehen, aber mit einigen Änderungen. Die größte ist wahrscheinlich, dass es real-time basiert ist. Zwei Spieler können per Kommandozeile Befehle eingeben, die z.B. Schiffe steuern, andere Schiffe angreifen, usw. können. Im Moment arbeite ich noch am Grundgerüst des Spiels und habe noch keine (eigentlichen) Spielinhalte hinzugefügt, da meiner Meinung das Grundkonzept erstmal stimmen sollte, sodass das hinzufügen von Spielinhalten kein größeres Problem darstellt. Ich bin soweit, dass Befehle eingeben können und diese verarbeitet werden. Außerdem steht Kommunikation mit dem Server und sich zwei Clients können sich connecten. Wenn das Grundkonzept stimmt, will ich mich weiter an die Spielinhalte wagen.
Meine Bitte an euch Profis wäre es, über den Quellcode drüberzuschauen und mir Tipps und Verbesserungen zu Vorgehensweisen, Struktur und Implementierung zu geben :). Damit ihr euch besser zurechtfindet, werde ich die einzelnen Teile kurz erklären und aus meiner Sicht problematische Stellen erläutern (die auch anders hätten gelöst werden können).
Generell zur Struktur des Quellcodes will ich sagen, dass ich zum besseren Lernen und Verständnis auf keine Frameworks/Libarys (später maximal zum Einlesen der Map) zurückgreifen möchte. Außerdem lege ich auch Wert auf die Qualität des Codes und die saubere Implementierung. Für verschiedene Aufgaben habe ich versucht, Design Patterns zu verwenden (inwiefern mir das gelungen ist, erfahre ich hoffentlich von euch :D). Mir geht es weniger darum, einfach schnell etwas hinzuklatschen, sondern ich will mir dafür Zeit nehmen und dabei etwas lernen. Ich hab zum jetzigen Stand nur die wichtigsten Stelle kommentiert, trotzdem kann man sich schnell zurecht finden. Genug gebrabbelt, los gehts :p:
Package “main”:
In diesem Package befindet sich die Hauptklasse mit der main Methode. Diese initialsiert den Logger, welche in eine Datei schreibt (hab gelesen, es ist besser, für jede Klasse einen eigenen Logger zu haben -> Warum?). Danach wird der GameManager, welche den aktuelle GameState und User beinhaltet übergeben und die Input Loop startet (-> main Thread).
Package “commands”:
Ich habe probiert, das “Command-Pattern” zu implementieren und die “Factory method pattern” zur Erzeugung der Commands. Das Problem an dieser Stelle war, die Spielinhalte zentral zu machen, sodass die verschiedenen “Commands” darauf zugreifen können. Um das Problem zu lösen habe ich mich dafür entschieden, das “Singleton-Pattern” beim GameManager zu verwenden. Damit kann jede Klasse auf alles zugreifen (z.B. User und später die TileMap und Entitys). Ich bin mir nur nicht sicher, ob das wirklich OOP ist. Gibt es hier schönere Alternativen?
Package “gamestates”:
Hierzu gibt es nicht viel zu sagen, ich habe bis jetzt den Gamestate für den Start und die Verbindung zum Server. Die einzelnen Gamestates müssen die update()-Methode implementiert haben.
Package “network”:
Für die Kommunikation mit dem Server will ich ein “Message System” verwenden. Die Messages haben eine Methode zum Empfangen und Senden von Daten (ähnlich wie beim Command System werden die Messages durch eine Message Factory übergeben). Zum Verbindungsaufbau nutze ich die Klasse Session, welche einen weiteren Thread für den Networkinput erstellt. Dieser kann die Inputloop anhalten, wenn es erforderlich ist und natürlich auch wieder weiterlaufen lassen. Dafür werden die beiden Bytes (0 und -1) verwendet. Wenn die Verbindung zum Server beendet wird, schickt der Client zuerst ein Byte (127) an den Server. Dieser schickt zur Bestätigung das Byte zurück an den Client. Damit ist die Verbindung beendet. Wegen dieser drei Sonderfälle habe ich mich dazu entschieden, ein switch Konstrukt zu verwenden. Gibt zu der Kommunikation andere, schönere Varianten?
Soviel zum Client. Der Server ist bis jetzt sehr schlicht und kann zwei Verbindungen annehmen und die Verbindung im Falle einer Exception ordnungsgemäß beenden. Auch hier habe ich einen GameManager genutzt (“Singleton-Pattern”). Außerem habe ich angefangen das “Observer-Pattern” zu implementieren. Die beiden Player sind die Listener, die sich beim GameManager registrieren müssen.
Wenn ihr Fragen zu bestimmten Stellen im Quellcode habt, bitte fragen. Ich bedanke mich gaanz herzlich für eure Mühe :),
Jonas