13 KiB
13 KiB
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:
// 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:
- 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:
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:
- 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:
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):
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:
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:
- On App Start: Load all level metadata + first level image (80% of loading bar)
- On Level Enter: Load current level image if needed
- After Level Completion: Preload next level asynchronously (doesn't block gameplay)
4. GAMEPLAY LOOP
File: PageLevel.ts
Game Sequence:
- Player enters level
- Main image displays with hint 1 visible
- 60-second countdown starts
- Player enters answer in single EditBox
- Player can unlock hints 2 & 3 by spending lives
- 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
- Formula:
- Max Length: Match answer length
- Placeholder: Shows answer length as hint
Answer Processing:
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:
- Stop Timer: Countdown stops
- Play Sound: Success audio plays
- Award 1 Life:
addLife()called - 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:
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:
- Play Sound: Fail audio plays
- Vibration:
WxSDK.vibrateLong()(400ms vibration on WeChat) - Toast Message: "答案错误,再试试吧!" (Answer wrong, try again!)
- No Penalty: No life deducted, level doesn't change
On Time Up:
- Play Sound: Fail audio plays
- Countdown Stops:
_isTimeUp = true - No Forced Exit: Player can continue typing and submitting
- No Life Penalty: Still can retry
7. HINT/CLUE SYSTEM
File: PageLevel.ts
Clue Mechanics:
- Clue 1: Always visible, FREE
- Clue 2: Hidden by default, costs 1 life to unlock
- Clue 3: Hidden by default, costs 1 life to unlock
Unlocking Process:
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:
// 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:
LevelDataManagerusesHttpUtil.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:
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:
interface WxShareConfig {
title: string; // Share title: "写英语"
imageUrl?: string; // Share image (optional)
query?: string; // Query params (e.g., "level=5")
}
5. Initialization:
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 menuPageLevel(z-index 1) - Game levelPassModal(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:
-
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
-
Stage 2 (30-80%): Preload first level image
- Uses
LevelDataManager.ensureLevelReady(0) - Shows "正在加载游戏必备资源..." message
- Uses
-
Stage 3 (80-100%): Preload PageHome view
ViewManager.preload('PageHome')- Shows "正在加载界面资源..." message
-
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)
- No User Authentication: No wx.login call visible
- No Backend Sync: No calls to save progress to server
- No Ads/IAP: No monetization system
- No Leaderboards: No score submission to WeChat
- No Analytics: No tracking events beyond console logs
- No Life Refill: No premium way to get more lives
- No Difficulty Levels: All players see same levels
- 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