Add lessons learned README for frontend POC
This commit is contained in:
parent
dc06a93e2e
commit
c594f0c8f8
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user