ROOM 42

Aus toolbox_interaktion
Wechseln zu: Navigation, Suche
Projekt Plakat (SS18)
Projekt Plakat (WS17/18)

ROOM42 - A Multiplayer Adventure Game ist ein Konzept für einen interaktiven Escape Room und wurde im Rahmen eines zehnköpfigen Projektteams im Zeitraum vom Wintersemester 2017/2018 bis zum Sommersemester 2018 umgesetzt.

Inhaltsverzeichnis

Einleitung

Room Escape-Games – Spiele also, deren Prinzip darin liegt, dass Spieler durch das Lösen verschiedener Aufgaben einem verschlossenen Raum entkommen müssen, sind eine der frühesten Entwicklungen der Videospielbranche. Ursprünglich textbasiert, entwickelten sie sich über Point-and-Click-Abenteuer zu auch grafisch ansehnlichen, unterhaltsamen Spielen. Die Adaption des Spielkonzeptes im Analogen eröffnete den Spielern weitere Ebenen der Interaktion, wobei auch der Multiplayer-Aspekt eine prägende Rollen spielt.

Heutzutage werden Escape Games, Room Escapes oder Exit Rooms vor allem in der Gruppe gespielt. Die Spieler befinden sich dabei im realen Raum und können frei mit ihrer Umgebung interagieren um – dem ursprünglichen Spielprinzip folgend – Rätsel und Aufgaben zu lösen. Nicht selten steht dabei die Zusammenarbeit als Team im Mittelpunkt des Spieles. Die Verarbeitung haptischer, akustischer oder optischer Reize ihrer Umwelt, sowie die Interaktion mit dieser sowie untereinander sind stilprägend für den Escape Room, der sich folglich in Europa und den USA steigender Popularität erfreut.

Die Rückbesinnung auf die digitalen Anfänge dieser Spiele, sowie die Auseinandersetzung mit verschiedenen Formen der Interaktion – sowohl im Digitalen als auch Analogen – sind ein logischer nächster Schritt in der Evolution von Escape Games. Folglich setzt sich dieses Projekt mit der Konzeptionierung und Erstellung eines Escape Rooms als Schnittstelle zwischen digitalen und analogen Räumen auseinander.


Game Design

Entstehung der Story zu Room 42

Zu Beginn des zweiten Projektabschnitts hat sich Prof. Ackermann von der Fakultät Design die Zeit genommen, mit dem gesamten Projektteam über die Story des Spiels zu sprechen. So sind einige grundlegende Ideen entstanden, wie man die bestehenden Komponenten schlüssig verbinden könnte und eine kleinere Gruppe hat diese Ansätze im Anschluss weiter ausgearbeitet. Wichtig war dabei, immer den roten Faden im Blick zu haben, selbst jedes noch so merkwürdige Ereignis plausibel innerhalb der Regeln der erschaffenen Welt schlüssig erklären zu können und eine gewisse Spannung zwischen den Charakteren zu erzeugen. Da das Setting im ersten Projektabschnitt stark an das Buch „Per Anhalter durch die Galaxis“ von Douglas Adams angelehnt war, wurde der Ansatz beibehalten und eine neue Geschichte innerhalb eines sehr ähnlichen Universums geschaffen. So wurde sichergestellt, dass keine Urheberrechtsverletzungen entstanden, aber trotzdem konnte man sich an einem vorgegebenen Rahmen orientieren und davon inspirieren lassen.

Narrative

Beginn

Die Spieler befinden sich in einem Raumschiff, welches sich falsch geparkt auf einem Seitenstreifen der Hyperraumumgehungsstraße liegt. Da das Raumschiff dort nicht geparkt werden kann, nimmt die “Vogonen”-Polizei die Verfolgung auf und versucht den Spielern einen Strafzettel auszustellen. Eddi empfiehlt der Crew durch einen Druck auf den “Start-Button” in den Hyperraum zu flüchten. Allerdings wird das Schiff während des Sprung von “Vogonen-Raketen” getroffen und von einem Virus befallen. Das Schiff ist im Energiesparmodus und Netti sagt der Crew, dass das Schiff nach einem Lebenszeichen wieder hochfahren solle.

Das Lebenszeichen (Pulsspiel)

Das Lebenszeichen wird über das Pulsspiel übermittelt. Nachdem dieses erkannt und das Schiff wieder hochgefahren wurde piepst Netti erschrocken und bemerkt, dass Eddi sich aufgrund eines Viruses verändert hat und zu Bady geworden ist. Um den Bordcomputer zurück zu setzen trägt Netti der Crew auf die vier Backup-Würfel zu finden. In ihnen sollte der originale Charakter von Eddi enthalten sein.

Der Safe (Sprachspiel)

Netti erinnert sich daran, die Backup-Würfel zuletzt im Safe gesehen zu haben. Allerdings kann er sich nicht mehr an die Zahlenkombination von diesem erinnern. Woran sie sich jedoch erinnern kann ist der Tag, an dem das Passwort festgelegt wurde, deshalb bittet sie üdie Spieler darum den Logbucheintrag von diesem Tag vorzulesen und übermittelt den Zahlencode anschließend in einer verschlüsselten Form. Während der Logbucheintrag vorgelesen wird versucht Bady die Crewmitglieder immer wieder durch Zwischenrufe zu demotivieren und abzulenken.

Die Spieler müssen den Code nun anhand einer Entschlüsselungstabelle herausfinden und den Safe öffnen. Im Safe finden sie jedoch nur einen von vier Würfeln und dazu drei Laser. Netti trägt den Spielern auf, die Würfel auf dem Schiff zu suchen. Einen können sie einfach in der Tasche eines an Bord befindlichen Bademantels finden.

Einen anderen Würfel können die Spieler hinter einer Plexiglasscheibe sehen, sie kommen jedoch nicht an ihn heran. Bady lacht die Spieler aus und sagt ihnen, dass dieses Fach durch eine Firewall geschützt sei und sie niemals an den Würfel rankommen würden. Netti jedoch weiß was zu tun ist und sagt den Spielen, dass sie die Firewall über die Laser überlasten können, sodass sich das Schloss öffnen wird. Nachdem die Laser nun auf die dafür vorgesehene Zielvorrichtung abgefeuert wurden, öffnete sich das Schloss und der zweite Würfel wurde ergattert. In dem Fach befindet sich außerdem eine Art Röntgengerät (das Tablet).

Das Mäuseloch (Labyrinthspiel)

Ein weiterer Würfel befindet sich in einem Mäuseloch, allerdings ist dieses zu klein, um den Würfel heraus zu nehmen. Die Mäuse klauen immer wieder alle möglichen Dinge auf dem Schiff, um die Menschen zu ärgern. Durch das Tablet kann man aber hinter die Wand sehen und die Wege dort erkennen. Einer der Wege führt zu einem Lüftungsschaft, welchen man öffnen kann. Mit Hilfe eines Magneten, welcher ebenfalls im Raumschiff gefunden werden kann, und mit dem Röntgengerät müssen nun zwei Spieler den Würfel aus dem Labyrinth manövrieren, um so einen weiteren Schritt näher an ihr Ziel zu kommen.

Die Flucht (Boardcomputer)

Die Spieler haben nun alle Backup-Würfel. Nun müssen sie diese nur noch in der richtigen Position und Reihenfolge an den Bordcomputer anschließen. Auch hier versucht Bady die Spieler noch davon abzubringen und zu demotivieren. Wenn alle Würfel richtig eingesetzt wurden wird der Boardcomputer resettet und Bady wird wieder zu Eddi. Eddi sagt den Spielern, dass die Vogonen schon wieder fast aufgeholt haben und sie durch einen weiteren Hyperraumsprung entkommen könnten. Die Spieler müssen nun nochmal auf den Start-Button drücken, um so zu entkommen.

Charaktere und ihre Eigenschaften

In dem Multiplayer Adventure Game gibt es drei bzw. vier Charaktere:

Eddie

Eddie ist der Bordcomputer und ist sozusagen der Hauptcharakter der Story. Er ist immer fröhlich und hilfsbereit. Auch macht er gerne Witze und ist sehr lebensfreudig und mit Energie geladen. Eddie kann sich akustisch ausdrücken und ist nicht sichtbar, sondern nur eine Stimme im Raum.

Bady

Bady ist eigentlich Eddie, aber durch den pogonischen Virus wurde sein Charakter zum kompletten Gegenteil verändert. Nun ist er bösartig und möchte den Spielern nerven und ihm vom Backup bzw. erfolgreichen Spielen abhalten. Dabei gibt er sadistische, trotzige und kindische Sprüche von sich. Bady verfolgt nun die Ziele der Pogonen Polizei und möchte die Spieler an diese ausliefern. Auch ist er sehr sprunghaft in dem was er äußert und möchte nur zusätzlich verwirren.

Netti

Netti ist der Tippsystemcomputer der sich nur textuell auf einem großen Bildschirm mitteilt und je nach Stimmung verschiedene Piepstöne von sich geben kann. Wie der Name des Charakters schon verrät, ist dieser immer nett und freundlich. Auch hilft er den Spielern, wenn sie bei einem Spiel zu lange brauchen. Netti leitet auch einem durch das Spiel und erzählt somit auch Story Elemente.

Pogonen

Die Pogonen sind eine alienartige Rasse und hat das Sagen auf den Weltraumstraßen und bildet die Verkehrspolizei. Diese nehmen ihre Berufung sehr ernst und lassen keinen kleinsten Verstoß einfach so davonkommen.


Sensorik

Pulsspiel

Ideenfindung

Bei der ersten Ideenfindung zum Thema Sensorik wurde der Schwerpunkt unserer Diskussionsrunden zu Beginn des Semesters schnell auf das Gebiet der menschlichen Körperfunktionen gelenkt. Schritte, Puls, Lautstärke oder eventuell Emotionen der Spieler sollen getrackt werden und auf Basis dieser Messwerte unterschiedliche Spielkonzepte entwickelt werden.

Aus verschiedenen Ansätzen hat sich unter Berücksichtigung der Storyline und des Raumsettings als gemeinsamer Konsens folgende Grundidee herauskristallisiert: Der defekte Bordcomputer des Raumschiffs läuft nur im Notstrombetrieb. Über einen Pulssensor wird der Herzschlag eines Spielers ermittelt und die Energie zum Neustart auf den Computer übertragen. Für diesen Reset ist jedoch ein gewisser Schwellwert zu überbieten. Hierzu wird der Spieler zu körperlichen Betätigung durch ein Springseil o.ä. animiert. Der Spieler bekommt vom Pulssensor eine akustische (später auch optische) Rückmeldung. Wird der Zielwert erreicht, erleuchtet das Cockpit des Raumschiffs hell und das nächste Spiel beginnt.


Research und Überlegungen

Zur Messung des Pulses werden verschiedene Technologien in Betracht gezogen. Auf der einen Seite wäre eine Realisierung mittels Pulsuhr oder Smartwatch denkbar. Andererseits fällt nach einiger Recherche ein für Arduino eigens entwickelter Pulssensor ins Auge, der durch eine LED den menschlichen Puls messen kann.

Für das Projekt fällt unsere Entscheidung zugunsten des Arduinos mit Pulssensor. Vor allem die einfache Erreichbarkeit der Messdaten, die kostengünstige Anschaffung und die grenzenlosen Erweiterungsmöglichkeiten für weitere Sensoren waren für diese Wahl ausschlaggebend.


Vorbereitung und Tests mit dem Pulssensor

Serieller Plotter

Bevor der Pulssensor verwendet werden kann, muss dieser gemäß der mitgelieferten Anleitung präpariert werden. Zunächst wird an der Vorderseite auf der LED ein durchsichtiger Klebestreifen angebracht, um diese in erster Linie vor Fettrückständen auf der Haut zu schützen. Außerdem wird auf der Rückseite über den Kontakten ein weiterer Klebestreifen platziert, um auch diese gegen äußere Einflüsse haltbarer zu machen.

Danach muss in die Arduino IDE eine eigene PulseSensor Library importiert werden. Dieser PulseSensor Playground liefert vorgefertigte Funktionen und Beispiele rund um die Pulsmessung mittels Arduino.

Als Board wird ein Arduino NodeMCU 1.0 ESP-12E Modul verwendet, da es sich durch die kompakte Bauweise auch als tragbares Gerät eignen würde und von Haus aus mit einem WLAN-Chip versehen ist, was für die spätere Übertragung der Pulsdaten unabdingbar ist.

Anschließend laufen erste Tests mit dem Pulssensor. Um mit der Messung vertraut zu werden, findet man im PulseSensor Playground das PulseSensor_BMP Beispiel. Hier wird der Puls optisch auf dem seriellen Plotter wie in der nachfolgenden Abbildung dargestellt - die grüne Kurve zeigt den aktuellen Pulsschlag an, die rote Linie kennzeichnet die Intervalldauer zwischen zwei Schlägen. Zusätzlich wird der Herzschlag über die im Arduino eingebaute LED signalisiert.


Realisierung des Pulsspiels

Für die Umsetzung des Spiels im ROOM 42 ist die optische Ausgabe auf dem Plotter weniger interessant. Viel mehr soll der Puls des Spielers durch ein akustisches Signal ausgegeben werden. Dazu muss der Pulswert (BPM) vom Arduino mittels OSC an ein zweites Programm in OpenFrameworks geschickt werden. Dort werden die Daten entsprechend verarbeitet und der Herzschlag wird als Sound über einen Midi- Controller ausgegeben.


Sender

Um Daten via OSC zu senden, muss der Arduino eine Verbindung mit einem WLAN Netzwerk herstellen. Der Empfänger der Daten muss sich ebenfalls in diesem Netzwerk befinden und die IP-Adresse (plus Port) des Receivers wird für die UDP-Verbindung an den Arduino übermittelt. Nach erfolgreichem Verbindungsaufbau startet der Arduino in der loop()-Methode mit der Pulsermittlung. Die Funktion pulseSensor.sawNewSample() aus dem PulseSensor Playground erkennt einen neuen Herzschlag. Jetzt wird mittels pulseSensor.sawStartOfBeat() erneut abgefragt, ob der Anfang eines Pulsschlags vorliegt. Ist dies der Fall, so wird ein OSC Paket mit dem Inhalt der aktuellen Beats per Minute (pulseSensor.getBeatsPerMiunte()) über den Adress-String „/pulse“ verschickt:

void loop() {
    if(pulseSensor.sawNewSample()) {
        if(pulseSensor.sawStartOfBeat()) {
            OSCMessage msg("/pulse");
            msg.add(pulseSensor.getBeatsPerMinute());
            Udp.beginPacket(outIp, outPort);
            msg.send(Udp);
            Udp.endPacket();
            msg.empty();
        }
     }
 }


Receiver

Der Empfänger der Daten, ein OpenFrameworks-Programm, hört in der update()-Methode auf der „/pulse“- Adresse. Wird ein neues UDP-Paket empfangen, so folgen zwei Aktionen:

Eine midiOut-Nachricht wird zum Midi-Controller (SimpleSynth auf MacOS) geschickt und ein Ton in Form eines Herzschlags ausgegeben. Da nur dann ein Paket vom Arduino geschickt wird, wenn ein Herzschlag registriert wurde, wird sichergestellt, dass die Tonausgabe im Rhythmus des Herzschlags des Spielers erklingt. Zudem wird der vom Arduino übermittelte Pulswert mit dem pulseThreshold verglichen. Erst wenn der Schwellwert überschritten wird, wird eine finale Aktion getriggert und das Pulsspiel ist beendet.

void ofApp::update() {
    while(receiver.hasWaitingMessages()) {
        ofxOscMessage m;
        receiver.getNextMessage(m);
        if(m.getAddress() == "/pulse") {
            bpm = m.getArgAsInt(0);
            midiOut.sendNoteOn(1, 55, 100);
        }
    }
    if(bpm > pulseThreshold) {
        // Start Bordcomputer
        midiOut.sendNoteOn(1, 55, 0);
        currentGame = 1;
    } 
}


Aufbau der Präsentation

Die Zwischenpräsentation konzentriert sich in erster Linie auf den prototypischen Aufbau der einzelnen Spiele und deren autarke Funktion, der gemeinschaftliche Kontext eines funktionierenden Escape Rooms wird hier noch komplett außer acht gelassen.

Der Arduino wird in einem Schuhkarton versteckt, nur der Pulssensor wird durch ein kleines Loch nach außen geführt. Zusätzlich zum Arduino befindet sich in dem Karton eine akkubetriebene Bluetooth-Box, die für die Soundausgabe verantwortlich ist und eine Stromquelle für den Arduino. Auf der Außenseite des Schuhkartons wird die Silhouette einer Hand aufgesprayt um eine Person zum Auflegen der Hand auf den Sensor zu motivieren.

Bringt ein Testspieler seinen Finger auf dem Sensor an, so beginnt der Arduino unmittelbar mit der Pulsmessung und sendet die OSC-Daten an ein im Raum befindliches MacBook. Auf diesem werden die Daten analysiert und gemäß der Funktion im vorangegangenen Abschnitt verarbeitet. Der Midi-Controller realisiert die Tonausgabe via Bluetooth über die Soundbox im Schuhkarton.

Verbesserungen

Für das Pulsspiel wurden zwei maßgebliche Veränderungen im laufenden Semester erzielt: die komplette Verarbeitung wurde überarbeitet und die daraus resultierende Ausgabe geändert bzw. erweitert.

Verarbeitung

Zuvor wurde vom NodeMCU bei der Erkennung eines neuen Pulsschlags ein OSC Paket an den Gameserver gesendet. Die akustische Ausgabe per MIDI erfolgte dort unmittelbar im vorgegebenen Rhythmus der eintreffenden Nachrichten. Das hatte jedoch zur Folge, dass Messungenauigkeiten, die bei der optischen Pulsmessung des öfteren vorkommen, nicht ausgeglichen werden und diese Fehler von den Spielern direkt wahrgenommen werden konnten.

Die überarbeitete Version hingegen nimmt sich jedes hundertste Sample (ca. im Sekundentakt) vom Pulssensor, vergleicht dies mit dem vorherigen Pulswert und zusätzlich mit der festgelegten Ober- & Untergrenze um Ausreißerwerte zu erkennen. Nur wenn sich der Wert in diesem Rahmen bewegt, wird er per WiFi an den Gameserver gesendet.

Ausgabe

Die Ausgabe wird in der update() Methode von OpenFrameworks realisiert und berechnet. Die Variable bpm enthält die aktuellen Pulsschläge pro Minute, die als OSC Nachricht vom Arduino eingeht. Mit der Funktion ofSleepMillis(60000/bpm) ‚schläft' das Hauptprogramm für die berechnete Dauer eines Herzschlags, wodurch eine deutlich konstantere optische und akustische Ausgabe möglich ist.

on(1, 200, 0, 0, bpm * 1.5);
midiOut.sendNoteOn(channel, note, 100);
ofSleepMillis(60000 / bpm / 8);
off(1);
midiOut.sendNoteOn(channel, note, 0);
ofSleepMillis(60000 / bpm / 8);
on(1, 200, 0, 0, bpm * 1.5);
midiOut.sendNoteOn(channel, note, 80);
ofSleepMillis(60000 / bpm / 8);
off(1);
midiOut.sendNoteOn(channel, note, 0);
ofSleepMillis(60000 / bpm / 8);
ofSleepMillis(60000 / bpm / 2);

Jeder Pulsschlag besteht bei der Ausgabe aus vier aufeinanderfolgenden Aktionen: Im ersten Achtel wird der Ton ausgegeben, gleichzeitig werden die Scheinwerfer angeschaltet. Für das darauffolgende Achtel des Herzschlags wird sowohl Ton, als auch Beleuchtung heruntergefahren, bevor beides für die gleiche Dauer nochmals eingeschaltet wird. Die restliche Zeit des Schlags setzen Licht und Ton wieder aus. Was auf den ersten Blick ungewöhnlich wirkt, transportiert in der Realität das pulsierende Gefühl eines menschlichen Herzschlags.

Würfelboard

Research und Ideenfindung

Beim Spielen und Analysieren zweier Escape Rooms zur Inspiration und für Recherchezwecke fällt eine Komponente deutlich auf: Die Reihenfolge der Einzelspiele ist nicht willkürlich festgelegt, sondern wird durch eine zentrale Einheit gesteuert. Für ROOM 42 ist eine solche logische Komponente ebenfalls unabdingbar und soll im Bereich Sensorik realisiert werden.

Setting und Story geben einen zerstörten Bordcomputer vor, der durch das Einsetzen einzelner Energiewürfel repariert werden muss und somit zur Lösung des Escape Rooms führt. Diese Würfel werden als Belohnung für das erfolgreiche Absolvieren der Einzelspiele im ROOM 42 gewonnen.

Jeder der Würfel soll einzigartig sein und ist einem festen Steckplatz auf dem Bordcomputer zugeordnet. Auf diese Weise kann im Nachhinein gezielt erkannt werden, welches Game von den Spielern bereits mit Erfolg beendet wurde und mit welchem Spiel im Raum fortgefahren wird.


Umsetzung

Würfelboard Schaltplan

Die zentrale Einheit des Würfelboards ist ebenfalls ein Arduino NodeMCU 1.0 ESP-12E Modul. An dieses Board werden zwischen einem 3.3V Ausgang und vier Input Pins vier separate Stromkreise aufgespannt, welche an den Steckplätzen für die Energiewürfel mit offenen Kontakten unterbrochen sind.

Die Energiewürfel werden zunächst mit Hilfe von vier identischen Holzwürfeln mit einer Kantenlänge von 5 cm prototypisch umgesetzt. Als Unterscheidungsmerkmal dienen einzigartige Patterns, die mit stromleitender Farbe auf die Würfel aufgezeichnet sind.

Wird ein Würfel auf seinen rechtmäßigen Steckplatz gelegt, wird der unterbrochene Stromkreis geschlossen, eine LED gibt hierfür eine sichtbare Rückmeldung und der Input am Arduino registriert den Stromfluss.

Ein Problem fällt bei der Umsetzung schnell auf: Wird ein Energiewürfel nachträglich vom Bordcomputer entfernt, erlischt zwar die LED, der Arduino liest auf dem Input Pin aber weiterhin ein HIGH und gibt dies auch unverändert an das Hauptprogramm weiter. Soll der Pin wieder auf ein LOW zurückgesetzt werden, so muss dieser zusätzlich über einen Vorwiderstand mit dem Ground des Arduinos verbunden werden.

Der besseren Übersicht halber, ist der nebenstehende Schaltplan auf zwei Stromkreise reduziert.


Sender

Zunächst müssen vier Pins des Arduinos als Inputs konfiguriert werden.

void setup() {
    pinMode(cube1, INPUT); 
    pinMode(cube2, INPUT);
    pinMode(cube3, INPUT);
    pinMode(cube4, INPUT);
}

Das Senden der Daten vom Arduino läuft ähnlich wie beim Pulsspiel: Zunächst muss eine WLAN- Verbindung vom Board hergestellt werden und die Socket-Daten des Empfängers für den Ardiuno bereitgestellt werden. Danach können die vier Zustände der Input Pins in eine OSC-Message verpackt und auf die Adresse „/cubes“ verschickt werden. Anschließend wird mit delay(1000) eine Sekunde gewartet und eine weitere Nachricht mit den neu ermittelten Zuständen der Inputs wird gesendet.

void loop() {
    OSCMessage msg("/cubes");
    msg.add(digitalRead(cube1));
    msg.add(digitalRead(cube2));
    msg.add(digitalRead(cube3));
    msg.add(digitalRead(cube4));
    Udp.beginPacket(outIp, outPort);
    msg.send(Udp);
    Udp.endPacket();
    msg.empty();
    delay(1000);
}


Receiver

OpenFrameworks fungiert auch hier als Empfänger der Daten. Die vier übertragenen Pin-Zustände werden als separate Integer abgespeichert, wobei 0 für einen nicht gesetzten, 1 für einen im Steckplatz liegenden Würfel steht. Auf diese Weise kann genau festgestellt werden, welcher Würfel korrekt in das Board eingesetzt wurde und die Summe der gesammelten Energiewürfel leicht ermittelt werden. Dies ist für die Ansteuerung der verschiedenen Einzelspiele und zur Überprüfung des Spielfortschritts essentiell.

void ofApp::update() {
    while(receiver.hasWaitingMessages()) {
        ofxOscMessage m;
        receiver.getNextMessage(m);
        if(m.getAddress() == "/cubes") {
            cube1 = m.getArgAsInt(0);
            cube2 = m.getArgAsInt(1);
            cube3 = m.getArgAsInt(2);
            cube4 = m.getArgAsInt(3);
        }
    } 
}

Um von außen nachvollziehen zu können welche Würfel bereits im Bordcomputer eingesetzt sind, werden die einzelnen Zustände vom Programm ausgegeben. Außerdem kann die Würfelanzahl per Tastatureingabe [+ / -] manuell verändert werden, um Testweise an eine bestimmt Stelle im Gameloop zu springen und somit den Escape Room von einer beliebigen Ausgangsposition starten zu können.

void ofApp::draw(){
    ofDrawBitmapString(cube1, 20, 20);
    ofDrawBitmapString(cube2, 30, 20);
    ofDrawBitmapString(cube3, 40, 20);
    ofDrawBitmapString(cube4, 50, 20);
    ofDrawBitmapString(cubeSum, 70, 20);
}
void ofApp::keyPressed(int key){
    switch(key) {
        case '+':
            cubeSum ++;
            break;
        case '-':
            cubeSum —-;
            break;
    } 
}

Aufbau der Präsentation

Für den Prototypen zur Projektpräsentation wurden zwei aufeinander liegende Kartonplatten als Basis für das Würfelboard verwendet. In den oberen Karton wurden die vier 5 x 5 cm Steckplätze geschnitten und für einen passenderen Look mit silberner Sprühfarbe versehen. Außerdem wurden 4 kleinere Löcher für die LEDs ausgespart. Auf der unteren Platte wurde die komplette Schaltlogik analog zum Plan in 4.2.2 aufgetragen: Pro Würfelsteckplatz sind 8 Kontakte in Form von aufgedröselten Kupferkabeln angebracht, 6 Attrappen und 2 tatsächlich an den Arduino gelötete Kabel. Auf diese Weise ist später für den Spieler beim Betrachten der Steckplätze nicht mehr ersichtlich, welche Kontakte durch die auf den Würfeln aufgetragene Farbe überbrückt werden müssen.

Room42 cubes aufbau.png


Laser-Spiel

Ideenfindung und Anforderungen

Da uns ein Laserspiel im Leichenhaus bei unserem Smart Room Besuch außerordentlich gefallen hat, kamen wir zu der Überlegung ein ähnliches Spiel in unseren Escape Room aufzunehmen. Das Konzept ist relativ simpel gehalten: Um das Spiel zu gewinnen, muss ein Laserstrahl über einen oder mehrere Spiegel von den Spielern so umgelenkt werden, dass dieser auf eine Fotodiode trifft.

Für das Spiel wird diese Hardware verwendet:

  • Laserpointer (Leistung darf 5 mW nicht überschreiten)
  • Fotodiode
  • LED
  • Arduino
  • Spiegel zur Lenkung des Laserstrahls


Technische Umsetzung

An den Output Pin 13 des Arduino Boards wird eine LED gekoppelt, die Fotodiode hängt am analogen Pin 0. Diese misst die Helligkeit des eintreffenden Lichts. Wenn der Laser auf die Diode zielt, wird der Schwellwert überschritten und die LED beginnt als optische Rückmeldung zu leuchten.

Der nachfolgende Sketch realisiert dieses Spiel:

int licht;
void setup() {  
    pinMode(13, OUTPUT);
}
void loop() { 
    licht = analogRead(0);
    if (licht > 150) {
        digitalWrite(13, HIGH);
    } else {
        digitalWrite(13, LOW);
    }
}


Aufbau der Präsentation

Für die Präsentation verbauten wir den Arduino in einem Schuhkarton und verlöteten alle Kabel. Durch ein kleines Loch im Karton ist nur die Fotodiode zu sehen. Um den Spielern zu signalisieren, dass auf dieses Loch gezielt werden muss, wurde eine Zielscheibe auf dem Karton angebracht.


Verbesserungen / Weiterentwicklung=

Im ersten Semester war das Laserspiel so aufgebaut, dass ein Laser auf eine lichtempfindliche Diode trifft und anschließend eine Leuchtdiode aufleuchtet. Da dieser Aufbau sehr simpel war, wurde dieser für dieses Semester weiterentwickelt. Das neue Laserspiel sollte statt nur einer lichtempfindlichen Diode, drei beinhalten und auch der Output sollte ein anderer sein. Nachdem alle drei Dioden gleichzeitig von Lasern getroffen werden, soll sich ein Fach öffnen, in welchem dann ein „Backup Würfel“ und das Tablet, welches zum lösen des Labyrinths benötigt wird, zu finden sind.

Vor Beginn der Entwicklung und des Aufbaus der Komponenten wurde ermittelt, welche Materialien benötigt werden. Hierzu gehören Bretter, um die Vorrichtung, in der die Ziele angebracht werden, bauen zu können, mehrere lichtempfindliche Dioden, ein altes Ikea- Fach, in welchem der Würfel sein wird, ein Plexiglas, um das Fach zu verschließen und Sprühfarben, um die Stationen zu lackieren. Diese Werkstoffe wurden im Baumarkt und im Elektrofachgeschäft beschafft. Außerdem haben wir uns von den Laboringenieuren zwei NodeMCUs, einen Servo zum Verriegeln des Plexiglasfachs und noch kleinere Dinge wie Kabel, Widerstände und Lochrasterplatten erhalten.

Laservorrichtung

Für die Zielscheiben wurden drei lichtempfindliche Dioden über einen Multiplexer an ein NodeMCU angeschlossen. Hierzu wurde jede Diode an eine kleine Lochrasterplatte gelötet und mit dem NodeMCU verkabelt. Anschließend wurden diese hinter Bohrungen an einem Brett, welches die Front der Verkleidung ist, angebracht. Die Dioden geben bei Lichteinfall einen analogen Wert zwischen 0 und 1024 aus. Dabei steht 0 für kein Licht und 1024 für maximal messbares Licht. Bei einer normalen Raumbeleuchtung schwankt dieser Wert zwischen 10 und 30. Da das NodeMCU nur einen einzigen analogen Input hat musste für die Verwendung von 3 Dioden ein Multiplexer zwischengeschalten werden. Der verwendete Multiplexer kann bis zu acht analoge Signale empfangen und sendet diese anschließend nacheinander und adressiert (Y0-Y7) über den analogen Input des NodeMCUs.

Das Problem, dass es beim Multiplexer in Kombination mit den Dioden gibt ist, dass die umliegenden Werte ebenfalls beeinflusst werden. Wenn beispielsweise die Diode an Y0 angeschlossen und beleuchtet wird und der Wert für Y0 auf 500 steigt, so steigt auch der Wert für Y1 auf 300 und der von Y2 auf 200. Diese Werte wurden exemplarisch gewählt und variieren je nach Lichtintensität. Wegen dieser Beeinflussung mussten die Dioden so an den Multiplexer angeschlossen werden, dass sie möglichst weit voneinander entfernt sind. Für jede der Dioden wurde eine Boolean Variable angelegt, welche auf “true” gesetzt wird solange der Messwert der Diode über 200 ist. Sobald alle drei Dioden gleichzeitig angestrahlt und deren Variablen auf “true” gesetzt wurden, wird eine OSC Message an den NodeMCU des Plexiglasfachs geschickt.

Plexiglasfach

Für das Fach wurde ebenfalls ein NodeMCU verwendet. An dieses NodeMCU wurde ein Servo angeschlossen, welcher sich öffnet sobald die OSCMessage der Laservorrichtung empfangen wurde. Dieser NodeMCU ist der einzige im gesamten Projekt, welcher nicht nur OSCMessages sendet, sondern auch empfängt. Um die Funktionsweise des Empfangens von Messages zu verstehen wurde das Empfängerbeispiel aus der Arduino Software verwendet und anschließend so angepasst, dass es für die Zwecke des Laserspiels genutzt werden kann.

Sobald der Servo geöffnet wurde wird eine OSCMessage an den Gameserver gesendet, welche das erfolgreiche Ende des Spiels initialisiert.

Die Laservorrichtung wurde aus Holz gebaut und besteht aus insgesamt 5 Teilen, welche durch Winkel aneinander geschraubt wurden. An der Frontseite wurden drei Bohrungen vorgenommen. Hinter diesen wurden die Dioden befestigt.

Für das Fach, in welchem sich der Würfel befindet, wurde ein altes Ikea-Regal verwendet. An der Öffnung von diesem wurde über Scharniere das Plexiglas angebracht. An der Innenseite des Plexiglases wurde ein kleines Holzstück angeklebt, welches genutzt wird, um die gesamte Vorrichtung über den Servo zu verriegeln. Außerdem wurde in der Innenseite links oben im Regal eine kleine Feder angebracht. Diese sorgt dafür, dass das Plexiglas nach vorne klappt sobald die Verriegelung geöffnet wurde.

Start-Button

Ideenfindung

Am Ende des ersten Projekt-Semesters fehlte dem Raum ein Startpunkt. Ein Startpunkt für die Spieler, den sie sehen können und mit dem sie auch interagieren können. Er soll den Spielern vermitteln, dass das Spiel nun beginnt und ihnen auch die Kontrolle über diesen Beginn geben. Er soll die erste einfache Station sein, welche den Spielern zeigt, dass sie Einfluss auf das haben, was im Raum passiert, dass ihren Aktionen Reaktionen folgen und dass sie selbst für den Fortschritt in der Geschichte zuständig sind. Somit wurde der Start-Button eingeführt.

Umsetzung

Der Kern des Startknopfes ist ein NodeMCU, an welchen ein Taster angeschlossen ist. Dieser Taster wurder an eine Rasterlochpatte gelötet. Diese Platte wurde dann in ein Spielzeug, welches ein roter Buzzer war, verbaut. Zuvor wurde die gesamte Elektronik, die ursprünglich in diesem Buzzer war, entfernt. In den Boden des Buzzers wurde ein Loch gebohrt und anschließend wurde er an einer Holzplatte befestigt, in die ebenfalls ein Loch gebohrt wurde. An der Unterseite der Holzplatte wurde ein NodeMCU angebracht, an diesem wurden die Kabel des Knopfes, welche vorher durch die Löcher geführt wurden, angeschlossen. Sobald der Knopf gedrückt wird sendet dieser über WiFi eine OSCMessage an den Gameserver und der Start des Spiels wird initialisiert.

Abstandssensoren

Ein weiteres Problem wurde im Bereich der Sensorik gelöst, um ein zusammenhängendes Spielen des kompletten Raums zu gewährleisten. Denn bisher gab es keine Möglichkeit zu detektieren, wann das Labyrinth Game erfolgreich absolviert wurde.

Hierfür wurde ein Abstandssensor mit einem weiteren NodeMCU Board verbunden, der auf Infrarotbasis Entfernungen im Bereich zwischen 5 und 40 Zentimetern misst und den Wert an den analogen Pin des Arduinos weitergibt. Wenn der zu erspielende Würfel am Ende des Labyrinths an dem Sensor vorbeigezogen wird und ein gewisser Abstandswert unterboten wird, so schickt das Board zur Bestätigung eine OSC Nachricht mit dem Inhalt 1 an die Adresse /labyrith des Gameservers.

Ein zweiter, identischer Sensor wurde später am unteren Mäuseloch des Labyrinths angebracht. Dieser reagiert ebenfalls anhand eines Schwellwerts auf Abstandsveränderungen bei (Nicht-)Vorhandenseins des Spielewürfels, wodurch eine Soundausgabe in Form von Mäusepiepen für Storytellingzwecke getriggert wird.

void loop() {
   if (analogRead(in) < threshold) {
      status = 0;
   } else {
      status = 1;
   }
   if (statusOld != status) { // bei Abstandsänderung 
      OSCMessage msg("/mouseSound");
      msg.add(status);
      Udp.beginPacket(outIp , outPort) ; 
      msg.send(Udp) ;
      Udp . endPacket() ;
      msg.empty() ; 
      statusOld = status ;
   }
delay(500); 
}

Audio

Konzept

Die Idee bei dem Audiospiel war, eine unbewusste Interaktion der Spieler auszuwerten. Die Gespräche der Spieler sollen aufgenommen werden, ohne dass diese etwas davon mitbekommen. Dies sollte mittels eines versteckten Mikrofons erfolgen. Das Programm, welches wir für die Spracherkennung nutzen – die Microsoft Speech Recognition, verarbeitet dann die Aufzeichnungen und sendet über OSC, die Nachrichten an unsere Hauptsoftware. Diese wertet die Worte aus und stellt sie durch eine visuelle Ausgabe als Planeten im Weltraum dar. Mit Hilfe der angezeigten Planeten, in denen jeweils ein Wort mit einem markierten Buchstaben steht, kann ein Codewort generiert werden, welches für den weiteren Verlauf des Escape Rooms benötigt wird.

Während wir unser Programm testeten, stellten wir fest, dass die Spracherkennung, die wir benutzen, nicht so gut funktioniert. Daher trainierte jeder seinen Laptop auf die eigene Stimme. Die dabei entstanden Dateien konnten auf einem Rechner benutzt werden, um eine bessere Spracherkennung zu erhalten. Uns war es auch technisch nicht möglich ein verstecktes Mikrofon zu nutzen und entschieden uns daher, die unbewusste Interaktion in den Hintergrund zu stellen und ein sichtbares Mikrofon zu nutzen.


Verwendete Soft- und Hardware

Für die Umsetzung der Spracherkennung recherchierten wir über verschiedene Online und Offline Lösungen. Wir stießen dabei unter anderem auf Amazon ́s Alexa, eine Google Speech API und verschiedene Ansätze von Microsoft. Letztendlich entschieden wir uns für eine offline Lösung von Microsoft – die Microsoft Speech Recognition – da diese nicht nur unabhängig von online Datenbanken ist, sondern man auch an die gesprochenen Texte herankommt.

Als Entwicklungsumgebung verwendeten wir Visual Studio mit open Frameworks. Die Software für die Spracherkennung lief auch in einem Visual Studio Projekt auf unseren Windows Rechnern.

Für open Frameworks verwendeten wir unterschiedliche Add Ons. Zunächst benutzten wir das ofxOSC AddOn, um OSC Nachrichten von der Spracherkennungssoftware an unsere Hauptsoftware zu schicken. Für den Output, also die Erstellung der Planeten/Polygone, verwendeten wir das ofxBox2d Add On. Durch dieses Add On war es möglich, den Kreisen physikalische Eigenschaften zu geben, sodass sich diese wie Planeten verhalten. Als letztes nutzen wir das ofxXmlSettings Add On, um die verschiedenen Parameter des Programms gebündelt in einer Datei zu haben. Dadurch war es möglich, Test durchzuführen mit z.B. unterschiedlichen Schriftgrößen, ohne das Programm neu kompilieren zu müssen.

Für die Sprachaufnahmen verwendeten wir ein Tischmikrofon – das Sennheiser E 912, welches mit Hilfe eines Interfaces an einen beliebigen Rechner angeschlossen werden kann.


Technische Umsetzung

Room42 audio1.png
Room42 audio2.PNG
Room42 audio3.PNG

Die Audiogruppe teilte sich nochmals in drei kleinere Gruppen auf – Textverarbeitung, Output und OSC. Eine Person kümmerte sich darum die Nachrichten des C# Projektes für die Inputverarbeitung an die Textverarbeitung zu schicken. Zwei kümmerten sich um die Verarbeitung und Auswertung der gesprochenen Worte und zwei weitere um die visuelle Darstellung der Worte mit Codebuchstaben.

Die Inputverarbeitung erfolgt in einem C# Projekt. In diesem wird zunächst überprüft ob ein Recognizer auf dem Rechner installiert ist und als Input wird das Standard Gerät des Rechners benutzt. Die Spracherkennung gibt als erstes Vermutung ab, was sie denkt, das gesagt wurde und legt sich dann auf einen String fest. Die Erkennung erfolgt mit Hilfe der hinterlegten Grammatik der Sprache. Das festgelegte Ergebnis wird dann über einen Port per OSC an die Textverarbeitung weitergeschickt.

Zu Beginn der Textverarbeitungssoftware werden alle Bilder und Fonts aus der XML-Datei geladen, welche später für den Output benötigt werden. Es wird auch durch eine Eingabe in der Konsole ein Codewort festgelegt, welches später von den Spielern erraten werden muss.

In der Verarbeitung werden die OSC-Nachrichten empfangen und ausgewertet. Die Nachricht wird in die einzelnen Wörter zerlegt und diese in einem Vector gespeichert. Anschließend wird geprüft ob die einzelnen Worte länger als ein Buchstabe sind, da die Spracherkennung - trotz hinterlegtem Wörterbuch – auch Wörter erkennt, die nur aus einem Buchstaben und einem Punkt bestehen. Danach werden die Wörter in einer Methode auf Sonderzeichen überprüft. Wenn ein Wort Sonderzeichen enthält wird es gleich aus dem Vector gelöscht.

Sobald alle Wörter überprüft worden sind, geht es an die eigentliche Verarbeitung. Die Wörter werden einzeln mit den Buchstaben des Codeworts abgeglichen und bei einer Übereinstimmung, wird des jeweilige Wort mit dem gefundenen Buchstaben an den Output weitergeleitet und aus den Listen für die Wörter und Buchstaben gelöscht.

Im Output werden aus den übergeben Wörter Planeten erstellt. Zunächst wird auf ein kreisähnliches Polygon ein Bild eines Planeten gelegt. Danach wird das Wort mit dem Buchstaben, der vorher in dem Wort gefunden wurde, auf den Planeten geschrieben. Die Größe des Planeten hängt von der Länge des Wortes ab und die Bilder der Planeten werden in einer bestimmten Reihenfolge angezeigt. Die Kreise/Planeten bewegen sich im Raum und stoßen sich voneinander und von den Grenzen des „Raumes“ ab. Sobald sich ein Planet nicht mehr in x- oder y-Richtung bewegt, wird er wieder „angestoßen“. In der rechten oberen Ecke zeigt ein Counter wie viele Buchstaben aus dem Codewort bereits auf Planeten dargestellt werden. Wenn alle Buchstaben erkannt wurden, können die Spieler das Codewort erraten und für den weiteren Spielverlauf verwenden.

Aufbau

Für den Aufbau der Zwischenpräsentation entschieden wir uns dafür, das Mikrofon an einem Rednerpult zu installieren. Das Tischmikrofon wurde über das Interface an einen der Laptops angeschlossen. Die visuelle Ausgabe des Programms erfolgte über einen Fernseher, den wir ebenfalls an den Laptop anschlossen. Der vorgelesene Text aus „Per Anhalter durch die Galaxis“ wurde von dem Mikrofon aufgenommen und an unsere Software weitergeleitet. Die erzeugten Planeten wurden auf dem angeschlossenen Fernseher angezeigt und das Codewort konnte erraten werden.

Verbesserungen / Weiterentwicklung

Spracherkennungstechnologie

Nachdem die Ergebnisse der Spracherkennung mit Microsoft Speech Recognition nicht zufriedenstellend waren, wurde beschlossen, einen der etablierten Cloud-Dienste auszuprobieren. Dabei fiel die Entscheidung auf die Google Cloud Speech API, da sie eine sehr gute Erkennungsqualität und unkomplizierte Registrierung bietet, eine sehr gute Dokumentation mit vielen Beispielen liefert und ein Startbudget von 240,93€, das man ein Jahr lang nutzen kann. Nachdem monatliche Transaktionen von bis zu 60 Minuten grundsätzlich kostenfrei sind, war damit der Bedarf von Room42 großzügig abgedeckt. Nach der Abschlusspräsentation und offenen Demo waren noch 237,77 € übrig.

Aufzeichnen der Sprache

Die Aufzeichnung der Sprache und Umwandlung in Strings über die Cloud Speech API wurde basierend auf einem Beispiel von Google realisiert. Ausgehend davon wurden das Python-Skript transcribe.py und die Python-Klasse speechTranscriber.py erstellt.

from speechTranscriber import SpeechTranscriber
if __name__ == ’ __main__ ’: 
   speechTranscriber = SpeechTranscriber()
   while true :
      var = input("Enter command (pause/start/shutdown) or text: \n")
      if (str(var) == "pause"): 
         speechTranscriber . pauseRecording ()
      elif(str(var) == "start"):
         speechTranscriber . startRecording () 
      elif (str(var) == "shutdown"):
         speechTranscriber . shutDown ( )
         break
      else :
         if(str(var) is not ""):
            speechTranscriber.sendOsc(str(var))

Das gezeigte Skript instanziiert ein Objekt der Klasse SpeechTranscriber und liest den Tastatur-Input in der Konsole aus. So werden die drei Aktionen Pause, Start und Shutdown, die normalerweise automatisiert ablaufen, manuell ausgelöst. Wenn ein anderer Text eingegeben wird, wird er vom SpeechTranscriber als OSC-Nachricht an die Verarbeitungseinheit des Audio-Spiels weitergeleitet. So kann das Zusammenspiel der Komponenten getestet werden, auch wenn gerade kein Mikrofon für die Spracheingabe vorhanden ist. Bei der Instanziierung initialisiert der SpeechTranscriber zunächst die benötigten Instanzvariablen.

__init__ (self)
self.__chunks = Queue()
self.__server = self . setupOscServer()
self.__recording = self . setupRecordingProcess()
self.__transcribing = self . setupTranscribingProcess()
#listen for OSC message
self.__listeningToOsc = threading.Thread (name="listeningToOsc", target = self.startOscServer)
self.__listeningToOsc.start()
self.__recorderIsRunning = False self.__transcriberIsRunning = False

Die Queue chunks speichert die aufgezeichneten Audio-Chunks, die immer 14 Sekunden dauern. Die Dauer ist so gewählt, da die Abrechnung von Google je 15 Sekunden gerechnet wird. Um eine kurze Reaktionszeit zu haben, wäre allerdings eine möglichst kurze Aufzeichnungsdauer wichtig. Mit den 14 Sekunden sollen Kosten eingespart werden und gleichzeitig verhindert werden, dass angeschnittene Wörter an Anfang und Ende jedes Chunks nicht erkannt werden können. Server ist ein ThreadingOSCUDPServer, der in der Funktion setupOscServer eine IP-Adresse, einen Port, eine Adresse und die Callback-Funktion handleOscInput zugewiesen bekommt. Mit recording und transcribing werden zwei Threads erzeugt, die die Funktionen recordAudio und transcribe als Callback zugewiesen bekommen und später bei Bedarf gestartet werden können. Anschließend werden der OSC Server gestartet und zwei Booleans recorderIsRunning und transcriberIsRunning auf false gesetzt. Sobald eine OSC Nachricht eintrifft, wird in der Funktion handleOscInput der Inhalt der Nachricht untersucht und entsprechend eine der Aktionen Start, Pause oder Shutdown ausgelöst. So kann im fertigen Spiel der Gameserver über eine Nachricht steuern, wann die Aufzeichnung der Sprache beginnt und wann sie wieder endet, um Transaktionen und damit Kosten einzusparen und auch um die Privat- sphäre der Spieler zu schützen und nur dann Aufnahmen zu erstellen, wenn es im Spiel nötig ist. Wenn die Aufzeichnung gestartet wird, werden die beiden Threads recording und transcribing gestartet und ihre Booleans auf true gesetzt, um den Zustand zu speichern. Recording zeichnet in einem Loop Audio-Chunks auf, speichert sie lokal im Ordner audio und speichert den zugehörigen Pfad in der Queue chunks. Der Code zur Aufzeichnung von Audio-Files basiert auf einem Skript, das auf GitHub bereitgestellt wurde. Transcribing greift auf chunks zu und schickt sie der Reihe nach per HTTP an die Cloud Speech API, um das Ergebnis als String zu erhalten. Die eintreffenden Strings werden direkt per OSC weitergeschickt an die Verarbeitungseinheit des Audio-Spiels. Die beiden Threads laufen so lange, bis ihre Booleans durch die Aktionen Pause oder Shutdown auf false gesetzt werden. Bei Pause können die Threads wieder von neuem gestartet werden, bei Shutdown werden zusätzlich der Thread, der auf eintreffende OSC-Nachrichten hört, und der OSC Server beendet, damit das gesamte Skript sauber beendet werden kann.

Verarbeitung der Strings

Nachdem die Sprache aufgezeichnet wurde, wurden die Strings per OSC an die Verarbeitungseinheit weitergeleitet. Die zur Verarbeitung nötige Logik ist vom ersten Projektab- schnitt übernommen worden, aber durch Refactoring und die Integration als Klasse in den Gameserver, der mit OpenFrameworks umgesetzt wurde, deutlich übersichtlicher geworden. Dazu wurden die neuen Klassen AudioGame und TextProcessor eingeführt. Die außerdem benötigte Klasse Word wurde unverändert übernommen. AudioGame stellt die Methode startAudioRecording zur Verfügung, die die Nachricht Start an den SpeechTranscriber sendet und einen Thread mit der Methode playAudioGame startet. In dem Thread werden die eintreffenden Nachrichten analysiert und Word-Objekte mit den benötigten Zeichen für das Lösungswort erzeugt. Sobald ein neues Word-Objekt erzeugt wurde, wird eine OSC-Nachricht mit den benötigten Informationen an die Anzeige-Einheit geschickt. Wenn alle benötigten Zeichen gefunden wurden und genügend Word-Objekte vorhanden sind, wird stopAudioRecording aufgerufen. Dadurch wird der Thread beendet und per OSC die Nachricht Pause ausgesendet, um die Sprachaufzeichnungen zu beenden. Zusätzlich wird intern die Information für den Gameserver abgespeichert, dass die Etappe des Spiels erfolgreich abgeschlossen wurde.

Ausgabe

Im Gegensatz zum ersten Projektabschnitt wurde die Ausgabe des Audio-Spiels komplett von der Verarbeitung getrennt und in das User Interface des Spielbegleiters Netti integriert. Netti ist ein openFrameworks-Programm und besteht aus einer Anzeige für Story-Elemente, für Tipps, den Spielfortschritt und das Wörterrätsel des Audio-Spiels.

Logik

Die benötigten Inhalte werden vom Gameserver geliefert, deshalb besteht die Update- Methode aus Fallunterscheidungen, um die eintreffenden OSC-Nachrichten je nach Adresse entsprechend zu behandeln.

Da nettiMessage und nettiTipp den Spielern konkret beim Weiterspielen helfen, lenkt Netti die Aufmerksamkeit der Spieler besonders auf sich, indem im Eck des Displays ein gelber Kreis blinkt und ein Sound abgespielt wird. Dazu werden bei Eintreffen einer der beiden Nachrichtentypen der Boolean newMessage auf true gesetzt und die Kon- stante LIGHT der Variable lightDuration zugewiesen. Diese Informationen werden in den Methoden animateLight und playBeepSound benötigt. animateLight wechselt für eine fest definierte Dauer den Zustand des Booleans lightOn zwischen true false und true ab, um abhängig davon in der Draw-Methode die Farben entsprechend anzuzeigen. playBeepSound bezieht sich auch auf den Boolean newMessage, unterscheidet zwischen drei Emotionen, die in der OSC Nachricht mitgeschickt werden können, und spielt dann das entsprechende Audio-File ab.

Darstellung
Room42 netti.PNG

Die Draw-Methode greift auf die Informationen zu, die während der Update-Methode in Instanzvariablen gespeichert wurden, und passt die grafische Ausgabe entsprechend an. Es gibt je nach Spielfortschritt unterschiedliche Screens, die angezeigt werden. Solange der Boolean isGameStarted false ist, wird ein Start-Screen angezeigt, der einen blauen Hintergrund und einen Text zur Begrüßung der Spieler anzeigt. Danach wird auf den Spiel-Screen gewechselt, der aus einem Header und Footer sowie einem linken und rechten Content-Bereich besteht.

Im Header wird eine Titelzeile und die Lampe für neu eingetroffene Nachrichten angezeigt. Der Footer bildet den Spielfortschritt über gefüllte Kreise auf einer Linie ab. Im linken Content-Bereich befinden sich die Texte, die die Story erklären und mit deren Hilfe Netti jedes Spiel einleitet. Rechts davon befindet sich der zweite Content-Bereich, der nach Bedarf Tipps anzeigt und während dem Audio-Spiel einen abgetrennten Bereich für das Wörterrätsel nutzt. Das Layout des Spiel-Screens ist auf keine feste Bildschirmgröße angepasst, sondern relativ aufgebaut. Jede Zone wird von einem ofRectangle beschrieben, das die zugehörige Hintergrundfläche beschreibt. Dazu wird Bezug auf die Maße und Positionen der anderen Zonen oder ofGetHeight und ofGetWidth genommen. Die Elemente im Vordergrund einer Zone werden immer relativ zum zugehörigen ofRectangle positioniert, wodurch eine responsive Skalierung des User Interfaces möglich ist. Nur die Schriftgrößen nutzen feste Werte und bei einem zu kleinen Display ist der Text nicht vollständig lesbar. Ein Wechsel von einem kleinen zu einem größeren Bildschirm bietet aber keinerlei Nachteile und dieser Use Case war für das Projekt besonders relevant, da das Programm auf einem kleinen Laptop entwickelt und später auf einem großen Fernseher dargestellt wurde. Die Texte für Story-Erklärungen und Tipps unterscheiden sich stark in ihrer Länge, weshalb für die Darstellung ein passendes Addon gesucht wurde. Zum einen sollte die Darstellung von Unicode Zeichen, also vor allem der deutschen Umlauten unterstützt werden, zum anderen sollte ein automatischer Zeilenumbruch durchgeführt werden. Außerdem sollte ein TrueType Font geladen werden und seine Schriftgröße eingestellt werden können. Als zusätzliches Feature wünschte sich das Team eine Animation des Textes, dass ihn so wirken lässt, als würde er gerade zeichenweise getippt werden. Für die Realisierung dieser Anforderungen wurden folgende Addons getestet:

ofxTextWriter animiert den Text wie gewünscht, ist aber nur mit ofDrawBitmapString kombinierbar. Also kann kein eigener Font geladen werden und die Schriftgröße nicht manuell eingestellt werden und das Addon ist somit nicht geeignet.

ofxUnicode sollte zumindest Unicode Zeichen wie gewünscht anzeigen, allerdings war es bei einem kurzen Test nicht einsetzbar, da eine Datei fehlte. Somit war das mitgelieferte Example nicht ausführbar und das Addon wurde nicht weiter betrachtet.

ofxFontStash soll Unicode-Zeichen anzeigen können, das hat allerdings nicht funktioniert und auch die Schriftgröße war nicht einstellbar. Deshalb war auch dieses Addon nicht geeignet.

ofxTextSuite kann ofxTextBlock-Objekte verwenden, die die verfügbare Fläche für einen Absatz definieren und innerhalb dieser Fläche bricht der Text automatisch um. Das erfüllte die wichtigste Anforderung, dass der Text vollständig angezeigt und gelesen werden kann. Auch die Schriftart und Größe kann frei gewählt werden. Allerdings wurden bei den Tests Umlaute nicht korrekt angezeigt, weshalb eine Methode in der Update-Methode eingefügt wurde, die jede OSC-Message auf Umlaute untersucht und diese durch ein- fache Vokale ersetzt. Somit war dieses Addon gut geeignet. Weitere Screens nach dem Spiel-Screen sind wie der Start-Screen blaue Flächen mit entsprechenden Texten. Wenn der Boolean isCubesComplete true ist, beschreibt der Text das erfolgreiche Einspielen des Backups. Ist dagegen isGameFinished true, erklärt der Text den Spielern, dass sie das Spiel erfolgreich abgeschlossen haben.

Logbuch

Um im Anschluss an das Audio-Spiel den Tresor zu öffnen, muss man sich den Zahlencode für das Schloss durch ein weiteres erspielen. Der Spieler bekommt den Hinweis, dass der Captain des Raumschiffs an einem bestimmten Datum die Zahlenkombination verriet. Der Spieler wird dazu aufgefordert den Logbucheintrag von diesem genannten Tag laut vorzulesen, um sich wieder an den Zahlencode erinnern zu können. Der entworfene Logbucheintrag wurde in DIN A4-Format erstellt und anschließend einlaminiert, um im Spiel genutzt werden zu können.

Codierung

Room42 umcodiertabelle.PNG

Durch das Vorlesen aus dem Logbucheintrag erkannte die Audioverarbeitung gewisse Wörter aus dem Text. Bei diesen Wörtern handelte es sich um diejenigen, die einen der Buchstaben eines zuvor festgelegten Lösungswortes enthalten. Insgesamt werden neun verschiedene Wörter mit solch einen Lösungsbuchstaben von der Audioverarbeitung erkannt und anschließend auf dem Boardcomputer Netti visuell wiedergegeben. Mittels dieser gekennzeichneten Lösungsbuchstaben kann das Lösungswort gebildet werden. Die Eingabe in den Tresor erfolgt über einen Nummernblock, was folglich zu einer Umwandlung der Buchstaben in Zahlen führt. Zunächst wurden dafür einige verschiedene Möglichkeiten zur Transformation der lateinischen Buchstaben in Zahlen überlegt und entsprechende Entwürfe sowie Skizzen angefertigt.

Entschieden hat man sich für das System, bei dem der Spielende das Lösungswort in der linken Tabellenhälfte sucht und sich parallel dazu auf der rechten Tabellenhälfte den entsprechenden Zahlencode merkt. Wurden der Buchstaben- und Zahlencode richtig erkannt und transformiert, kann der Safe geöffnet werden. Diese Tabelle wurde in Querformat einer DIN A4-Seite erstellt und für die Verwendung im Adventure Game einlaminiert.

Tresor / Safe

Aufbau

Der Safe ist ein Kernbestandteil des Spieldurchlaufs und sollte demnach auch einem echten Tresor ähneln, um ein authentisches Echtheitsgefühl bei dessen Benutzung hervorzurufen. Bevor dieser gebaut werden konnte, musste überlegt werden, aus welchen Material und in welcher Dimension er umgesetzt werden sollte. Als preiswertesten und am einfachsten zu verarbeitende Werkstoff stellte sich Holz heraus, da mit Holz ein stabiler, aber nicht allzu schwerer Tresor nachgebaut werden konnte. Bevor das Schließfach erschaffen werden konnte, wurde eine Skizze auf Papier erstellt, um das Aussehen grob zu definieren.

Anschließend wurden Wände, Boden, Türe und ein Deckel auf die richtige Größe zuge- sägt und mit einigen Schrauben zusammengefügt. Die Tresortüre wurde mit Hilfe zweier Scharniere eingesetzt. Um den Safe metallisch wirken zu lassen, wurde er mit anthrazitfarbenen Holzlack angestrichen. Der Innenraum erhielt einen schwarzen Anstrich. Um den Deckel herauszuheben und die Türe öffnen zu können, wurden zwei unterschiedliche Griffe an zwei Positionen montiert. Der Türknauf zum Öffnen der Tür dient dem Spieler, der andere Griff wurde am Gehäusedeckel platziert, um die Elektronik im Inneren des Safes zunächst einzubauen und später bei Benutzung den Strom zuzuführen.

Anschließend mussten auf der Vorderwand des Safes zwei Löcher für die Leuchtdioden gebohrt werden, um dessen Status anzuzeigen. Hinzu kommt das Bohren eines kleinen Schlitzes wenige Zentimeter über den Löchern der LEDs. Darüber wurden die Kabel des KeyPads, das auf der Vorderseite aufgeklebt wurde, nach hinten in den Innenraum zur weiteren Verbauung geführt. Im Innenraum des Safes wird das Breadboard mit aufgestecktem Node MCU Board, wie das KeyPad, die Leuchtdioden, als auch der Servomotor miteinander verkabelt und eine Halterung für eine externe, wechselbare Batterie eingebaut. Um über den Servomotor den Tresor zu verriegeln, wurde mit Hilfe eines kleinen Holzplättchens eine Verlängerung des Servo-Riegels erzeugt. An der Innenseite der Tresortüre musste anschließend ein Holzkeil mit etwas Abstand angeschraubt werden, so dass der Servo bei Ausführung den Riegel zwischen die Türe und den Holzkeil schiebt und den Safe folglich versperrt.

Labyrinth

Ideenfindung

Wir entwickelten mit dem Aufkommen von Virtual- und Augmented Reality (AR) Technologien ein sehr hohes Interesse für diese Art der Technologie. Um VR und AR besser kennenzulernen und damit zu arbeiten, nahmen wir uns vor eine dieser Thematiken in unser Projekt einzubinden. Aufgrund dessen, dass sich unser Spiel in einem Raum zuträgt, lag es näher AR zu verwenden, um auch in eben diesem Raum zu verweilen und sich nicht in einen anderen, virtuellen Raum, zu begeben.

Wir Thematisierten die Idee in unserer Gruppe und überlegten uns Beispiele dafür auf, wie es sich in unser Gesamtspiel einbinden ließe. Da jeder damit einverstanden war diese Technologie zu nutzen, entschieden wir uns dafür, das Teilprojekt Hololens in die Welt zu rufen und das Labyrinthspiel in unsere Spieleliste aufzunehmen.

Die Idee für das Labyrinthspiel ist, dass sich ein Energiewürfel, der für das Entkommen aus dem Raum benötigt wird, in einem Mäuseloch befindet. Dieses Loch ist allerdings zu klein, um den Würfel einfach zu entnehmen und deshalb muss er mithilfe eines Magneten durch ein Labyrinth, welches sich hinter der Wand befindet, zu einem Lüftungsschacht befördert und von dort entnommen werden. Der Clou bei der ganzen Sache ist, dass das Labyrinth nur für die Person sichtbar ist, die die Hololens trägt.

Technische Umsetzung

Für die technische Umsetzung war es zunächst notwendig zu ermitteln, welche Möglichkeiten es überhaupt für die Umsetzung gibt. So konnten wir nach einiger Recherche herausfinden, dass die aktuell beste Vorgehensweise für die Darstellung von virtuellen Objekten in Abhängigkeit von Objekten, die Nutzung des „Unity“ Add-on „Vuforia“ ist. Demzufolge waren die verwendeten Softwarekomponenten „Unity“ und „Vuforia“.

Die Ermittlung von der für unsere Zwecke richtigen „Unity“-Version stellte sich als schwierig heraus. Dies lag daran, dass das Thema AR noch sehr frisch war und es demzufolge auch nicht wirklich viel Content wie Tutorials, Anleitung etc. gab. Deshalb mussten wir sehr viel rumprobieren bis wir die beste Lösung fanden. Erschwerend kam hinzu, dass die vorhandenen Anleitungen für die Nutzung von „Vuforia“ in „Unity“ nicht auf die aktuell funktionierenden Versionen abgestimmt waren. Viele Funktionen und Einstellungen waren an anderen Orten vorzunehmen, waren unter anderem Namen zu finden oder wurden gar nicht mehr verwendet.

Die Funktionsweise von „Vuforia“ ist im Grunde die, dass ein sogenannter „Marker“ z.B. ein Blatt Papier mit einer auffälligen, einmaligen und wiedererkennbaren Struktur bedruckt wird, welche dem Programm auch in digitaler Form bekannt ist. Dieses Bild wird dann als ein 2D-Objekt in „Unity“ platziert. Das virtuelle Objekt wird ebenfalls in Unity platziert. Durch die Hololens sollte es dann möglich sein, das virtuelle Objekt in derselben Relation wie in dem „Unity“-Projekt abhängig vom Marker zu sehen.

Nachdem wir es, mit Verwendung einer Webcam, geschafft hatten ein virtuelles Objekt abhängig von einem Objekt in der wirklichen Welt darzustellen, wollten wir das „Unity“-Projekt unter der Nutzung von „Visual Studio 2017“ auf die Hololens kompilieren. Das funktionierte auch, doch leider konnte man das Objekt nicht sehen. Wir verbrachten viele Stunden damit verschiedene Tutorials anzusehen und auch selbst an den Einstellungen herumzuspielen, doch vergebens. Es gelang uns nicht die Hololens für unsere Zwecke zu nutzen.

Um jedoch für unsere Projektpräsentation etwas Vorzeigbares zu haben, entschieden wir uns dazu die Verwendung der Hololens zunächst zurückzustellen und stattdessen ein Tablett zu nehmen. Das Tablett würde im Grunde genau dasselbe machen wie die Hololens. Unter Verwendung der Kamera des Tablets wird der Marker gesucht und, abhängig von diesem, wird dann auf dem Display das virtuelle Objekt in dem Abbild der Realität zu sehen sein. Nachdem ich einige Einstellungen angepasst hatte, konnte ich das „Unity“-Projekt ohne große Probleme als funktionierende App auf das Tablet kompilieren.

Nach dem Bau des Labyrinthes aus Holz modelierten wir dieses noch letztendlich in Unity und platziert es abhängig von einem Marker.

Bau des Labyrinths

Beim Bau des Labyrinthes war die größte Herausforderung es so zu bauen, dass der Würfel, falls er auf dem Weg durch das Labyrinth runterfallen sollte, immer wieder beim Mauseloch landet. Der Kasten, in dem sich das Labyrinth befindet, ist 1m x 1m x 0,1m groß und besteht aus Holz. Die Materialien besorgten wir im Baumarkt und das Werkzeug brachten wir von Zuhause mit. Die Platten, die genutzt wurden, mussten gesägt, gebohrt, verschraubt und geklebt werden.

Der Weg des Labyrinths wurde durch zugesägte Stücke der Spanpressplatten auf der Rückseite der Frontwand angeklebt. Zwei schräg verlaufende Bretter dienen als „Rutschen“ und sorgen dafür, dass der Würfel immer wieder zum Ursprungsort zurück fällt.

Verbesserungen / Weiterentwicklung

Die einzige Änderung, die noch vorgenommen wurde, war das Aussehen des Labyrinths zu verändern. Hierfür wurde in Unity ein neues Material erstellt, welches lilafarben war und einen Glow-Effekt hatte. Dieses Material wurde für das Labyrinth-Objekt übernommen, sodass dieses besser in das gesamt Setting des Spiels passt.

Außerdem wurde das Image-Target geändert. Auch hier war der Grund, dass es so besser in das Design des Spiels passt.

Gameserver

Voraussetzungen

Der Gameserver ist die zentrale Einheit des multiplayer adventure games ROOM42. Er verbindet alle Stationen und visuellen und auditiven Ausgaben miteinander, sodass ein logischer Kontext entsteht. Somit ist er der Mittelpunkt der Kommunikation. Über den Gameserver wird der Spielablauf geregelt, indem Nachrichten von den verschiedenen Stationen per OSC empfangen, darauf reagiert und OSC Nachrichten an weitere Programme gesendet werden. Folgend wird jede Klasse/Komponente des Gameservers näher beleuchtet.

Zur Veranschaulichung der logischen Abfolge wurde zu Beginn ein Aktivitätsdiagramm anhand der entwickelten Story erstellt und dieses zur Orientierung bei der Programmierung des Gameservers zur Hilfe gezogen. Der erste Punkt im Ablaufdiagramm war der Startknopf. Es wurde sich daher erkundigt wie man OSC Nachrichten empfängt und dann darauf reagieren kann. Daraufhin wurde eine Adresse für die Nachricht festgelegt. Nachdem der Code auf Seite des Startknopfes implementiert war, kam die OSC Nachricht direkt bei dem Gameserver an und die Intro.mp3 Datei wurde abgespielt.

Senden von OSC Nachrichten

Mit Hilfe des oscSenderExamples von Open Frameworks war es möglich, gleichzeitig den Gameserver zu implementieren und zu testen und währenddessen die einzelnen Stationen fertigzustellen. Es wurde sich an dem vorhandenen Code orientiert, um anschließend daran angelehnt die passenden OSC Nachrichten an den Gameserver schicken zu können. Die jeweiligen Nachrichten werden nach einem festgelegten Tastendruck losgeschickt, um eine OSC Nachricht einer Station zu simulieren. Jede dieser Nachrichten besteht aus einer Adresse und den passenden Parametern, welche auf der Seite des Gameservers erwartet werden. Mit diesen Nachrichten kann der Ablauf des Gameservers getestet werden, ohne das echte Sensordaten nötig sind. Die Adressen wurden passend zu den Stationen ausgewählt und mit den Bearbeitern der jeweiligen Station abgesprochen. Die simulierten OSC Nachrichten halfen dabei, Fehler im logischen Ablauf des Gameservers zu finden. Es konnten somit auch einige kleine Ungenauigkeiten gefunden werden, welche sonst erst beim Testen aller Komponenten aufgefallen wären. Dieses Beispiel erleichterte die Arbeit mit dem Gameserver.

Im Folgenden Code Ausschnitt erkennt man, wie eine Station simuliert abgeschlossen wird. Es wird die Nachricht für den Startknopf mit der Adresse „/startSwitch“ losgeschickt, sobald auf „0“ gedrückt wurde.

void ofApp::keyPressed(int key){
   if (key == '0') {
      ofxOscMessage m;
      m.setAddress("/startSwitch");
      sender.sendMessage(m);
      cout << "start Switch" << endl;
   }
}

Textausgabe „Netti“

Netti ist einer der Charaktere, die in diesem Semester neu dazugekommen sind. Das Programm für Netti reagiert auf OSC Nachrichten, die von dem Gameserver geschickt werden. In der Hauptklasse des Gameservers werden die verschiedenen Story-Elemente von Netti anhand des aktuellen Spiels getriggert. Je nachdem in welchem Abschnitt sich die Spieler gerade befinden, wird ein Story-Element aus einem XML File geladen und zusammen mit einer Emotion an das Netti Programm geschickt. Falls zwei Teile der Geschichte während eines Spiels an-kommen müssen, wird ein Timeout gesetzt, nach welchem eine zweite OSC Nachricht mit der nächsten Story geschickt wird. So ist es den Spielern möglich beide Nachrichten lesen zu können.

Die Nachrichten bestehen aus einer spezifischen Adresse und Argumenten. Für Netti wurden verschiedene Adressen verwendet. Es gibt auf der einen Seite die Nachrichten mit der Adresse „/nettiProgress“, welche nur eine „0“ als Argument an Netti schicken. Dadurch kann der Fort-schritt des gesamten Spiels angezeigt werden. Die Spieler wissen so, wie weit sie im Spiel bereits sind.

Dann gibt es noch Nachrichten mit der Adresse „/nettiMessage“ und zwei Argumenten. In dem ersten Argument ist das Story-Element in Form eines Strings enthalten. Durch das andere Argument wird eine Emotion an Netti übermittelt. An dem untenstehenden Beispiel kann erkannt werden, wie die Methoden für die Story-Elemente aufgebaut sind.

void Netti::firstStory() {
   ofxOscMessage msg;
   msg.setAddress("/nettiMessage");
   string text = nettiTexte.getValue("settings:story0:text", "Oh nein");
   msg.addStringArg(text);
   string emotion = nettiTexte.getValue("settings:story0:emotion", "nervous");
   msg.addStringArg(emotion);
   sender.sendMessage(msg);
}

Die Story-Elemente enthalten die Geschichte des Multiplayer Adventure Games. Die Spieler werden dadurch durch die Geschichte geführt und es werden auch Hinweise auf die nächste Station gegeben. Die Methode sendWord schickt die Wörter, die von der Klasse TextProcessor aus dem Audiospiel ausgewählt wurden an die Adresse „/audioGame“. Netti zeigt dann die Wörter in einem speziellen Bereich an. Durch die Nachrichten der Methoden sendBoardComplete und sendGameComplete, weiß Netti, wann alle Würfel in den Bordcomputer eingesetzt wurden und wann das komplette Spiel beendet ist.

Soundausgabe

Die Klasse Bady ist ähnlich aufgebaut wie die Netti Klasse. Bady ist der andere Charakter, der im Laufe des Spiels seine Persönlichkeit ändert und nur durch eine Soundausgabe präsent ist. Auch hier werden die OSC Nachrichten an einen anderen PC bzw. ein anderes Programm gesendet, welches diese auswertet. Es gibt für verschiedene Zwecke unterschiedliche Adressen. Einmal wird die Musik über fünf Methoden gesteuert. Je nach Argument kann so die Musik unter anderem gestartet, pausiert und leiser gemacht werden. All diese Nachrichten werden an die Adresse „/music“ geschickt.

Mit der folgenden Methode wird Bady mitgeteilt, dass die Musik gestartet werden soll.

void Bady::oscStartMusic() {
   ofxOscMessage msg;
   msg.setAddress("/music");
   msg.addStringArg("play");
   sender.sendMessage(msg);
}

Alle anderen Methoden beschränken sich auf Kommentare von Bady. Diese Methoden schicken Nachrichten an die Adresse „/bady“. Da einige Kommentare zu bestimmten Zeitpunkten abgespielt werden sollen, wird eine spezifische Nachricht an die Adresse „/bady“ geschickt. So soll unter anderem zu Beginn die Einleitung in das Multiplayer Adventure Game abgespielt werden. Hierzu wird die Methode playIntro aufgerufen, welche eine Nachricht mit zwei Argumenten schickt. Diese sagen dem Bady Programm, welches Intro abgespielt werden soll. Es wurden zudem zwei Methoden implementiert, welche zufällige Werte an Bady schicken, wodurch entweder ein generischer Kommentar oder eines welches passend zum aktuellen Spiel ist, abgespielt werden kann.

Die allgemeinen Soundausgaben wurden zunächst über die Klasse Sounds geregelt. Das Intro zu Beginn des Spiels wurde über diese Klasse abgespielt. Es wurden mehrere Methoden prototypisch angelegt, um weitere Sounds wie Kommentare abspielen lassen zu können. Im Laufe des Semesters wurden jedoch alle Sounds in ein separates Programm ausgelagert. Dieses übernahm fast alle akustischen Ausgaben. Dadurch blieb nur noch die Methode übrig, welche die Maus Geräusche abspielte.

void Sounds::playMouseSounds() {
   soundPlayer.play();
}

Lichtausgabe

Die Lichtsteuerung wurde auch in den Gameserver integriert. Zunächst wurde nur durch einen Kommentar an der passenden Stelle festgelegt, wie sich die Lichtstimmung ändern soll. Später wurden die dazu gehörigen Methoden und Methodenaufrufe in den Quellcode eingefügt.

Timer

Mit der Klasse Timer sollte die Zeit, welche für ein Spiel maximal benötigt werden darf, gestoppt werden. Auch die Zeit für einen kompletten Spielablauf sollte ermittelt werden können. Dazu wurden jeweils zwei Methoden implementiert. In einer wird der Timer gestartet und die andere frägt ab, wie viel Zeit vergangen ist, seit dem der Timer gestartet wurde. Da es aber nicht vorgesehen war, diese Zeit im oder am Ende des Spiels auszugeben, wurde dieser letztendlich als unrelevant angesehen. Es wurde nur noch der Wert der Methode GetCurrentTime verwendet, um zu sehen ob der Gameserver Thread gerade schläft und somit nicht auf OSC Nachrichten reagieren kann.

Noch im Spiel integrierte Timer wurden mit dem von ofUtils bereitgestelltem void ofSleepMillis(int millis) gelöst. Verwendung fanden diese zum Beispiel für den Fall, dass zwei Story-Element nacheinander auf Netti ausgegeben werden sollen und kein anderes Event dazwischen stattfindet, welches die nächste Story über OSC auslöst:

netti.sixthStory(); 
ofSleepMillis(settings.getValue("settings:timerBetweenStories", 20000)); 
netti.seventhStory(); 

Die zu wartenden Millisekunden sind hier in der XML-Datei für die Settings definiert und werden beim Aufruf daraus geladen. Diese Pause ist nötig, damit die Spieler genügend Zeit haben, die Story-Elemente komplett zu lesen.

Audio

Die Audioverarbeitung aus dem letzten Semester wurde in den Gameserver integriert, um möglichst wenig OSC Nachrichten zwischen den einzelnen Programmen schicken zu müssen. Dazu wurden die Klassen word und TextProcessor aus dem Audioprogramm übernommen. Das Audiospiel wird nun über die Klasse AudioGame gestartet. Da die Implementierung nicht von dem Autor übernommen wurde, wird hier nicht weiter auf die Klasse AudioGame eingegangen.

Tippsystem

Zunächst wurden für das Tippsystem Tipps geschrieben. Für einige Spiele wurden bis zu fünf Tipps gesammelt, die für dieses Spiel wichtig oder relevant sein können. Aus diesem Pool an Tipps wurde sich auf die zwei wichtigsten für jede Station geeinigt. Danach wurden die Tipps in den Gameserver integriert.


Ausgegeben werden die Tipps auf der Oberfläche von Netti in einem dafür vorgesehenen Bereich. Dazu wird eine OSC Nachricht bestehend aus der Adresse „/nettiTipp“ und dem Tipp als Argument an das Netti Programm geschickt. Beim Drücken des Keys „t“ wird die Methode giveTipp() aufgerufen, welche wie folgt aufgebaut ist:

//give tipp according to current game 
void ofApp::giveTipp() { 
   switch (currentGame) { 
      case PULS_GAME: 
         tippSystem.pulsTipp(nrTipp) 
         break; 
      case AUDIO_GAME: 
         tippSystem.audioTipp(nrTipp); 
         break; 
      case TRESOR: 
         tippSystem.safeTipp(nrTipp); 
         break; 
      case LASER: 
         tippSystem.laserTipp(nrTipp); 
         break; 
      case LABYRINTH: 
         tippSystem.labyrinthTipp(nrTipp); 
         break; 
      case BOARD: 
         tippSystem.boardTipp(nrTipp); 
         break; 
      default: 
         break; 
   } 

}

Das zeigt, dass abhängig von der Variablen currentGame, welche nach Beendigung jedes Spiels erhöht wird, in der Klasse TippSystem der entsprechende Tipp aufgerufen wird. Bei der switch-case-Belegung ist zudem folgende if-else-Bedingung:

if (nrTipp <= 1) { 
   nrTipp++; 
} else { 
   nrTipp = 0; 
}

Das bedeutet, dass wenn der erste Tipp gesendet wurde, die nrTipp um eins hochgesetzt wird und somit bei erneutem Drücken von „t“ innerhalb eines Spiels der zweite Tipp gesendet wird. In der Klasse Tippsystem werden die Tipps dann via Osc an Netti mit der Adresse „/nettiTipp“ und dem eigentlichen Tipp als String gesendet. Die Methoden sind alle wie das nachstehende Beispiel für die Laser-Tipps aufgebaut:

void TippSystem::laserTipp(int nrOfTipp) { 
   ofxOscMessage laserTipp; 
   laserTipp.setAddress("/nettiTipp"); 
   if (nrOfTipp == 0) { 
      laserTipp.addStringArg("Müssen alle Sensoren getroffen werden?"); 
   } else { 
      laserTipp.addStringArg("Schaut doch mal nach oben."); 
   } 
   sender.sendMessage(laserTipp); 
   cout << "laser Tipp" << endl; 
} 

Key-Handler

In der Hauptklasse des Gameservers sind einige Keys festgelegt, mit denen bestimmte Events ausgelöst werden können. Als erstes ist hier der Buchstabe „s“ zu erwähnen, durch welchen sich der Gameloop starten lässt. Durch die Buchstaben „w“, „p“, „v“, „c“ und „m“ lässt sich die Musik steuern. So wird zum Beispiel durch „p“ die Methode oscPauseMusic aufgerufen, welche die Musik pausiert. Mit „k“ und „g“ können zufällige Kommentare von Bady angestoßen werden. Einige Buchstaben wurden auch zu Testzwecken belegt. Übrig blieb hier nur „l“, wodurch eine Nachricht an den Arduino der Falltür geschickt wird, welche diese öffnet. Einer der wichtigsten Buchstaben ist das „t“, da dadurch Tipps an Netti geschickt werden. Es wurde entschieden dies über einen Tastendruck zu regeln, um möglichst zum passenden Zeitpunkt einen Tipp an die Spieler geben zu können. Hier wurden auch die Erfahrungen aus dem echten Escape Room benutzt, da dort durch die Kameras im Raum immer ein passender Tipp von dem Betreuer gegeben werden konnte.

XML-Dateien

Einige Informationen des Gameservers wurden in einer XML-Datei „settings.xml“ gespeichert. In dieser Datei wurden Einstellungen, wie die IP-Adressen der Netti und Bady PCs hinterlegt, sowie die Pfade für einige anderen Dateien. Es wurden auch die Ports für die OSC Übertragung in dieser XML-Datei festgelegt. Die Einstellungen werden dann aus der XML-Datei ausgelesen.

In einer anderen XML-Datei „netti.xml“ wurden alle Texte, die an Netti übermittelt werden sollen, gespeichert. Dazu wurde ein <story> Tag angelegt, welcher wiederum zwei Tags enthält. In dem einen wurde eine Story-Element gespeichert und das andere legt die Emotion für diese Story fest.

ofApp

In der ofApp Klasse befindet sich der Gameloop des Gameservers. Hier werden alle OSC Nachrichten empfangen und die entsprechenden Aktionen ausgeführt. Hier sind unter anderem die Key-Handler eingebaut und der komplette Ablauf des Multiplayer Adventure Games wird gesteuert. In der setup Methode werden alle Initialisierungen vorgenommen. Die update Methode regelt den Spielablauf. Der Gameloop bestand zunächst aus mehreren Methoden, es blieb jedoch nur die handleOSCMessages Methode übrig.

//wait for osc messages 
while (receiver.hasWaitingMessages()) { 
   ofxOscMessage m; 
   receiver.getNextMessage(m); 
   cout << m.getAddress() << endl; 
   handleButtonActions(m); 
   playPulsGame(m); 
   handleSafeActions(m); 
   handleLaserActions(m); 
   handleMazeActions(m); 
   handleBoardActions(m); 
   … 

}

In dieser werden alle OSC Nachrichten empfangen und je nachdem welche Nachricht ankommt, auch darauf reagiert. Die Methode sendLaser wurde nur verwendet, falls das Laserspiel zu lange dauert oder die Sensoren nicht reagieren. In den Methoden handleButtonActions, playPulsGame, handleSafeActions, handleLaserAct-ions, handleMazeActions und handleBoardActions wurde auf die OSC Nachrichten der Stationen Startbutton, Puls, Tresor, Laser, Labyrinth und Würfelboard reagiert. Alle diese Methoden sind relativ gleich aufgebaut. Zunächst wird überprüft ob man sich gerade im richtigen Spiel befindet und ob die Adresse und gegebenenfalls auch die Argumente der OSC Nachrichten richtig sind. Wenn die Station abgeschlossen wurde, wird der Zähler currentGame erhöht und man befindet sich im nächsten Spiel. Je nach Station werden auch OSC Nachrichten an Netti und auch Bady geschickt.

Mit Hilfe der Methode giveTipp wird je nachdem in welchem Spiel man sich befindet, eine Methode in der Klasse TippSystem aufgerufen. Die Methode keyPressed übernimmt das Key-Handling und reagiert auf die verschiedenen Tastendrücke.

Lichtsteuerung

Ideenfindung & Vorbereitungen

Bereits zu Beginn des Semesters stand fest, dass für die atmosphärische Stimmung im ROOM42 neben Musik auch Licht zum Einsatz kommen soll. Während der Abschlusspräsentation im vorangegangenen Studiensemester wurden erstmals für das Projekt LED-Scheinwerfer verwendet, die zu diesem Zeitpunkt aber noch nicht extern angesteuert wurden, sondern nur zur einfachen Beleuchtung aufgestellt wurden.

Beim Brainstorming bezüglich der Tasks für das Sommersemester wurde dann die Rolle der Lichtsteuerung für das Projekt definiert: Das Licht soll maßgeblich dafür verantwortlich sein, die Spieler durch den Raum zu lenken. So können mehr oder weniger deutlich Hinweise auf die Reihenfolge der Spiele gestreut werden und die Personen im Raum tauchen durch Lichteffekte noch tiefer in die Spielwelt ein.

Umsetzung

Als Interface kommt ein Enttec DMX Pro zum Einsatz. Dieses wird per USB-Schnittstelle mit dem Gameserver verbunden. An das Gerät werden fünf LED-Scheinwerfer mit XLR Kabeln in Reihe gehängt. Das DMX Protokoll stellt 512 Kanäle pro Datenpaket bereit, wobei jedem Kanal ein Statuswert zwischen 0 und 255 zugewiesen werden kann.

Die verwendeten Scheinwerfer besitzen jeweils 8 Kanäle (je einen Kanal für die drei Grundfarben im RGB Schema, die Helligkeit, Stobo-Effekt etc.), wobei auf der Rückseite der Geräte die Startadresse binär über zwölf Kippschalter eingestellt werden muss. Jedes DMX Endgerät erhält also das komplette Datenpaket mit den Statusinformationen aller 512 Kanäle – jede Lampe pickt sich aber anhand ihrer zugewiesenen Startadresse nur die für sie relevanten Informationen aus dem Paket heraus.

Das Ansteuern der Geräte aus OpenFrameworks ist nahezu trivial – vom Plug-in ofxDMX sind dabei in erster Linie drei Funktionen interessant:

dmx.connect(0, 512); // baut die Verbindung zum DMX Interface auf (Port 0 mit 512 individuellen Kanälen)
dmx.setLevel(x, y); // setzt Kanal x auf den Wert y
dmx.update(); // verschickt ein neues DMX Paket um die Werte an den Geräten zu ändern

Im ROOM42 sind vor allem drei verschiedene Zustände pro Lampe denkbar: EIN, AUS oder BLINKEN. Dabei wurde schnell ein Problem erkannt: will man alle fünf Scheinwerfer gleichzeitig von einem Zustand in einen anderen überführen, so müssen für nur diese eine Aktion bis zu 40 Zeilen Programmcode (8 Kanäle * 5 Geräte) geschrieben werden. Bei ca. 50 Zustandswechseln pro Lampe über die Laufzeit des Spiels würde der Source Code sehr lange und unübersichtlich werden.

Deshalb wurden für den Gameserver drei eigene Methoden geschrieben, um deutlich komfortabler mit nur einer Zeile Code den Status eines Scheinwerfers ändern zu können:

// DMX Lampe anschalten
void ofApp::on(int id, int r, int g, int b, int l) {
   dmx.setLevel(1 + (id - 1) * 8, r); // rot
   dmx.setLevel(2 + (id - 1) * 8, g); // grün
   dmx.setLevel(3 + (id - 1) * 8, b); // blau
   dmx.setLevel(5 + (id - 1) * 8, 0); // Geschwindigkeit
   dmx.setLevel(7 + (id - 1) * 8, l); // Helligkeit
   dmx.update(); 
}

Als Parameter werden hierbei ID (1-5; nicht die Startadresse der Lampe), Farbe in RGB und die Helligkeit übergeben. Der Funktionskörper weißt die jeweiligen Werte dem entsprechenden Kanal zu und wendet die Veränderung per dmx.update() umgehend an.

Die Funktionen für das Ausschalten bzw. Blinken sehen ganz ähnlich aus:

// DMX Lampe blinken lassen
void ofApp::on(int id, int r, int g, int b, int s) {
   dmx.setLevel(1 + (id - 1) * 8, r); // rot
   dmx.setLevel(2 + (id - 1) * 8, g); // grün
   dmx.setLevel(3 + (id - 1) * 8, b); // blau
   dmx.setLevel(5 + (id - 1) * 8, s); // Geschwindigkeit
   dmx.setLevel(7 + (id - 1) * 8, 200); // Helligkeit
   dmx.update(); 
}
// DMX Lampe ausschalten
void ofApp::off(int id, int r, int g, int b, int l) {
   for (int i = 1; i <= 8; i++) {
      dmx.setLevel(i + (id - 1) * 8, 0);
   }
   dmx.update();
}


Sound Design

Gestaltung

Bei der immersiven Ausgestaltung des Raumes spielen akustische Reize eine wesentliche Rolle. Wichtige Aspekte sind dabei die Beeinflussung des Spielgeschehens durch akustische oder sprachliche Ausgaben, die den Spielerinnen verbal ausgestaltetet Rückmeldun- gen über den Spielverlauf geben können, oder situativ auf das Spielgeschehen Einfluss nehmen können, sowie eine immersive, narrative musikalische Untermalung des Spieles. Beide sollen dynamisch auf bestimmte spielerische Events reagieren, wodurch den Spielerinnen eine weitere erzählerische und interaktive Ebene ermöglicht wird.

Sprachausgaben

Die Ausgabe sprachlicher Elemente ist ein narratives Schlüsselelement des Spieles. Die dem Spiel eigenen Charaktere Eddie und Bady sind jeweils nur durch ihre sprachliche Präsenz manifestiert und somit nur akustisch erlebbar. Dieser Verhalt macht die charakterliche Ausgestaltung der jeweiligen Persönlichkeiten auf einer verbal-sprachlichen Ebene notwendig. Dazu wurden passende Dialoge verfasst und vertont.

Die Texte wurden aufwändig vertont und eingesprochen und im Anschluss durch Zuhilfenahme von Pitch- und Verzerreffekten moduliert, um den Charakteristika der beiden Computer mehr Authentizität zu verleihen.

Sound-Effekte

Um bestimmte Geschehnisse innerhalb der Geschichte akustisch zu untermalen, verdeutlichen und überhaupt erfahrbar zu machen, wurden mehrere Sound-Effekte im Tonstudio aufgenommen und produziert. Sie besitzen narrativen Charakter und sind unabdingbares Element des Handlungsstranges des Spieles. Bei der Produktion kamen dabei verschiedene Instrumente sowie sowohl privat produzierte als auch kommerzielle Samples zum Einsatz. Als Aufnahmesoftware wurde Ableton Live 9 verwendet, das ebenfalls bei der Modulation von Sound- und Sprachaufnahmen eingesetzt wurde.

Teilweise wurden aus den produzierten Sound-Effekten und Sprachaufnahmen aufwändige Arrangements kreiert, um die szenische Narration einer Sequenz zu ermöglichen.

Musik

Neben den in vorangegangenen visuellen sowie räumlichen Eigenheiten und akustischen Elementen, spielt die musikalische Untermalung des Spieles eine für die narrative und atmosphärischen Gestaltung signifikante Rolle. Wesentlich ist dabei die musikalische Unterstützung der Dramaturgie des Spiel- und Handlungsbogens. Des Weiteren war die dynamische Anpassung der Musik an den Verlauf des Spieles maßgeblich.

Bei der Komposition der Musikstücke wurde auf genretypische Ton- und Harmonieabfolgen zurückgegriffen, um bei den Spielerinnen Assoziationen zu SciFi- und Mysterythemen zu wecken. Polyrythmische Muster und die Kombination von Drei- und Viervierteltakten erzeugen bei den Spielerinnen den Eindruck der Eiligkeit und Brisanz. Zum Einsatz kamen neben digitalen Tasteninstrumenten ein Fender Rhodes Mark 2, ein NS StickBass, sowie mehrere Percussion- und Ryhtmusinstrumente. Ein zusätzlicher atmosphärischer Klangteppich wurde mittels des Luftstroms eines Akkordeons erzeugt, der durch Zuhilfenahme von Verzerr- und Halleffekten verfremdet und den Eindruck von Größe und Raum geben soll. Bei der Aufnahme der Musik wurde wieder auf die Software Ableton Live 9 zurück gegriffen. Mehrere PlugIns kamen dabei zur weiteren Bearbeitung des Klangs zum Einsatz.

Wiedergabe

Die Wiedergabe der Musik, von Dialogen, bzw. Monologen sowie der Sound-Effekte erfolgt hauptsächlich über zwei zentral platzierte aktive Lautsprechereinheiten, die mittels eines analogen Audiokabels von einem Laptop aus angesteuert werden. Lediglich die Ausgabe der akustischen Reaktionen des Netti-Computers erfolgt über die Implementierung dessen selbst.

Steuerung

Bei der Steuerung der Audiowiedergabe muss prinzipiell zwischen der Wiedergabe der Musik- und der Sprachausgaben und Effekte unterschieden werden, obwohl beides im selben System realisiert wurde. Dazu wurde ein Programm realisiert, das, basierend auf openFrameworks, via OSC-Nachrichten vom zentralen Game Loop, angesteuert werden kann und das so, abhängig von übertragenen Parametern, die Wiedergabe bestimmter Dateien kontrolliert. Das Programm entscheiden dann auf Grundlage der bei der Übertragung benutzen Adresse sowie der via übermittelter Nachricht ebenfalls gesendeten Parameter, welche weiterführenden Verarbeitungsschritte es vornimmt, um zielsicher die gewünschten Dateien abzuspielen. Aus logistischen Gründen wird das Programm auf einem vom Game Loop separaten Rechner ausgeführt. Es wäre aber problemlos in den selben Rechner zu integrieren.

Marketing

Voraussetzungen

Um die Präsentation des Projekts zu bewerben, wurde in der Untergruppe Marketing un- ter anderem ein Hauptplakat, ein technisches Plakat und ein Teaser-Trailer erarbeitet. Aus verschiedenen Entwürfen entstand ein einheitliches Design für Room 42. Das Marketing-Team wurde erneut von der Fakultät Design unterstützt und beraten.

Room42 logo ss18.png

Unser erster geplanter Schritt war es, ein Logo für unser Projekt zu entwerfen. Jedes Marketing-Teammitglied fertigte einen oder mehrere Logoentwürfe an, die wir bei einem Treffen gemeinsam besprochen haben. So haben sich nochmals Änderungen ergeben und nach einer erneuten Runde haben wir uns auf das finale Logo geeinigt.

Zusätzlich haben wir auf der sozialen Plattform Pinterest eine gemeinsame Pinnwand angelegt, in der wir Inspirationsbilder zum Thema Per Anhalter durch die Galaxis sammelten. So konnten wir einen Überblick über die zum Thema passenden Stilrichtungen erhalten.

Plakate

Werbeplakat

Aus vielen internen Entwürfen entstand ein Hauptplakat. Verwendet werden dunklere, gedeckte Farben und ein „erwachseneres“ Design, welches Elemente des Storytelling aufgreift.

Zu sehen ist das Innere eines Raumschiffs. Der Eyecatcher in der Mitte des Plakates ist der Boardcomputer des Raumschiffs, welcher durch sechseckige Formen simuliert wird. Die Sechsecke greifen die Story des Escape Rooms auf, z.B. durch verschiedene Anzeigen. Zusätzlich befindet sich in der Mitte des Boardcomputers ein roter Hebel, der den ursprünglich als Hebel geplanten Startknopf des Spieles darstellt. Des Weiteren befindet sich über dem Boardcomputer ein Fenster, welches die Aussicht in den Weltraum zeigt. Der Sternenhimmel gibt ein „Weltraum-Feeling“ und macht das Setting damit offensichtlicher. Unterhalb des Computers wird das Logo inklusive Untertitel dargestellt. Darunter wird in wenigen Sätzen die Geschichte des Adventure Games beschrieben.

Das Plakat wurde mit den Programmen Adobe InDesign und Adobe Illustrator erstellt.

Technisches Plakat

Zusätzlich zum Hauptplakat wurde ein technisches Plakat angefertigt. Alle technischen Komponenten des Adventure Games sind symbolisch aufgeführt und miteinander verknüpft.

Das technische Plakat orientiert sich am Stil des bereits vorher entstandenen Hauptplakates. Wieder wurden sechseckige Formen miteingebunden und auch das Farbschema bezieht sich auf den erstellten Styleguide. Die Symbole wurden mit Adobe Illustrator erstellt, während das Plakat in Adobe InDesign zusammengesetzt wurde. Später wurden die Symbole auch in der PowerPoint Präsentation verwendet.

Styleguide

Im Rahmen der Plakaterstellung wurde zusätzlich ein Styleguide erstellt. Dieser fungier- te als Leitfaden für die Erstellung des technischen Plakates sowie auch der PowerPoint Präsentation. Das Dokument definiert das Farbschema, Schriftarten und das Logo.

Werbeplakat SS18 Technisches Plakat SS18 Styleguide

Video Teaser

Im Marketing Team wurde zusätzlich ein Konzept für einen Teaser-Trailer erarbeitet. Der Trailer sollte das Projekt in 30-60 Sekunden vorstellen und das Interesse des Publikums wecken. Nachdem die ersten Ideen gesammelt wurden, hielt das Team diese in einem Storyboard fest.

Der Teaser Trailer startet mit einer Szene, die einen Mann ab der Mitte des Rückens aufwärts, von hinten zeigt. Er zieht sich einen Bademantel über die Schultern. Dann erfolgt ein Schnitt auf eine Szene, in der er den Bademantel zuknotet. Ein weiterer Schnitt und er greift nach einem Handtuch, wirft es sich über die Schulter und betritt daraufhin einen Raum. Diese Szenen sollen das Betreten eines Spielers vom Room 42 simulieren. Der Bademantel und das Handtuch stellen seine Ausrüstung dar, angelehnt an Per Anhalter durch die Galaxis. Nach diesen Szenen erfolgt ein schwarzer Übergang. Danach werden in wenigen Sekunden, schnell aufeinanderfolgend, die einzelnen Stationen des Adventure Games eingeblendet. Abgeschlossen werden diese Ausschnitte mit dem Einsetzen des letzten Würfels in das Board. Daraufhin folgt die Einblendung des Logos.

Die Filmaufnahmen fanden im Audio- und Videostudio der Hochschule statt. Verwendet wurde eine Canon Eos 750d Spiegelreflexkamera, ein Stativ und zwei Lampen für die Beleuchtung. Der Schnitt erfolgte über Adobe Premiere Pro. Das Logo wurde mit Adobe After Effects animiert. Zusätzlich wurde eine Farbkorrektur vorgenommen.

Pressefoto

Um den fertigen Aufbau des Raums festzuhalten, wurde ein Pressefoto mit einer Canon Eos 600d Spiegelreflexkamera geschossen. Das Foto wurde in Adobe Photoshop nachbearbeitet.

Room42 pressefoto.jpg