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