Initial commit: Mobil_Tag project setup

This commit is contained in:
topsith
2026-08-05 20:29:18 +02:00
commit 9db3812cf8
46 changed files with 3888 additions and 0 deletions
+292
View File
@@ -0,0 +1,292 @@
# Mobile Tag Projektplan
GPS-basiertes Outdoor-Lasertag für jedes Smartphone als Progressive Web App (PWA).
---
## 1. Konzept & Spielprinzip
- **Plattform:** Webanwendung (PWA), installierbar auf iOS/Android, keine App-Store-Downloads nötig.
- **Spielort:** Überall im Freien. Das Spielfeld wird vor dem Match definiert.
- **Treffermodell:** Hybrid aus GPS/Kompass (Grob-Check) und Kamera-Computer-Vision (Fein-Check).
- GPS + Kompass prüfen, ob ein Gegner in Reichweite und Blickrichtung liegt.
- Die Kamera zeigt ein Fadenkreuz über dem Live-Bild.
- TensorFlow.js (MoveNet) erkennt Körperteile. Das Fadenkreuz entscheidet über Kopf, Brust, Arm oder Bein.
- Jeder Körperteil verursacht unterschiedlichen Schaden.
- **Nachlade-Mechanik:** Kein Dauerfeuer. Cooldown einstellbar, Standard ~3 s.
- **Spielmodi:** Free-for-All und Teams.
- **Spieleranzahl:** 210 Spieler pro Match (MVP).
---
## 2. Architektur
```
┌─────────────────┐ ┌──────────────────────┐
│ Client PWA │◄───────►│ Node.js Game Server │
│ React + Vite │ WebSocket│ Express + Socket.io │
└─────────────────┘ └──────────────────────┘
Handy-Sensoren:
- GPS (Geolocation API)
- Kompass (DeviceOrientationEvent)
- Kamera (getUserMedia)
- TensorFlow.js MoveNet (Pose Estimation)
```
---
## 3. Tech-Stack
| Schicht | Technologie |
|---|---|
| Frontend | React 18 + Vite + TypeScript |
| PWA | `vite-plugin-pwa` (Workbox) |
| Styling | Tailwind CSS |
| Geo-Logik | Turf.js |
| Kamera | `getUserMedia` + `<video>` + Canvas/SVG-Overlay |
| Computer Vision | TensorFlow.js + MoveNet (`@tensorflow-models/pose-detection`) |
| Backend | Node.js + Express + Socket.io |
| Hosting Frontend | Vercel / Netlify |
| Hosting Backend | Render / Railway / Fly.io |
---
## 4. Projektstruktur
```
Mobil_Tag/
├── README.md
├── PLAN.md
├── package.json
├── client/ # Frontend PWA
│ ├── package.json
│ ├── vite.config.ts
│ ├── tsconfig.json
│ ├── index.html
│ ├── public/
│ │ ├── manifest.webmanifest
│ │ └── icons/
│ └── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── components/
│ │ ├── CameraView.tsx
│ │ ├── Crosshair.tsx
│ │ ├── HUD.tsx
│ │ ├── Lobby.tsx
│ │ ├── Radar.tsx
│ │ └── Scoreboard.tsx
│ ├── hooks/
│ │ ├── useGeolocation.ts
│ │ ├── useDeviceOrientation.ts
│ │ ├── useCamera.ts
│ │ ├── usePoseDetection.ts
│ │ └── useSocket.ts
│ ├── game/
│ │ ├── targeting.ts
│ │ ├── bodyParts.ts
│ │ └── geo.ts
│ ├── store/
│ │ └── gameStore.ts
│ ├── types/
│ │ └── game.ts
│ └── styles/
│ └── index.css
└── server/ # Game Server
├── package.json
├── tsconfig.json
└── src/
├── index.ts
├── rooms.ts
├── game.ts
├── geo.ts
└── types.ts
```
---
## 5. Datenmodell
```typescript
type BodyPart = 'head' | 'chest' | 'arm' | 'leg';
interface BodyPartDamage {
head: number; // 100
chest: number; // 40
leg: number; // 25
arm: number; // 15
}
interface Player {
id: string;
name: string;
team?: 'A' | 'B';
position: { lat: number; lng: number };
heading: number; // 0-359 Grad
health: number; // 0-100
alive: boolean;
cooldownUntil: number; // Timestamp
score: number;
kills: number;
deaths: number;
}
interface Match {
id: string;
code: string; // 6-stelliger Raumcode
mode: 'ffa' | 'teams';
status: 'lobby' | 'running' | 'ended';
boundary: Circle | Polygon;
settings: MatchSettings;
players: Map<string, Player>;
startTime: number;
endTime?: number;
}
interface MatchSettings {
maxPlayers: number; // 2-10
reloadMs: number; // Standard 3000
rangeM: number; // Standard 20
angleTolerance: number; // Standard 5
damageByBodyPart: BodyPartDamage;
respawnMs: number; // Standard 5000
matchDurationMs: number; // Standard 600000
startHealth: number; // Standard 100
powerSaveDimMs: number; // Standard 10000
powerSaveSleepMs: number; // Standard 30000
}
interface Shot {
shooterId: string;
timestamp: number;
shooterPos: { lat: number; lng: number };
heading: number;
bodyPart: BodyPart;
}
interface HitEvent {
shooterId: string;
shooterName?: string;
targetId: string;
targetName?: string;
bodyPart: BodyPart;
damage: number;
targetHealth: number;
}
```
---
## 6. Trefferlogik
### 6.1 Zwei-Stufen-Validierung
1. **Grob-Check (GPS + Kompass):**
- Gegner innerhalb `rangeM`?
- Gegner innerhalb `±angleTolerance` der Blickrichtung?
- Gegner am Leben?
- Nicht im selben Team?
- Cooldown abgelaufen?
2. **Fein-Check (Kamera-CV):**
- MoveNet erkennt mindestens eine Person im Kamerabild.
- Das Fadenkreuz liegt innerhalb der erkannten Körperregion.
- Bestimmtes Körperteil wird ermittelt.
### 6.2 Body-Part-Mapping (MoveNet)
MoveNet liefert 17 Keypoints. Daraus werden Regionen gebildet:
| Körperteil | Keypoints | Schaden |
|---|---|---|
| Kopf | nose, eyes, ears | 100 |
| Brust | shoulders, hips | 40 |
| Arm | elbows, wrists | 15 |
| Bein | knees, ankles | 25 |
### 6.3 Power-Saving
Während des Spiels dimmt sich der Bildschirm nach `powerSaveDimMs` (Standard 10 s) und geht nach `powerSaveSleepMs` (Standard 30 s) in den Tiefschlaf. Kamera und MoveNet werden pausiert, GPS und Kompass laufen weiter. Ein Touch reaktiviert den aktiven Modus sofort.
### 6.4 Server-Validierung
Der Server empfängt `shot` und validiert:
- Spieler am Leben und Cooldown abgelaufen.
- GPS-Plausibilität (keine unrealistischen Positionssprünge).
- Gegner innerhalb Reichweite + Blickkegel.
- `bodyPart` ist gültig.
- Trefferziel ist der GPS-nächste Gegner in Blickrichtung (Konsistenzcheck).
Bei Treffer: Schaden anwenden, Score aktualisieren, `hit`-Event broadcasten.
---
## 7. Spielablauf
```
Lobby (Code teilen) → Spieler joinen → Host konfiguriert Match →
Spielstart → 10 Hz Positions-Updates → Clients zeigen Visier →
Spieler feuert → Server validiert → Hit/Miss → Respawn →
Spielende (Timer oder letzter Spieler) → Scoreboard
```
---
## 8. Wichtige Socket.io-Events
| Event | Richtung | Beschreibung |
|---|---|---|
| `room:create` | C→S | Raum erstellen |
| `room:created` | S→C | Raumcode zurück |
| `room:join` | C→S | Raum beitreten |
| `room:state` | S→C | Aktueller Raumzustand |
| `match:start` | C→S | Spiel starten (Host) |
| `match:started` | S→C | Spiel gestartet |
| `player:update` | C→S | Position + Heading (10 Hz) |
| `state:sync` | S→C | Spieler-Positionen broadcast |
| `player:shoot` | C→S | Schussabgabe mit BodyPart |
| `player:hit` | S→C | Treffer mit Körperteil + Schaden |
| `player:death` | S→C | Spieler ausgeschaltet |
| `player:respawn` | S→C | Spieler wiederbelebt |
| `match:end` | S→C | Match beendet + Scoreboard |
---
## 9. PWA-Setup
- Manifest mit Name, Icons, `display: fullscreen`, Theme.
- Service Worker via `vite-plugin-pwa`.
- Install-Prompt auf Home-Screen.
- HTTPS erforderlich (Kamera, Geolocation, Orientation).
---
## 10. Risiken & Mitigation
| Risiko | Mitigation |
|---|---|
| GPS-Ungenauigkeit | Glättung, großzügige Toleranzen, serverseitige Plausibilität |
| Kompass-Kalibrierung | Anleitung, Live-Heading-Anzeige |
| Akkuverbrauch | Bildschirm an lassen, einstellbare Update-Raten |
| iOS-Safari-Quirks | HTTPS, Permission-Requests, fallbacks |
| Kamera-CV-Performance | MoveNet Lightning, WebGL-Backend |
| Netz im Freien | Reconnect-Logik, Positionscache |
| GPS-Spoofing | Server-Plausibilität, Casual-Play akzeptabel |
---
## 11. Implementierungsphasen
1. **Phase 1 Scaffolding:** Projektstruktur, Vite + React + TS, Tailwind, Socket.io-Server, TensorFlow.js.
2. **Phase 2 Geo & Orientierung:** GPS-Tracking, Kompass, Turf.js.
3. **Phase 3 Backend & Lobby:** Räume, Socket.io-Events, Match-State.
4. **Phase 4 Kamera-CV & Visier:** MoveNet, Body-Part-Erkennung, Fadenkreuz.
5. **Phase 5 Trefferlogik & Gameplay:** Server-autoritative Treffervalidierung, Schaden, Respawn, Score.
6. **Phase 6 Spielablauf & UI:** Lobby, Settings, Spielgebiet, Timer, Scoreboard.
7. **Phase 7 PWA & Polish:** Manifest, Service Worker, Permissions, Performance, Power-Saving, Hit-Feed.
---
*Stand: 26.07.2026*