Files
mp-xieyingeng/GAME_ANALYSIS.md
2026-04-05 13:37:58 +08:00

505 lines
13 KiB
Markdown

# Cocos Creator Game - Complete Point/Score System Analysis
## Project Overview
This is a WeChat Mini-Game built with Cocos Creator. It's a word puzzle game where players guess answers to images within a 60-second time limit. The game uses a "lives" system instead of traditional points/coins.
---
## 1. LIVES/RESOURCE SYSTEM (The Currency/Points Equivalent)
### Storage & Persistence
**File:** `StorageManager.ts`
- **Storage Key:** `game_lives`
- **Default Lives:** 10 (for new users)
- **Minimum Lives:** 0
- **Storage Method:** `sys.localStorage` (Cocos local storage)
### Lives Management Methods:
```typescript
// Core Methods:
- getLives(): number // Get current lives (default 10 if not set)
- setLives(lives: number) // Set lives value (min 0)
- consumeLife(): boolean // Deduct 1 life, returns success status
- addLife(): void // Add 1 life
- hasLives(): boolean // Check if lives > 0
- resetLives(): void // Reset to default 10
```
### Lives Usage:
1. **Unlocking Hints (Clues):** Each unlock costs 1 life
- Clue 1: Free (always unlocked)
- Clue 2: Costs 1 life to unlock
- Clue 3: Costs 1 life to unlock
---
## 2. LEVEL PROGRESSION SYSTEM
**File:** `StorageManager.ts` (User Progress section)
### Progress Data Structure:
```typescript
interface UserProgress {
currentLevelIndex: number; // Current level (0-based)
maxUnlockedLevelIndex: number; // Highest level player reached
}
```
### Progress Storage:
- **Storage Key:** `game_progress`
- **Default:** `{ currentLevelIndex: 0, maxUnlockedLevelIndex: 0 }`
- **Caching:** Cached in memory (`_progressCache`) to avoid repeated localStorage reads
### Progression Methods:
```typescript
- getCurrentLevelIndex(): number // Get current level
- setCurrentLevelIndex(index): void // Set current level
- getMaxUnlockedLevelIndex(): number // Get highest unlocked level
- isLevelUnlocked(levelIndex): boolean // Check if level is playable
- onLevelCompleted(completedLevelIndex) // Called when level is beaten
- resetProgress(): void // Reset to level 1
```
### Level Unlock Logic:
- **Level 1** is always unlocked
- When player completes level N:
- Current level → N+1
- Max unlocked → max(maxUnlocked, N)
- This allows replaying lower levels while progressing forward
---
## 3. GAME LEVEL DATA & API
**File:** `LevelDataManager.ts`
### API Configuration:
```typescript
API_URL = 'https://ilookai.cn/api/v1/wechat-game/levels'
REQUEST_TIMEOUT = 8000ms
API_RETRY_COUNT = 2 (retries on failure)
```
### Level Data Structure (from API):
```typescript
interface ApiLevelData {
id: string; // UUID
level: number; // Level number
imageUrl: string; // Main image URL
hint1: string; // Free clue
hint2: string; // Paid clue (costs 1 life)
hint3: string; // Paid clue (costs 1 life)
answer: string; // The correct answer
sortOrder: number; // Sorting order
}
```
### API Response:
```typescript
interface ApiResponse {
success: boolean;
message: string | null;
data: {
levels: ApiLevelData[];
total: number;
}
}
```
### Image Loading:
- Remote images loaded via `assetManager.loadRemote()`
- Cached in memory (`_imageCache: Map<URL, SpriteFrame>`)
- First level image preloaded during app initialization
- Next level image preloaded silently after entering current level
### Loading Strategy:
1. **On App Start:** Load all level metadata + first level image (80% of loading bar)
2. **On Level Enter:** Load current level image if needed
3. **After Level Completion:** Preload next level asynchronously (doesn't block gameplay)
---
## 4. GAMEPLAY LOOP
**File:** `PageLevel.ts`
### Game Sequence:
1. Player enters level
2. Main image displays with hint 1 visible
3. 60-second countdown starts
4. Player enters answer in single EditBox
5. Player can unlock hints 2 & 3 by spending lives
6. Player submits answer
### Time Limit:
- **Duration:** 60 seconds per level
- **Implementation:** `schedule(this.onCountdownTick, 1)` (1-second interval)
- **On Time Up:** Plays fail sound, but doesn't force level exit
### Input System:
- **Type:** Single EditBox (not multi-input per character)
- **Width:** Dynamic (based on answer length)
- Formula: `Math.min(600, Math.max(200, answerLength * 60 + 40))` pixels
- **Max Length:** Match answer length
- **Placeholder:** Shows answer length as hint
### Answer Processing:
```typescript
getAnswer(): string {
const editBox = this._inputNodes[0].getComponent(EditBox);
return (editBox?.string ?? '').trim(); // Trimmed
}
// Comparison is case-sensitive:
if (userAnswer === this._currentConfig.answer) {
// WIN
} else {
// LOSE
}
```
---
## 5. WINNING & REWARDS
**File:** `PageLevel.ts`
### On Correct Answer:
1. **Stop Timer:** Countdown stops
2. **Play Sound:** Success audio plays
3. **Award 1 Life:** `addLife()` called
4. **Show Modal:** PassModal displays with buttons
### Pass Modal (Victory Screen):
- Shows "Next Level" button
- Shows "Share with Friends" button
- On "Next Level": Progress to next level (calls `onLevelCompleted()`)
- On "Share": Triggers WeChat share with query param `?level=<levelIndex>`
### Progression on Pass:
```typescript
StorageManager.onLevelCompleted(currentLevelIndex);
// Sets:
// - currentLevelIndex → currentLevelIndex + 1
// - maxUnlockedLevelIndex → max(maxUnlocked, currentLevelIndex)
```
### Game End Condition:
- When `currentLevelIndex >= totalLevels`, player has beaten all levels
- Game returns to home page
---
## 6. LOSING & CONSEQUENCES
**File:** `PageLevel.ts`
### On Wrong Answer:
1. **Play Sound:** Fail audio plays
2. **Vibration:** `WxSDK.vibrateLong()` (400ms vibration on WeChat)
3. **Toast Message:** "答案错误,再试试吧!" (Answer wrong, try again!)
4. **No Penalty:** No life deducted, level doesn't change
### On Time Up:
1. **Play Sound:** Fail audio plays
2. **Countdown Stops:** `_isTimeUp = true`
3. **No Forced Exit:** Player can continue typing and submitting
4. **No Life Penalty:** Still can retry
---
## 7. HINT/CLUE SYSTEM
**File:** `PageLevel.ts`
### Clue Mechanics:
1. **Clue 1:** Always visible, FREE
2. **Clue 2:** Hidden by default, costs 1 life to unlock
3. **Clue 3:** Hidden by default, costs 1 life to unlock
### Unlocking Process:
```typescript
onUnlockClue(index: number) {
// 1. Check if lives available
if (!this.hasLives()) return;
// 2. Consume 1 life
if (!this.consumeLife()) return;
// 3. Play click sound
this.playClickSound();
// 4. Hide unlock button
this.hideUnlockButton(index);
// 5. Show clue content
this.showClue(index);
this.setClue(index, clueContent);
}
```
### Clue Cost Implications:
- Player starts with 10 lives
- Can unlock both clues 2 & 3 = 2 lives spent minimum
- But only 8 lives remain per level if both used
- Player can conserve lives by solving without clues
---
## 8. NETWORK & API COMMUNICATION
**File:** `HttpUtil.ts`
### HTTP Methods:
```typescript
// GET request
HttpUtil.get<T>(url: string, timeout: number = 10000): Promise<T>
// POST request
HttpUtil.post<T>(url: string, data: object, timeout: number = 10000): Promise<T>
```
### Implementation:
- Uses `XMLHttpRequest`
- Supports JSON responses
- Default timeout: 10 seconds
- Error handling: Rejects on HTTP errors, timeouts, or network failures
### Used By:
- `LevelDataManager` uses `HttpUtil.get()` to fetch level data from API
- No POST requests currently used
---
## 9. WECHAT SDK INTEGRATION
**File:** `WxSDK.ts`
### WeChat Features Used:
#### 1. Platform Detection:
```typescript
isWechat(): boolean
// Returns: sys.platform === sys.Platform.WECHAT_GAME
```
#### 2. Sharing:
- **Share Menu:** `showShareMenu()` - Enables share button in header
- **Friend Share:** `onShareAppMessage(config)` - Right-click "Share" message
- **Timeline Share:** `onShareTimeline(config)` - Moments sharing
- **Active Share:** `shareAppMessage(config)` - Trigger share dialog
#### 3. Vibration:
- **Short Vibrate:** `vibrateShort()` - 15ms, for button clicks
- **Long Vibrate:** `vibrateLong()` - 400ms, for errors
#### 4. Share Configuration:
```typescript
interface WxShareConfig {
title: string; // Share title: "写英语"
imageUrl?: string; // Share image (optional)
query?: string; // Query params (e.g., "level=5")
}
```
#### 5. Initialization:
```typescript
WxSDK.initShare(config) {
// Calls in sequence:
// 1. showShareMenu()
// 2. onShareAppMessage(config)
// 3. onShareTimeline(config)
}
```
**Called in:** PageHome on game start
---
## 10. GAME STATE MANAGEMENT
**File:** `ViewManager.ts` (Page Stack) + `StorageManager.ts` (Data)
### View Stack (Navigation):
- Maintains page stack for navigation
- `PageHome` (z-index 0) - Main menu
- `PageLevel` (z-index 1) - Game level
- `PassModal` (z-index 999) - Victory overlay
### Persistent State:
- Lives stored in localStorage with key `game_lives`
- Progress stored in localStorage with key `game_progress`
- Both persist across app sessions
- Data survives app closure and reopening
### Runtime State:
- Current countdown timer
- Current input box content
- Unlocked clues state (reset each level)
- Current level config (API data)
---
## 11. LOADING PAGE FLOW
**File:** `PageLoading.ts`
### Initialization Sequence:
1. **Stage 1 (0-30%):** Fetch all levels from API via `LevelDataManager.initialize()`
- API call with retry logic
- Parse level metadata
- NOT loading all images yet
2. **Stage 2 (30-80%):** Preload first level image
- Uses `LevelDataManager.ensureLevelReady(0)`
- Shows "正在加载游戏必备资源..." message
3. **Stage 3 (80-100%):** Preload PageHome view
- `ViewManager.preload('PageHome')`
- Shows "正在加载界面资源..." message
4. **Completion (100%):** Open PageHome and destroy loading page
---
## 12. COMPLETE POINTS FLOW DIAGRAM
```
START GAME
[10 Lives] (default)
LEVEL 1 STARTS
├─ View Clue 1 (FREE)
├─ Option: Unlock Clue 2 (-1 Life) → [9 Lives]
├─ Option: Unlock Clue 3 (-1 Life) → [8 Lives]
├─ Player submits answer
├─ IF CORRECT:
│ ├─ Add 1 Life → [9 or 10+ Lives]
│ ├─ Show PassModal
│ └─ Move to LEVEL 2
└─ IF WRONG:
├─ Play fail sound & vibrate
├─ Show toast message
├─ Lives unchanged
└─ Can retry (no level exit)
IF ALL LEVELS COMPLETE:
└─ Return to home
IF TIME UP:
├─ Play fail sound
├─ Can still submit (lives unchanged)
└─ No forced exit
```
---
## 13. KEY FILES SUMMARY
| File | Purpose | Key Components |
|------|---------|-----------------|
| StorageManager.ts | Data persistence | Lives + Progress storage |
| LevelDataManager.ts | Level data loading | API calls + Image caching |
| PageLevel.ts | Main game logic | Countdown, input, hints, validation |
| PageLoading.ts | App initialization | Loading bar + progress |
| PageHome.ts | Home screen | Start game button |
| PassModal.ts | Victory screen | Next/Share buttons |
| ViewManager.ts | Page navigation | View stack + caching |
| WxSDK.ts | WeChat API | Share + vibration |
| HttpUtil.ts | Network requests | GET/POST + error handling |
| ToastManager.ts | Notifications | Brief toast messages |
---
## 14. IMPORTANT CONSTANTS
### Game Constants:
- **Level Time Limit:** 60 seconds
- **Default Lives:** 10
- **Life Cost per Hint:** 1 life per hint (hints 2 & 3)
- **Reward per Level:** +1 life
### API Constants:
- **Endpoint:** `https://ilookai.cn/api/v1/wechat-game/levels`
- **Timeout:** 8000ms
- **Retry Count:** 2
### UI Constants:
- **PageHome z-index:** 0
- **PageLevel z-index:** 1
- **PassModal z-index:** 999
---
## 15. MISSING FEATURES (Observations)
1. **No User Authentication:** No wx.login call visible
2. **No Backend Sync:** No calls to save progress to server
3. **No Ads/IAP:** No monetization system
4. **No Leaderboards:** No score submission to WeChat
5. **No Analytics:** No tracking events beyond console logs
6. **No Life Refill:** No premium way to get more lives
7. **No Difficulty Levels:** All players see same levels
8. **No Sound Toggle:** Sound plays automatically
---
## 16. DATA FLOW SUMMARY
```
WeChat API: https://ilookai.cn/api/v1/wechat-game/levels
LevelDataManager (fetch + cache)
PageLevel (display + gameplay)
├─ InputBox (player answer)
├─ Clues (cost lives to unlock)
└─ Timer (60 second countdown)
StorageManager (save lives + progress)
localStorage
├─ game_lives: number
└─ game_progress: UserProgress (JSON)
```
---
## 17. CRITICAL BUSINESS LOGIC
### Win Condition:
```
userAnswer (trimmed) === correctAnswer (from API)
→ Award +1 life
→ Save progress
→ Move to next level
```
### Lose Condition:
```
userAnswer !== correctAnswer
→ No penalty
→ Can retry immediately
→ Timer continues (even after time up)
```
### Progression:
```
Beat Level N
→ currentLevel = N + 1
→ maxUnlocked = max(maxUnlocked, N)
→ Reward: +1 life (so levels can chain profitably)
```
### Economy Balance:
- Start: 10 lives
- Per level: Can spend 0-2 lives (hints) or 0 lives (no hints)
- Per level: Earn +1 life (net: -1 or +1 lives)
- Average player with no hints: +1 life/level → infinite scaling
- Average player with 1 hint: 0 lives/level → stable
- Hardcore with 2 hints: -1 life/level → finite runway