Add lessons learned README for frontend POC

This commit is contained in:
Cal Corum 2026-01-31 22:06:10 -06:00
parent dc06a93e2e
commit c594f0c8f8

View File

@ -1,5 +1,210 @@
# Vue 3 + TypeScript + Vite
# Frontend POC - F3 Phaser Demo
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
This is a proof-of-concept implementation of the Mantimon TCG frontend through Phase F3 (Phaser Integration). It demonstrates Vue 3 + Phaser 3 integration with real card data.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
## What This POC Demonstrates
- **Vue-Phaser Integration**: Bidirectional communication between Vue components and Phaser scenes
- **Card Rendering**: Real card images with type-colored borders and damage counters
- **State Synchronization**: Game state flows from Vue to Phaser via the gameBridge
- **Interactive Cards**: Click and hover events propagate from Phaser back to Vue
## Running the Demo
```bash
cd .claude/frontend-poc
npm install
npm run dev
```
Navigate to `/demo` after logging in (requires auth).
## Key Lessons Learned
### 1. Scene Initialization Timing
**Problem**: PhaserGame.vue was calling `createGame(container)` without passing scenes, resulting in an empty canvas.
**Solution**: Import and pass the scenes array:
```typescript
import { createGame, scenes } from '@/game'
game.value = createGame(container.value, scenes)
```
**File**: `src/components/game/PhaserGame.vue`
### 2. Event Bridge Timing (Critical)
**Problem**: DemoPage listened for PhaserGame's Vue `ready` event (Phaser core ready), but this fires BEFORE MatchScene's `create()` runs. When state was sent, MatchScene hadn't subscribed to events yet.
**Solution**: Listen for the gameBridge's `ready` event instead, which MatchScene emits at the END of `create()` after subscribing to events:
```typescript
// WRONG - fires too early
function handlePhaserReady(game: Phaser.Game): void {
emitToBridge('state:updated', gameState.value) // MatchScene not ready!
}
// CORRECT - wait for MatchScene
onMounted(() => {
onBridgeEvent('ready', handleSceneReady)
})
function handleSceneReady(): void {
emitToBridge('state:updated', gameState.value) // MatchScene is subscribed
}
```
**Files**: `src/pages/DemoPage.vue`, `src/game/scenes/MatchScene.ts`
### 3. Card Image Loading
**Problem**: Card definitions have `image_path` field (e.g., `"a1/094-pikachu.webp"`) but the loader was constructing paths from `set_id` and `set_number`.
**Solution**: Add `loadCardImageFromPath()` function that uses `image_path` directly:
```typescript
// Card.ts - prefer image_path
if (this.cardDefinition.image_path) {
loadedKey = await loadCardImageFromPath(
this.scene,
textureKey,
this.cardDefinition.image_path
)
}
```
**Files**: `src/game/assets/loader.ts`, `src/game/objects/Card.ts`
### 4. Asset Base URL
Card images are served from `/game/cards/{image_path}`:
- Card back: `/game/cards/card_back.webp`
- Pokemon: `/game/cards/a1/094-pikachu.webp`
- Energy: `/game/cards/basic/lightning.webp`
**File**: `src/game/assets/manifest.ts` (`ASSET_BASE_URL = '/game'`)
## Layout Issues (For Phase F4)
The demo revealed several layout issues that need fixing:
1. **Opponent cards upside down** - Rotation not applied correctly for opponent's side
2. **Cards overlapping in zones** - Zone arrangement logic needs work
3. **Hand cards cut off** - Hand zone extends beyond viewport
4. **Zone positioning** - Active/bench/pile positions need adjustment
5. **Card sizes** - May need responsive sizing based on viewport
## Architecture Overview
```
Vue Component (DemoPage/GamePage)
|
| emits 'state:updated'
v
gameBridge (mitt event emitter)
|
| subscribed in create()
v
MatchScene (Phaser Scene)
|
| delegates rendering
v
StateRenderer
|
| creates/updates
v
Zone objects (ActiveZone, BenchZone, HandZone, etc.)
|
| contain
v
Card objects (with image loading, damage counters)
```
### Key Files
| File | Purpose |
|------|---------|
| `src/game/bridge.ts` | GameBridge class - typed event emitter for Vue-Phaser communication |
| `src/composables/useGameBridge.ts` | Vue composable with auto-cleanup |
| `src/game/scenes/MatchScene.ts` | Main Phaser scene - subscribes to bridge events |
| `src/game/sync/StateRenderer.ts` | Converts VisibleGameState to Phaser objects |
| `src/game/objects/Card.ts` | Card game object with image loading |
| `src/game/objects/Zone.ts` | Base zone class for card containers |
| `src/game/layout.ts` | Board layout calculations |
## State Flow
1. **Vue initializes state** (mockGameState.ts or from WebSocket)
2. **Vue emits to bridge**: `emitToBridge('state:updated', gameState)`
3. **MatchScene receives**: Handler calls `stateRenderer.render(state)`
4. **StateRenderer processes**:
- Creates zones if needed
- Updates zone positions from layout
- Creates/updates Card objects from state
- Sets cards face-up/face-down based on visibility
5. **User interacts with card** (click/hover in Phaser)
6. **Card emits to bridge**: `gameBridge.emit('card:clicked', {...})`
7. **Vue receives**: Handler updates UI (selected card, etc.)
## Type Definitions
Key types are in `src/types/game.ts`:
```typescript
interface VisibleGameState {
game_id: string
viewer_id: string
players: Record<string, VisiblePlayerState>
card_registry: Record<string, CardDefinition>
phase: TurnPhase
is_my_turn: boolean
// ...
}
interface CardDefinition {
id: string
name: string
card_type: 'pokemon' | 'trainer' | 'energy'
image_path?: string // Added for image loading
// ...
}
interface CardInstance {
instance_id: string
definition_id: string
damage: number
attached_energy: CardInstance[]
// ...
}
```
## Testing the Demo
The demo page (`/demo`) provides:
- **Debug Panel**: Shows zone counts, game state, selected card
- **Controls**:
- Next Phase - cycles through turn phases
- Toggle Turn - switches between player/opponent turn
- +10 Damage buttons - adds damage to active Pokemon
- Reset State - returns to initial state
## What's NOT in This POC
- Real WebSocket connection (uses mock state)
- Game actions (play card, attack, etc.)
- Animations for card movement
- Proper responsive scaling
- Prize card reveal
- Stadium cards
- Energy attachment display on cards
## Recommendations for True Game Page
1. **Keep the bridge pattern** - It works well for decoupling Vue and Phaser
2. **Fix layout before adding features** - Get zone positions right first
3. **Add animation system** - Cards should animate when moving between zones
4. **Implement action validation UI** - Highlight valid targets before confirming
5. **Consider zone click handling** - For dropping cards, not just card clicks
6. **Test on mobile early** - Touch targets and scaling will need tuning