knuspermagier.de
Er bloggt noch? Krass!

Der Bann ist gebrochen

Dereinst, als ich mal studierte im verwunschenen Winter-Wonderland Furtwangen im Schwarzwald, spielte ich ab und zu mit meinen Kommilitonen Daniel und Fabian, viele Grüße an dieser Stelle, ein uraltes Spiel: Tank Wars. Laut Wikipedia ein genreprägender Klassiker!

2008-05-29_171253.jpg
Vier Studenten und ein CRT, 29. Mai 2008.

Wie auf dieser altertümlichen Low-Megapixel-Fotografie zu erkennen, haben wir uns manchmal einen Jux draus gemacht, es standesgemäß auf einem der CRTs im Labor der Hochschule zu spielen. Es war zwar erst 2006, aber es gab auch dort schon Flachbildschirme, keine Sorge. Es gab sogar einen iMac, bei dem ich Stunden zubrachte, etwas auszudrucken, weil das “Datei”-Menü “Ablage” hieß und ich natürlich nicht auf die Idee kam, dort nach dem Drucken-Dialog zu suchen. Aber es war der einzige Rechner von dem aus der Drucker funktionierte. Glaube ich. Vielleicht ging der Drucker auch einfach auf meinem Linux-Notebook nicht und ich war mir zu fein ein Windows-Gerät anzufassen. Es ist sehr lange her.

Jedenfalls verbindet mich mit diesem Spiel ein gewisser Fluch, denn ich, der damals schon Lust auf Spielentwicklung hatte, wollte natürlich dieses grandiose und wegweisende Spiel nachbauen. So schwer kann das ja nicht sein, dachte ich mir, und produzierte sicherlich ungefähr vierhundert Java-Projekte, bei denen ich immer wieder verzweifelte. Doch warum?

Für alle, die die Wikipedia-Seite nicht gelesen haben, nochmal kurz die Grundlagen vom Spiel: Es gibt für jeden Spieler einen Panzer, der Dinge abschießen kann, die entweder die anderen Spieler kaputt machen, oder, wenn sie aufs Gelände treffen, eben dieses zerstören. Man kann nicht fahren, sondern nur den Rüssel bewegen und die Schusskraft verändern. Ich wollte diese Spielelemente nun also möglichst originalgetreu nachbauen, also auch das zerstörbare Terrain. Abgesehen davon, dass es 2006 noch nicht tausend super dokumentierte Game-Frameworks gab, wollte ich auch einfach verstehen, was da passiert, ich beschloss also, alles in Java, der einzigen Sprache, neben PHP, die ich damals konnte, umzusetzen. Mit Swing, oder wie das hieß. Ich malte also fleißig ein BufferedImage nach dem anderen und zeigte es in einem JFrame an.

Die meisten Sachen bekam ich auch hin. Diese ganze Mathe-Kacke. Eine Linie zeichnen von A nach B, Kreise malen, Winkel, Sinus und Kosinus. Damals konnte ich auch schon googeln. Ich schaffte es auch, dass das Terrain kaputt ging, wenn man daneben schoss, und baute auch so ein paar Extras ein, dass Dinge am Bildschirmrand abprallen und so Spielereien.


Was ich allerdings nie schaffte, und was mir irgendwann den letzten Nerv raubte: Es performant laufen zu lassen. Auf meinem Notebook, der 2006 quasi neu war, lief es halbwegs okay, aber auf dem etwas kleineren Rechner meines Kommilitonen ruckelte es wie verrückt. Unspielbar! Ich kam nie so richtig darüber hinweg, warum es auf Hardware mit mehreren Gigahertz nicht möglich ist ein paar tausend Pixel sechzig mal in der Sekunde neu zu malen. Mir war schon bewusst, dass das natürlich nicht auf der Grafikkarte läuft, wenn man nur einzeln ein paar Pixel in einem Bild im RAM ändert, aber es beschäftigte mich wirklich lange. Ich begann immer wieder von vorne, fand aber keine Lösung, auch meine Ansätze, herauszufinden, welche Pixel sich änderten und nur diese neu zu malen brachten irgendwie nichts.

Nach dem Studium legte ich das Projekt erstmal wieder zur Seite, ich hatte anders zu tun, als immer wieder das gleiche zu machen, ohne irgendeinen Fortschritt bei der Sache zu machen. Nun, zwölf bis vierzehn Jahre später, kam es aber zu einer unvorhergesehenen Folge von Ereignissen. Zum einen scrollte ich durch Photos.app und fand das oben abgebildete Foto und zum anderen fand ich auf Hacker News einen Blogpost von jemandem, der so eine realistische Sand-Simulation in JavaScript programmierte. Da blühte etwas in meinem Kopf auf, ich musste erneut versuchen dieses Spiel zu programmieren.

Nun wurde ich leider Krank, zwar nur eine handelsübliche Erkältung, trotzdem ganz schön foggy im Kopf, vor allem Abends, wenn ich nochmal fünf Minuten Lust habe, was zu programmieren, daher ist der Code ganz schön schlimm und enthält einige // hä Kommentare vor komischen if-Blöcken mit kopiertem und leicht angepassten Code, die man sicher hätte eleganter lösen können, aber es funktioniert halbwegs. Also das Terrain geht kaputt und man kann sich sogar bewegen, was völlig entgegen der eigentlichen Spielidee ist, aber gut. Das Schönste ist, wie das Terrain kaputt geht und herunterrieselt.

Das beste an JavaScript ist ja auch, dass ich es hier jetzt einfach embedden kann, bitte schön: Wichtig sind WASD und die Leertaste. (Natürlich nur am Desktop, außer ihr habt ein Nokia mit Tastatur)

Ja ich weiß, es fehlen weitere Mitspieler und es gibt auch keine Schwerkraft für die Panzer. Ich habe es auch Elephant THROW! genannt, und die fünfzehn Quadratpixel großen Rechtecke sind natürlich Elefanten, die Steine werfen oder sowas, denn ich finde es in dieser Zeit auch irgendwie blöd ein Spiel mit Panzern zu bauen, aber man kann auch nichts machen, wenn einen nunmal die für ein Jahrzehnt versteckte Motivation packt.

Eigentlich wäre das Projekt auch fast schon am ersten Tag wieder vorbei gewesen, denn die Auswahl des Frameworks hätte mich fast schon wieder völlig verrückt gemacht. Der Herr Jason vom Sandspiel hat das nämlich mit p5.js umgesetzt, was irgendwie so eine JavaScript-Version von Processing ist, oder so. Ich hab mir da fünf Minuten die Doku angeguckt und ich habe nicht verstanden, wie ich das schnell benutzen kann. Bei so Kram wie PixiJS fand ich keine Möglichkeit, einzelne Pixel zu setzen, was wahrscheinlich Sinn ergibt, da man das sicher nicht möchte, wenn man ein performantes Spiel bauen will. In meinem Kopf war festgesetzt, dass das Terrain aus einzelnen Pixeln besteht, die verschwinden und rieseln sollen, also brauchte ich etwas, mit dem ich die Pixel modifizieren kann. Ich landete letztendlich also beim ganz klassischen HTML Canvas-Element.

Also, da kann man natürlich auch erstmal keine Pixel setzen, sondern im Grunde ist es ein bisschen wie damals in Java, man erzeugt mit context.createImageData(width, height); einen sehr sehr langen Array, der für jeden Pixel vier Werte (R, G, B, Alpha) enthält. Was für ein riesiger Array! Am Ende spuckt man das komplette Array in den Canvas und die bunten Pixel werden angezeigt. Es gäbe zwar noch die Möglichkeit mit context.fillRect() direkt ein Rechteck malen, und immer 1x1 Pixel große Rechtecke malen, aber Stack Overflow meinte, die Methode mit .createImageData wäre schneller, was mir auch logisch erscheint, da man halt direkt ganz viele Pixel auf einmal setzen kann.

Einen weiteren Trick, den ich angewandt habe ist, einfach viel weniger Pixel zu rendern, als am Ende anzuzeigen. Intern gibt es nur 320x240 Pixel, angezeigt wird es auf 640x480. Werden diese Standard-Displaygrößen jemals aus meinem Kopf entweichen? Das gibt der ganzen Sache jedenfalls einen gewissen unscharfen Retro-Look. Jedenfalls rendert der bei mir ziemlich viele Frames, ohne größere CPU-Last zu erzeugen. Entweder liegt es nun daran, dass ich eine schnelle M1-CPU habe und seit meinem ersten Umsetzungsversuch einfach fast zwanzig Jahre vergangen sind, oder diese Canvas-Implementierung im Browser ist performanter im Pixel-Malen, als es diese JFrame-Java Geschichte damals war.


Nach dem Schreiben des letzten Absatzes wollte ich es natürlich doch genauer wissen und baute mal einen FPS-Zähler ein. 116 Frames schafft er! Im Chrome Taskmanager braucht der Tab dann etwa 30% CPU. Keine Ahnung, warum es nicht noch mehr sind, wenn er die CPU nur so gering belastet, gleichzeitig gefühlt aber auch ganz schön viel für ein paar Pixel.

Turns out, es ist immer noch super anstrengend im CPU Modus ein paar Pixel zu setzen. Ich habe also damals nicht komplett versagt. Irgendwie reicht mir das jetzt auch schon als Outcome von diesem kleinen Projekt. Mein Interesse, da jetzt alle Features des Ursprungsspiels einzubauen, oder sogar noch mehr, ist doch irgendwie ziemlich gering. Vielleicht versuche ich es in Zehn Jahren nochmal mit einer echten Game Engine und Sprites statt Pixeln.