Flappy Bird & Bluetooth
- Door Wout De Schryver
Flappy Bird
Wat is het?
Flappy Bird is een eenvoudig reactiespel waarin de speler een vogel (bij ons een ster) bestuurt die tussen obstakels moet vliegen zonder te botsen. In deze escape room dient het spel niet als eindpuzzel, maar als samenwerkingsmechanisme: zolang speler A in leven blijft in Flappy Bird, worden de sterren die speler B via de telescoop bekijkt geactiveerd en verlicht. Hierdoor kunnen beide spelers samen de sterrenpuzzel voltooien. Het spel voegt tijdsdruk, coördinatie en samenwerking toe aan de ervaring.
Dit moet het spel kunnen doen
Een programma maken dat alle onderdelen van onze puzzel kan aansturen en de status kan bijhouden, en dit op een manier waarop het verloop van de puzzel snel en volledig kan veranderd worden.
Projectoverzicht
De orchestrator is ontworpen als een runtime-brug tussen drie lagen:
- Browser-UI en API
- Spelflowlogica
- Seriële hardware / spelsimulatie
Het houdt een gedeelde runtime-status (gameState) bij en gebruikt een gesandboxte spelflow-script om te bepalen wanneer commando's verstuurd worden. De UI kan toewijzingen aanpassen, de status manueel overschrijven en de flow starten/stoppen.
Logisch flowschema
Stap 1 – Plan het bord
- laad /config/portConfig.json
- initialiseer seriële poorten en gamestate
- start gesandboxt spelflow-script
- start Express-server op http://localhost:8080
Browser UI / API
- GET / => render UI met huidige poorten, status en toewijzingen.
- POST /sendCommand => stuur een commando naar een poort.
- POST /FlowCommand => start of stop het flow-script.
- POST /saveAssignment => bewaar toewijzingen en herinitialiseer poorten.
- WS /gameState => verstuur elke 500 ms live statusupdates.
Spelflowlogica
- Draait in /game/gameFlow.js.
- Wordt uitgevoerd binnen een isolated-vm-sandbox.
- Leest gedeelde status via de callbacks state() en assignment(.
- Verstuurt commando's via setState(portName, code).
Seriële laag
- serial/initSerialPorts.js opent elke toegewezen poort.
- serial/sendCommand.js schrijft bytes naar seriële apparaten.
- flappybird wordt behandeld als een standaard seriële target omdat het geëmuleerd wordt door de Python-helper.
- Binnenkomende data werkt de gameState bij.
Werking kernomponenten
index.js
- Start de Express-app.
- Past JSON-parsing toe.
- Initialiseert de seriële poorten.
- Start de gameFlow.
serial/initSerialPorts.js
- Opent alle geconfigureerde seriële poorten.
- Zet event listeners op voor open, data en error.
game/runFlowScript.js
- Start game/gameFlow.js binnen een isolated-vm.
- Beschermt de hostomgeving tegen directe toegang vanuit het script.
- Maakt script-gestuurd gedrag mogelijk.
routes/public.js
- Rendert de gebruikersinterface.
- Serveert het JavaScript-bestand bundle.js.
routes/api.js
- Voorziet API's voor commando's.
- Voorziet API's voor toewijzingen.
- Voorziet API's voor flowcontrole.
- Voorziet API's voor runtimeconfiguratie.
- Beheert de WebSocket voor live gameState-updates.
variables/gameState.js
- Bevat de gedeelde in-memory state map.
- Wordt gebruikt door het hoofdproces.
- Wordt gebruikt door het gesandboxte flow-script.
Hoofdmappen
- routes/ – HTTP- en WebSocket-endpoints.
- serial/ – levenscyclus en communicatie van seriële poorten.
- game/ – dynamische flowcontroller en uitvoering van gesandboxte scripts.
- config/ – persistente toewijzings- en UI-configuratie.
- site/ – frontend-assets en EJS-template.
- variables/ – runtime maps voor status en toewijzingen.
- python/ – flappybird-commando- en spawn-helpers.
Python-helpers
- python/handleCommand.js – mapt spelflow- of UI-seriële codes naar het Python-spel.
- python/spawnGame.js – spawnt een Python-proces voor ../flappyBird/main.py, luistert naar stdout-updates en werkt gameState bij.
- Python-helper – behoudt gameState.flappybird en houdt de hostapplicatie op de hoogte of het externe spel aan het draaien, klaar of gestopt is.
Code
Code vind je hier: gitlab flappy bird
Bluetooth
Betrouwbaarheid van Bluetooth
Een groot deel van de communicatie van en naar onze onderdelen gebeurt via Bluetooth. Omdat Bluetooth ontworpen is voor snelheid en niet voor betrouwbaarheid, gaat het systeem er nooit van uit dat een status geüpdatet is. Het onderdeel ontvangt de statuscode, update zijn status en stuurt deze code ook terug. Pas als deze bevestiging ontvangen is, wordt de status van het onderdeel in de orchestrator geüpdatet. Aangezien het systeem dat statussen nakijkt en updatet een loop is, blijft de updatecode verstuurd worden tot de status van het onderdeel geüpdatet is.
Verloop aanpassen met Blockly
Om het verloop van de puzzel snel en eenvoudig te kunnen aanpassen wordt gebruikgemaakt van Blockly. Dit zorgt ervoor dat de flow van het spel met blokken getoond en aangepast kan worden. De bibliotheek genereert custom JavaScript-code, die naar de backend gestuurd wordt via een API-call.
Deze code wordt vervolgens uitgevoerd in een geïsoleerde thread met behulp van isolated-vm, wat een geïsoleerde instantie van de Node.js V8-engine geeft. Dit zorgt ervoor dat de onbetrouwbare code van de frontend geen invloed kan hebben op de rest van de server. Toegang tot specifieke functies wordt verleend via callbacks.
Wanneer het verloop van het spel wordt geüpdatet, wordt de oude thread afgesloten en start er een nieuwe op met het nieuwe verloop.
Python-integratie
Het Python-spel draait op dezelfde computer als de orchestrator, wat het terugkoppelen van data vergemakkelijkt. Aangezien het Python-spel vertrouwde code is, wordt hiervoor het ingebouwde Child process van Node.js gebruikt. Data die in Python via print("") verstuurd wordt, kan in Node.js opgevangen worden via stdout. Het Python-spel zelf wordt aangestuurd met een arcade-knop, die via een Arduino een serieel signaal naar het Python-spel stuurt. de basis van het onderdeel is gekopieert van mehmetemineker
Code
Code vind je hier: gitlab orchestrator
Notes
- Het flow-script is gesandboxt zodat custom logica kan draaien zonder de host rechtstreeks bloot te stellen.
- GameState is het centrale runtime-schema: het bevat de huidige status, de overwrite-modus en of de flow aan het draaien is.
- De manuele overwrite-modus behoudt de werkelijke onderliggende status, terwijl gebruikers toch een andere status kunnen forceren.