Schon vor ein paar Jahren hatte ich ein Spiel-Grundgerüst in Java programmiert, erst in BlueJ, dann in Greenfoot, das sind zwei Entwicklungsumgebungen für die Schule. Aus einer Laune heraus wollte ich das jetzt für Android haben. 2018 hatte ich eine Fortbildung in Dillingen dazu gemacht, das wollte ich auch mal umsetzen.
Der aktuelle Stand
Im Spiel bewegen sich bis zu vier Spielerinnen und Spieler in einem Verlies oder Höhlensystem, das nach und nach aus verschiedenen Plättchen aufgebaut wird. Man kann nur auf ein anliegendes Feld ziehen, wenn es zu diesem eine direkte Verbindung gibt. Eine Spielerin startet den zentralen Server (dort werden die Informationen verwaltet, wer gerade wo steht), alle melden sich bei diesem Server an; wenn die vorgesehene Spielerzahl erreicht wird, beginnt das Spiel. Der Server kann unter Greenfoot, BlueJ oder Android laufen, und Clients (das sind die Programme der Mitspielenden) gibt es ebenfalls für alle drei. Hier im Screenshot sieht man zwei Clients und einen Server gleichzeitig auf den drei Entwicklungsumgebungen laufen:

Ein kurzer Film (45 Sekunden), unscharf aber ausreichend, demonstriert das Prinzip:
(Bin schon dabei, mir eine ordentliche Box für solche Aufnahmen zu basteln.)
Die IP-Adresse des Servers könnte man vielleicht auch übers Programm herausfinden lassen. Man kann das auch übers ganze Internet und nicht nur im lokalen LAN spielen, wenn man weiß, was man tut und im Router einen Port freigibt. Im Moment ist die Kommunikation völlig unverschlüsselt.
Wie es weiter gehen könnte
Noch ist das gar kein Spiel, nicht mal eine Spieledemo, eher ein Proof-of-Concept für mich selber. Zu einem Spiel fehlt ja vor allem ein Spielziel, und danach die Feineinstellung bei Schwierigkeitsgrad und Belohnung. Und natürlich Ton und feinere Grafik.
Man könnte weiter in die Richtung gehen, von der her kommend ich das Spiel gedacht habe: das Brettspiel DungeonQuest. Da legt man kleine Pappplättchen vor sich und versucht, in die Mitte des Spielfeld zu kommen, wo man so kurz wie nötig bleibt, um dem Drachen den Schatz zu rauben, ohne ihn aufzuwecken, und rechtzeitig vor Sonnenuntergang wieder zum Ausgang zurückfindet. Bei dem Spiel gibt es Fallgitter, die den Weg hinter einem versperren, Drehkorridorteile, Fallgruben und Brücken als Hindernisse, Schatzkammern und Monster. Ein sehr lustiges Spiel, aber mit wenig Interaktion zwischen den Spielerinnen und Spielern; man kämpft jeweils mehr mit dem Höhlensystem als gegeneinander. Das könnte man bei der Computerversion anders machen.
Oder man könnte mehr Richtung Spy vs. Spy gehen (1984, es gibt auch eine moderne Version). Das war ein Spiel für den Commodore 64, basierend auf den gleichnamigen Mad-Magazin-Figuren. Zwei Spione durchsuchen ein Gebäude mit verschiedenen Räumen nach irgendwas und können dabei in den Möbeln in den Räumen verschiedene Fallen hinterlassen, auf die die jeweils andere Figur beim Durchsuchen dann stößt. Bei meinem Spiel hieße das zum Beispiel: ein langer Druck auf die Position, an der man sich gerade befindet, hinterlässt dort eine – aus einem Vorrat auswählbare? – Falle, die zum Beispiel Lähmung für eine kleine Weile bewirkt, oder Teleportation an einen anderen Ort, oder die Drehung des Plättchens um 90 oder 180 Grad, oder das Erscheinen eines Monsters. Die Fallen sieht man auf dem eigenen Bildschirm, aber die andere Spielerin kriegt diese Hinterlassenschaften natürlich nicht angezeigt.
Oder man könnte das als Solospiel ausbauen und ein Rogue-like machen: Zufallsgenerierte Stockwerke, in die man hinabsteigen kann. Dort dann vor allem Schätze und Tränke und Waffen und Monster.
Oder man reduziert die Spielfläche auf 5×6 Felder und lässt eine Art Strategiespiel daraus werden, wo man einander jagt oder ausweicht. Dann vielleicht rundenbasiert statt Echtzeit?
Vermutlich werde ich nichts davon machen. Ab jetzt würde nämlich die eigentliche Arbeit anfangen, der Entwurf geht schneller.

Erfahrungen bei der Entwicklung
Die Spiellogik habe ich schnell heruntergetippt, das Spielprinzip ist einfach und ich habe es ja schon mal programmiert. Diesmal habe ich das sogar noch einfacher gehalten, nicht so viele Klassen angelegt wie sonst und nicht schon vorab Erweiterungsmöglichkeiten eingeplant. Mehr Arbeit haben zwei andere Punkte gemacht, die bei Android anders laufen als bei meinen bisherigen Arbeiten: Die Netzwerkverbindung und die Grafik. – Ab hier wird es technisch, aber auch nicht sehr.
Client-Server-Programmierung
Echte Programmierer wissen, welches Framework man für welche Aufgaben nutzt, man muss nicht alles selber programmieren. Aber dazu bin ich zu weit weg von der Entwicklung. Ich habe meine selbstgeschriebenen Klassen für Server und Clients, alle schon so angelegt, dass ich sie bequem für neue Projekte erweitern kann. Nur dass das Programmieren für Android Probleme mit sich bringt, die man sonst nicht so hat.
Kurzer Überblick zu Threads: Für die meisten Aufgaben, die einem in der Schule unterkommen, schreibt man ein Programm, und das läuft dann der Reihe nach ab, und wenn es läuft, läuft es, und wenn es fertig ist, ist es fertig. Nur manchmal möchte man ein Programm, das sich sozusagen zweiteilt, oder noch weiter aufteilt, wobei die einzelnen Teile parallel oder quasi-parallel und relativ unabhängig voneinander arbeiten. (Der erste von drei Einträgen zu Threads.) Das passiert im Hintergrund ohnehin oft, wenn man mit Benutzeroberflächen arbeitet, also etwa Buttons, die man drücken kann. Denn was auch immer das Haupt-Programm gerade tut, ein Nebenprogramm läuft immer und schaut nach, ob nicht gerade ein Knopf gedrückt worden ist.
Man braucht diese Parallelität auch, wenn man eine Client-Server-Struktur programmiert. Das Programm muss ja gleichzeitig Nachrichten aus dem Internet verwalten und verarbeiten und normal weiter laufen. In meinem bisherigen Java ging das: Versuche Verbindung zu Server aufzubauen, und weil das immer eine wackelige Sache ist, wird man außerdem gezwungen, was geschehen soll, wenn das nicht klappt. Try/catch heißt das: Versuche es erst einmal, und wenn es nicht klappt, stürze nicht gleich mit Fehlermeldung ab, sondern nimm die Reservelösung für den Notfall. In Android muss man das auch machen, aber das reicht noch nicht: Vieles, was absehbar länger dauern könnte, wie der Versuch einer Kontaktaufnahme über Internet, muss außerdem in einem eigenen Thread laufen, der unabhängig vom Hauptthread ist. Ansonsten passiert nämlich das, was viele am Smartphone kennen: der Hauptthread ist mit irgendeiner Aufgabe beschäftigt und das Gerät reagiert nicht auf Berührungen, sanftes Tippen, immer wütender werdendes Fingerstakkato.
Wenn ich eine NetworkOnMainThreadException vermeiden will, muss ich also die Serversachen mittels AsyncTask auslagern. Das ist in den neuesten Versionen zwar auch schon wieder als deprecated markiert, also: überholt, bitte nicht mehr benutzen; aber noch läuft alles.
Besonderheiten bei der grafischen Benutzeroberfläche
Zu einer Benutzeroberfläche gehören üblicherweise Buttons, Texteingabe- und Textanzeigefelder und so weiter. In Greenfoot gibt es so etwas gar nicht, da gibt es nur die Klasse Actor, die erzeugt so etwas wie Sprites. In BlueJ habe ich mit Java Swing gearbeitet. Das ist das, was man vor Java FX hatte.
In Android Studio ist das wieder anders. Man kann die GUI-Elemente entweder programmatisch anlegen, also im Programmcode, Erzeuge einen Knopf und setze die Breite auf 100. Nahegelegt wird aber, eine Art integrierten grafischen Oberflächeneditor zu verwenden, auf dem man mit Drag-and-drop Elemente platziert. Da werden Breite und Höhe und Farbe und Beschriftung eines Buttons festgelegt und dessen Position. (Die ist aber auch wieder schwieriger, weil Smartphones unterschiedliche Bildschirm-Seitenverhältnisse haben.) Der Generator erzeugt eine – auch manuell bearbeitbare – XML-Datei mit den Objektbeschreibungen. Das hat den Vorteil, dass man die ganze Oberflächengestaltung leicht kopieren und wiederverwenden oder weitergeben oder aus dem Web herunterladen kann. Außerdem bleibt der eigentliche Javacode dann übersichtlicher, weil man da diese ganzen Angaben eben nicht machen muss.
Es gibt jetzt aber das Problem, dass ich von einem selbst angelegten Thread aus nicht auf die GUI-Elemente zugreifen kann: Only the original thread that created a view hierarchy can touch its views. Der Haupt-Thread soll wieder stabil laufen, ohne dass andere Threads reingrätschen. Die Lösung: Wenn man in einem solchen manuell angelegten Thread doch auf den Haupt-Thread mit den GUI-Elementen zugreifen will, benutzt man dazu die Methode runOnUiThread(Runnable).
Man merkt: Diese Threads machen alles etwas schwieriger.
Activities und Lebenszyklus
Activities: Das sind die verschiedenen Screens der Anwendung. Eine Activity zum Anmelden, eine Activity für das eigentliche Spiel, zum Beispiel. Typisch für eine App ist, dass man als Benutzer innerhalb einer App zurückblättern kann zur letzten Activity. Was geschieht dann mit der Activity, die man gerade verlassen hat? Was geschieht, wenn man die App schließt, zu einer anderen wechselt, das Handy sich abschaltet? Das sind Fragen zum Lebenszyklus einer App, die man sonst nicht hat. Damit habe ich mich noch kaum beschäftigt.
Zukunft
Und jetzt wäre ich dann soweit, dass ich mein altes Storyworld-Projekt für Android umsetze. (Blogeinträge hier und hier.) Das müsste sich vom Format her besonders gut eignen, und die Spiellogik kann ich ja übernehmen, muss nur die grafische Oberfläche anpassen, zugegeben keine kleine Aufgabe bei diesem Spiel, aber ohne echt anspruchsvolle Programmierung dabei – keine Threads, kein Server.
Ich bin zwar als App-Entwickler angemeldet und habe schon eine App veröffentlicht, aber das ganze in den Shop zu kriegen, ist nochmal viel Aufwand und dauert auch eine Weile. Wenn ich also mal mit einer Klasse oder einem Kurs daram herumbastle, wird die Einreichung als App vermutlich zu lange dauern, so dass sich das nicht lohnt. Aber denkbar ist es schon.
Schreibe einen Kommentar