von Andy Pillip
Das Geräte-Puzzle
Mein erstes Node.js Projekt ist ein Puzzle aus mobilen Geräten
Die teilnehmenden Geräte müssen eine Webseite besuchen – also den
Node.js Server – mit der sie dann permanent in Verbindung bleiben.
Beim Verbinden jedes neuen Gerätes werden alle Geräte angewiesen, sich ein neues Puzzlebild abzuholen. So lässt sich das Puzzle theoretisch mit beliebig vielen Geräten spielen.
Für die Entwicklung und zum Überprüfen, ob man das Puzzle richtig gelöst hat, kann man z.B. mit einem Laptop die Lösungs-Seite besuchen, die die Puzzleteile auf dem Gesamtbild zeigt und durchnummeriert. Tippt man jedes teilnehmende Gerät einmal an, zeigen alle Geräte ihre Nummer, was die Überprüfung der Lösung vereinfacht. Aber natürlich sollte das Puzzle-Motiv selbst schon gut zu erkennen sein.
Node.js und Socket.io
Für node.js als Server habe ich mich entschieden, weil es damit sehr einfach ist, in fast Echtzeit parallel mehrere Clients zu bedienen.
Socket.io erlaubt dem Server, HTTP-Clients über neue Ereignisse zu informieren. Meldet sich zum Beispiel ein neues Gerät an, werden alle Geräte angewiesen, sich ein neues Puzzleteil zu holen.
Normalisierung der Auflösung
Ein
Pixel ist kein Pixel ist kein Pixel beschreibt das Problem sehr gut: Webentwickler
können die physikalische Größe z.B. eines Bildes auf verschiedenen Geräten gar nicht
direkt kontrollieren.
Würde man einfach ein Bild in sagen wir 300 Pixel Breite auf einer Webseite einbinden, und würde sie auf zwei unterschiedlichen Geräten vergleichen, dann wäre das Bild unterschiedlich groß.
Damit meine Puzzleteile aber aneinander passen, benötige ich gleich große Bilder. Ich muss die Auflösung normalisieren, bevor ein Algorithmus das komplette Puzzlemotiv auf die Geräte aufteilen kann.
Ausschlaggebend für die Korrektur sind die CSSPPI, die CSS Pixel per Inch, also dem Verhältnis der in CSS angegebenen Pixel zur physikalischen Größe auf dem Bildschirm.
Die CSSPPI lassen sich berechnen aus den für
viele Geräte bekannten Device Pixel per Inch (DPPI), geteilt durch die
ebenfalls bekannte devicePixelRatio
.
Das Samsung Galaxy S4 hat beispielsweise eine Auflösung von 441 DPPI, und rechnet CSS-Pixel im Verhältnis von 3 um. 441:3 = 147 CSSPPI.
Leider lassen sich die DPPI im Browser nicht abrufen, deshalb muss ich diesen Wert für jedes teilnehmende Gerät nachschlagen, wofür ich in meinem Puzzle eine Tabelle angelegt habe.
Berücksichtigung des Geräterahmens
Smartphones und Tablets bestehen ja nicht nur aus
Bildschirm, sondern haben immer auch einen Rahmen zum Festhalten drum herum.
Verwendet man sie als Puzzleteile, kann man die Bilder des Puzzles nicht direkt aneinander legen, weil der Ramen dazwischen ist.
Auch die Rahmendicke kann man im Browser nicht abfragen, weshalb ich alle teilnemenden Gerätemodelle mit dem Meterstab ausmesse.
Auch die Rahmenstärke wird mit dem CSSPPI-Faktor belegt. Der Server kennt dann zu jedem angemeldeten Gerät ein Rechteck, das den Rahmen mit abbildet. Die Aufteilung des Puzzlemotivs auf die Geräte passiert dann mit diesem äußeren Rechteck.
Für mehr Varianz werden die Recktecke mit ½ Wahrscheinlichkeit um 90° gedreht, so dass die Spieler dann auch das Gerät drehen müssen.
Erst bei der Auslieferung des Teil-Bildes an das Gerät wird der Ramen wieder weggerechnet.
Vollbild mit der Fullscreen-API
Browser haben initial ja auch eine Title-Bar, die man wie den Rahmen vom Puzzle-Teil wegrechnen müsste. Für das Auge funktioniert das aber nicht mehr gut. Während sich der einfarbige Rahmen leicht wegsieht, stört die Browserbar gewaltig.
Meine Lösung ist, auf das erste Tippen den Browser in den Vollbild-Modus zu schalten. Automatisch geht das nicht, weil die Fullscreen-API den Aufruf nur durch eine Nutzerinteraktion erlaubt.
Das ist also eine Aktion, die ich einmalig beim Anmelden eines neuen Gerätes machen muss.
Gerätemodelle erkennen
Für Responsive Web-Apps oder Webseiten ist es keine gute Idee, vom Gerätemodell auf irgendwelche Funktionen oder Eigenschaften zu schließen – diese hängen schließlich auch von der Software-Version ab, die Menge an Geräten ist schlichtweg unüberschaubar und es ist nicht möglich, so eine Entscheidungstabelle aktuell zu halten.
Feature Detection ist das richtige Schlüsselwort.
In meinem speziellen Fall muss ich aber zwei Größen in das Puzzle einfließen lassen, die sich nicht vom Browser ermitteln lassen: die CSSPPI und die Rahmenstärke des Geräts.
Dafür muss ich leider jedes Modell, das teilnehmen soll, einmalig ausmessen und die CSSPPI ermitteln. Die Werte trage ich dann in eine Tabelle ein. Über einen regulären Ausdruck erkennt der Server dann anhand des User Agent Strings das Gerät.
Dabei ist mir klar, dass man zum Beispiel die iPhone-Version so nicht erkennen kann, neuerdings haben ja auch die unterschiedliche Bildschirmgrößen.
Um überprüfen zu können, ob die Puzzleteile auch in der richtigen Größe auf den Bildschirmen landen, habe ich ein Prüfbild mit Schachmuster erstellt. So lassen sich alle Geräte aneinander legen, die Quadrate müssen gleich groß sein.
Aufteilung des Puzzles analog zum Behälterproblem
Abstrahiert lässt sich die Aufteilung eines Bildes auf verschiedene Geräte folgendermaßen beschreiben:
Wie kann man unterschiedlich große Rechtecke so aneinander legen, dass eine möglichst kleine Fläche ohne Lücken entseht?
Damit sind wir fast beim Behälterproblem oder bin packing problem, das sich in der Webentwicklung auch beim Generieren von Sprites stellt, oder in Masonry-Layouts.
Zwar gab es deshalb verschiedenste NPM-Pakete, die irgendwie ein bin-packing implementieren, leider hat fast keines eine direkte Schnittstelle zum Algorithmus angeboten. Viele konnten sogar nur auf dem DOM arbeiten.
Nachdem bin-packing NP-schwer ist, fällt auch die Lösung der jeweiligen Algorithmen unterschiedlich gut aus. Das bedeutet für die Spieler auch unterschiedlich schwer zu lösende Puzzles. Am Ende hat boxpack für mich die besten Ergebnisse gebracht.
Lustig dabei ist natürlich, dass man selbst beim Entwickeln nicht weiß, wie das Puzzle zusammen gehört.
Praxistest
Im Juni 2015 haben wir das Puzzle auf Kultur im Quartier ausgestellt und ausprobiert. Um das Puzzle gut in die Ausstellung zu integrieren, habe ich die ausgehängten Gemälde von Thomas fotografiert und als Puzzlemotive verwendet.
Das hat das Puzzle auch einfacher gemacht, weil die Spieler manchmal mit den Geräten zu den Gemälden gelaufen sind, um den Ausschnitt zu finden.
Gespielt haben wir mit folgenden Geräten
- Samsung Galaxy S4
- Nexus 7 2012
- iPhone 3
- iPhone 4
- HTC Windows Phone 8S
- Samsung Galay S3 Neo
- HTC Legend
- HTC One M8
- iPad
- Galaxy Tab 1
- Sony Xperia acro
- LG G-Pad 8.3
- Kobo Glo N613 (eReader)
- Ubuntu Phone
Energie sparen vs. zig Minuten puzzeln
Der Webbrowser ist nicht dafür gemacht, viele Minuten lang an zu bleiben. Die Zeit bis zum Bildschirm-abdunkeln lässt sich noch hoch genug stellen. Aber einige Browser/Plattformen haben eine Logik zum Energie sparen eingebaut, die die Verbindung zum Server trennt.
Ergebnis im Puzzle ist, dass sich plötzlich alle anderen Geräte ein neues Puzzlebild holen. Das betroffene Gerät zeigt nach wie vor das alte Puzzleteil, spielt aber nicht mehr mit. Leider stört das den Puzzlespaß extrem, die Teilnehmer verlieren das Interesse wenn so etwas passiert.
Verkabelt
Gerade in der Ausstellungs-Situation ist das Problem, dass immer wieder Leute vorbeischauen und kurz das Puzzle ausprobieren. Müsste man alle Geräte erst anschalten, wäre das für viele Besucher nicht interessant. Lässt man die Geräte den ganzen Tag an, braucht das Puzzle recht viel Energie, so dass man Geräte zum Wechseln braucht.
Ein riesiger Vorteil dafür war aber, dass ich beliebig Geräte aus dem Puzzle herausnemen kann, indem ich den Browser schließe. Der Server verteilt dann ja neue Teile an alle anderen Geräte.
Ausblick
Was hab' ich nicht mehr geschafft beziehungsweise welche Ideen gab es noch?
Geräte-Erkennung automatisieren
Es wäre noch cool wenn jeder Spieler sein eigenes Handy dazu legen könnte, oder?
Dafür müsste aber das Ausmessen von Rahmen und Bildschirm wegfallen – was sogar geht:
Der Internet-Dienst WURFL kann über den User Agent String anhand einer sehr umfangreichen Datenbank das Gerätemodell erkennen, und hat viele Details zum Gerät gespeichert – zum Beispiel auch die physikalischen Maße des Rahmens und des Bildschirms. Darüber ließen sich auch die CSSPPI bestimmen.
Bilder von Twitter laden
Um das Spiel noch mehr in die Gesamtveranstaltung einzubinden, die ja im ganzen Quartier statt fand, hätte man beim Neu-Generieren des Puzzles das jeweils letzte Bild zum Veranstaltungs-Hashtag von Twitter benutzen können. Allerdings gab es gar kein Hashtag.