Initial commit: Mobil_Tag project setup
This commit is contained in:
@@ -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:** 2–10 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*
|
||||
Reference in New Issue
Block a user