5.6 KiB
🏎️ Turbo Race
Multiplayer browser-racegame voor tot 20 spelers, speelbaar op de telefoon, met een neon/synthwave-look. Server-authoritatief (Node.js + WebSocket), deploybaar op je eigen Kubernetes-node.
Features
- Verticale scrollende weg, portretmodus, eenhandig besturen (zet je vinger op de baan; de auto rijdt direct naar die plek en blijft staan).
- Keyboard-up / pijltjes op desktop.
- Host opent de kamer met
?host=<token>en start heimelijk met deS-toets (30-seconden countdown, nieuwe aanmelders blijven welkom). De host rijdt niet mee, maar volgt als toeschouwer de koploper (#1) en ziet live de top 5 in het koploperspaneel. - Obstakels: pylonen (vertragen) en olievlekken (wegslijden).
- Power-ups: ⚡ boost en 🛡 schild (respawnen na 4,5s).
- Live positie, snelheid, afstand, leaderboard en minimap.
- 1:30 minuten race over een baan van 23.000 m; wie over de finish is wint, anders wint bij aflopen van de tijd wie het verst is. Daarna uitslag + podium en direct een nieuwe ronde.
Snel starten (lokaal)
npm install
npm start # → http://localhost:8080
Open http://localhost:8080 op je laptop én op je telefoon (zelfde wifi).
Vul een naam in, klik aanmelden, en stuur met je vingers.
🎮 De host is toeschouwer en rijdt niet zelf mee. Open als host:
http://localhost:8080/?host=dev-hosten druk op de S-toets om de start-telling in gang te zetten (pas als de kamer vol is of iedereen klaar is 😉). Tijdens de race volgt de camera de nummer 1 (ook bij wisselende koppositie) en zie je live de top 5 koplopers; na afloop de volledige uitslag.
Besturing
| Apparaat | Actie |
|---|---|
| Telefoon | Zet je vinger op de baan; de auto rijdt direct naar die plek en blijft staan tot je beweegt |
| Toetsenbord | ← / A en → / D |
| Host | S in lobby of uitslag = race (laten) starten · camera volgt de koploper (#1), rijdt niet mee |
Configuratie (omgevingsvariabelen)
| Variabele | Standaard | Betekenis |
|---|---|---|
PORT |
8080 |
HTTP-poort (ook WebSocket op /ws) |
MAX_PLAYERS |
20 |
Maximaal aantal coureurs per kamer |
ADMIN_TOKEN |
dev-host |
Geheim token waarmee je host wordt |
COUNTDOWN_S |
30 |
Aftellen nadat de host op S drukt |
RACE_DURATION_S |
90 |
Maximale racetijd in seconden |
TRACK_LENGTH |
23000 |
Baanglengte in meter (finish = einde van de baan) |
⚠️ Gebruik in productie een sterk
ADMIN_TOKEN(random string), anders kan iedereen met de URL host worden.
Tests
Headless simulatie: engine-unit-test plus live integratietest met een echte server, 20 websocket-clients en een host die de race start en de uitslag leest.
npm test
Docker
docker build -t turbo-race:latest .
docker run --rm -p 8080:8080 \
-e ADMIN_TOKEN='moeilijk-te-raden-token' \
turbo-race:latest
Deployen op Kubernetes
Manifests staan in k8s/ (Kustomize). Bouw de image op je node (of push naar
een registry) en pas de image-tag in k8s/base/deployment.yaml aan.
# 1) image bouwen (op de node zelf)
docker build -t turbo-race:latest .
# 2) token instellen (kustomization generatiert een Secret)
# Bewerk de literal 'admin-token=...' in k8s/base/kustomization.yaml
# 3) deployen
kubectl apply -k k8s/base
kubectl rollout status deploy/turbo-race
kubectl get svc turbo-race # -> NodePort 30090
Speel nu via: http://<IP-van-je-node>:30090
- Healthcheck:
curl http://<node-ip>:30090/healthz→{"ok":true,...} - Met een domein + ingress-controller voeg je
k8s/ingress.yamltoe (pas dehostaan, WebSocket-tunnels worden lang opengehouden).
Lokaal testen met Kind (macOS/Linux)
Op een echte node is NodePort 30090 direct bereikbaar op het node-IP.
Met Kind moet de poort wél expliciet naar de host gemapt worden:
dienst k8s/kind-config.yaml mapt 30090 → localhost.
kind create cluster --config k8s/kind-config.yaml # alleen bij (her)aanmaken
kind load docker-image turbo-race:latest
kubectl apply -k k8s/base
# speel op: http://localhost:30090
ℹ️ Kind hergebruikt een bestaand cluster; als NodePort niet naar
localhostwerkt, verwijder het cluster eerst (kind delete cluster) en maak het opnieuw aan met bovenstaande--config(port-mappings staan vast bij creatie).
⚠️ Belangrijk: de game staat in-memory (1 kamer). Houd
replicas: 1— meerdere replica's zouden spelers over losse kamers verspreiden.
Architectuur
public/ frontend (geen bundler): index.html, style.css, client.js, render.js
server/engine.js server-side spelengine: fysica, obstakels, power-ups, fases, ranking
server/index.js HTTP/static + WebSocket /ws + events + broadcasts
test/simulate.js headless test (fluweel: 20 clients ipv 1)
- Engine loop 40 Hz; snapshots op ~16 Hz; clients interpoleren de rendering.
- Baan is deterministisch opgebouwd uit één random seed → eerlijk & identiek voor elke speler.
- De host is een toeschouwer (neemt geen spelerslot in); de
host_start-melding wordt alleen geaccepteerd van sockets die met het juiste token zijn aangemeld.
Flow & regels
- Lobby — namen aanmelden tot max 20 (fases live zichtbaar).
- Host drukt
S→ 30s countdown (heen-en-weer banners, nieuwe spelers welkom). - Race — rechte weg met obstakels en power-ups; finish is het einde van de baan. Wie als eerste over de line is, wint; anders wint bij het aflopen van de tijd wie het verst is.
- Uitslag — podium + volledige lijst; de host start opnieuw met
S.