diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 469fe9a..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,496 +0,0 @@ -# Architecture Overview - -## System Architecture Diagram - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ COCOS CREATOR GAME │ -│ WeChat Mini-Game Version 3.8.8 │ -└─────────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────────┐ -│ PRESENTATION LAYER (UI) │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ PageLoading.ts ──────┐ │ -│ (Loading Screen) │ │ -│ │ │ -│ ┌──────────┐ │ ┌──────────┐ ┌──────────┐ │ -│ │PageHome │─────────┴──────│PageLevel │──────│PassModal │ │ -│ │(Menu) │ │ (Play) │ │(Victory) │ │ -│ └──────────┘ └──────────┘ └──────────┘ │ -│ ▲ ▲ │ │ -│ │ │ │ │ -│ └────────────────┬───────────┘ │ │ -│ │ (Back button) │ │ -│ └─────────────────────────────┘ │ -│ │ -│ Toast.ts ────────────────────────────────────────────────────────────│ -│ (Notifications) │ -│ │ -└─────────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────────┐ -│ CORE LOGIC LAYER │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────┐ ┌──────────────────────┐ │ -│ │ PageLevel.ts │ │ PassModal.ts │ │ -│ ├──────────────────────┤ ├──────────────────────┤ │ -│ │ • Answer validation │ │ • Next level │ │ -│ │ • Hint unlock (-life)│ │ • Share button │ │ -│ │ • Countdown (60s) │ │ • Callbacks │ │ -│ │ • Sound/Vibration │ │ │ │ -│ │ • Life display │ │ │ │ -│ └──────────────────────┘ └──────────────────────┘ │ -│ ▲ ▲ │ -│ │ │ │ -│ └────────────────┬───────────────┘ │ -│ │ │ -│ ┌──────────────────────────┴──────────────────────────────────┐ │ -│ │ ViewManager.ts (View Navigation) │ │ -│ ├──────────────────────────────────────────────────────────────┤ │ -│ │ • Page registration & caching │ │ -│ │ • Page stack management (push/pop/replace) │ │ -│ │ • Lifecycle: onViewLoad → onViewShow → onViewHide → destroy │ │ -│ │ • Parameter passing between pages │ │ -│ └──────────────────────────────────────────────────────────────┘ │ -│ ▲ │ -│ │ │ -│ BaseView.ts (Abstract base class for all pages) │ -│ • Lifecycle hooks │ -│ • Page state management │ -│ │ -└─────────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────────┐ -│ DATA & STATE MANAGEMENT LAYER │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────────────────────────────────────────────┐ │ -│ │ StorageManager.ts (Single Source of Truth) │ │ -│ ├──────────────────────────────────────────────────────────────┤ │ -│ │ Lives Management: │ │ -│ │ • getLives() / setLives() / consumeLife() / addLife() │ │ -│ │ │ │ -│ │ Progress Management: │ │ -│ │ • getCurrentLevelIndex() / getMaxUnlockedLevelIndex() │ │ -│ │ • onLevelCompleted(levelIndex) │ │ -│ │ • isLevelUnlocked(levelIndex) │ │ -│ │ │ │ -│ │ Storage Backend: sys.localStorage │ │ -│ │ └─ game_lives: string number (default: "10") │ │ -│ │ └─ game_progress: JSON with currentIndex & maxUnlockedIdx │ │ -│ └──────────────────────────────────────────────────────────────┘ │ -│ ▲ │ -│ │ │ -│ ┌──────────────────────────────────────────────────────────────┐ │ -│ │ LevelDataManager.ts (Level Data & Assets) │ │ -│ ├──────────────────────────────────────────────────────────────┤ │ -│ │ • initialize(onProgress) - Fetch API & load first level │ │ -│ │ • getLevelConfig(index) - Get level data + image │ │ -│ │ • ensureLevelReady(index) - On-demand level loading │ │ -│ │ • preloadNextLevel(currentIndex) - Async preload │ │ -│ │ │ │ -│ │ Memory Caches: │ │ -│ │ • _apiData: All levels from server │ │ -│ │ • _levelConfigs: Map of loaded level configs │ │ -│ │ • _imageCache: Map of loaded images (SpriteFrames) │ │ -│ │ • _loadingLevels: Set of levels being loaded │ │ -│ └──────────────────────────────────────────────────────────────┘ │ -│ ▲ │ -│ │ │ -└─────────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────────┐ -│ UTILITY & SDK LAYER │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌────────────────────┐ ┌────────────────────┐ ┌──────────────────┐ │ -│ │ HttpUtil.ts │ │ WxSDK.ts │ │ToastManager.ts │ │ -│ ├────────────────────┤ ├────────────────────┤ ├──────────────────┤ │ -│ │ • get() │ │ • isWechat() │ │ • init() │ │ -│ │ • post() │ │ • initShare() │ │ • show() │ │ -│ │ │ │ • shareAppMessage()│ │ │ │ -│ │ Timeout: 10s │ │ • onShareAppMsg() │ │ Display duration │ │ -│ │ Error handling │ │ • vibrateShort() │ │ Fade out anim │ │ -│ │ │ │ • vibrateLong() │ │ │ │ -│ └────────────────────┘ └────────────────────┘ └──────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────┘ - -┌─────────────────────────────────────────────────────────────────────────┐ -│ EXTERNAL SYSTEMS │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────────────────────┐ │ -│ │ WeChat Mini-Game SDK (wx global) │ │ -│ ├──────────────────────────────────────┤ │ -│ │ • wx.shareAppMessage() │ │ -│ │ • wx.onShareAppMessage() │ │ -│ │ • wx.showShareMenu() │ │ -│ │ • wx.vibrateShort() │ │ -│ │ • wx.vibrateLong() │ │ -│ └──────────────────────────────────────┘ │ -│ ▲ │ -│ │ │ -│ │ │ -│ ┌──────────────────────────────────────┐ │ -│ │ Backend API Server │ │ -│ │ https://ilookai.cn │ │ -│ ├──────────────────────────────────────┤ │ -│ │ GET /api/v1/wechat-game/levels │ │ -│ │ • Returns: {success, data, message} │ │ -│ │ • Retry: 2x with 1s delay │ │ -│ │ • Timeout: 8s │ │ -│ └──────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Data Flow Diagram: Complete User Session - -``` -START - │ - ├─ [main.ts] onLoad() - │ └─ ViewManager.init(canvas) - │ └─ ToastManager.init(toastPrefab) - │ └─ Register PageHome & PageLevel - │ - ├─ [PageLoading.ts] start() - │ ├─ LevelDataManager.initialize() - │ │ ├─ HttpUtil.get() → API call - │ │ │ └─ https://ilookai.cn/api/v1/wechat-game/levels - │ │ ├─ Cache response → _apiData - │ │ ├─ Load first level image - │ │ │ └─ assetManager.loadRemote() → SpriteFrame - │ │ │ └─ Cache in _imageCache - │ │ └─ Return success - │ │ - │ └─ ViewManager.preload('PageHome') - │ └─ ViewManager.open('PageHome') - │ └─ PageHome.onViewLoad() - │ └─ WxSDK.initShare() - │ - ├─ [USER CLICKS "START GAME"] - │ - ├─ ViewManager.open('PageLevel', {params: {levelIndex: 0}}) - │ └─ PageLevel.onViewLoad() - │ ├─ StorageManager.getCurrentLevelIndex() - │ ├─ PageLevel.initLevel() - │ │ ├─ LevelDataManager.ensureLevelReady(levelIndex) - │ │ │ ├─ Check cache (_levelConfigs) - │ │ │ ├─ If not cached: load image + create config - │ │ │ └─ Store in cache - │ │ │ - │ │ └─ PageLevel._applyLevelConfig() - │ │ ├─ Display main image (sprite) - │ │ ├─ Show Hint 1 (free) - │ │ ├─ Hide Hints 2, 3 (show unlock buttons) - │ │ ├─ Create input field based on answer.length - │ │ ├─ Display lives: StorageManager.getLives() - │ │ ├─ Start countdown (60s) - │ │ └─ LevelDataManager.preloadNextLevel() [async] - │ - ├─ [USER INTERACTS] - │ - ├─ Option A: UNLOCK HINT - │ ├─ PageLevel.onUnlockClue(2 or 3) - │ ├─ Check: StorageManager.hasLives()? - │ ├─ Yes → StorageManager.consumeLife() → display hint - │ └─ No → Show "生命值不足" message - │ - ├─ Option B: SUBMIT ANSWER - │ ├─ PageLevel.onSubmitAnswer() - │ ├─ Validate: userAnswer === correctAnswer? - │ │ - │ ├─ YES (Correct) - │ │ ├─ PageLevel.showSuccess() - │ │ ├─ Stop countdown - │ │ ├─ Play successAudio - │ │ ├─ StorageManager.addLife() [+1] - │ │ ├─ PageLevel._showPassModal() - │ │ │ ├─ Instantiate PassModal prefab - │ │ │ ├─ PassModal.onViewLoad() - │ │ │ ├─ PassModal.onViewShow() - │ │ │ │ └─ Play success sound - │ │ │ │ └─ Adjust widget to full screen - │ │ │ │ - │ │ │ └─ User clicks button: - │ │ │ ├─ "Next Level" → PageLevel.nextLevel() - │ │ │ │ ├─ StorageManager.onLevelCompleted(currentIndex) - │ │ │ │ │ ├─ currentLevelIndex++ - │ │ │ │ │ ├─ maxUnlockedLevelIndex = max(prev, current) - │ │ │ │ │ └─ Save to localStorage - │ │ │ │ ├─ Reload PageLevel with new index - │ │ │ │ │ └─ Repeat from Level Setup - │ │ │ │ │ - │ │ │ │ └─ If last level: - │ │ │ │ └─ ViewManager.back() → return to PageHome - │ │ │ │ - │ │ │ └─ "Share" → WxSDK.shareAppMessage() - │ │ │ └─ query: `level=${currentIndex+1}` - │ │ - │ ├─ NO (Wrong) - │ │ ├─ PageLevel.showError() - │ │ ├─ Play failAudio - │ │ ├─ WxSDK.vibrateLong() [device vibration] - │ │ ├─ ToastManager.show("答案错误,再试试吧!") - │ │ │ ├─ Create Toast node - │ │ │ ├─ Show for 2000ms - │ │ │ ├─ Fade out animation (300ms) - │ │ │ └─ Destroy node - │ │ │ - │ │ └─ Continue playing (allow retry) - │ - ├─ Option C: TIMEOUT - │ └─ Countdown reaches 0 - │ ├─ PageLevel.onTimeUp() - │ ├─ Play failAudio - │ └─ [Incomplete: can still submit after timeout] - │ - ├─ [USER CLICKS BACK BUTTON] - │ ├─ ViewManager.back() - │ │ ├─ PageLevel._doHide() - │ │ └─ PageHome._doShow() - │ │ - │ └─ Return to home (progress saved in localStorage) - │ - └─ END -``` - ---- - -## State Management Flow - -``` -USER PROGRESS STATE -│ -├─ localStorage: game_progress -│ └─ { -│ "currentLevelIndex": 5, -│ "maxUnlockedLevelIndex": 5 -│ } -│ -├─ Memory Cache (LevelDataManager) -│ ├─ _apiData: ApiLevelData[] (all levels) -│ ├─ _levelConfigs: Map -│ ├─ _imageCache: Map -│ └─ _loadingLevels: Set (currently loading) -│ -└─ Session State (PageLevel component) - ├─ currentLevelIndex: number - ├─ _currentConfig: RuntimeLevelConfig - ├─ _countdown: number (60 → 0) - ├─ _isTimeUp: boolean - ├─ _isTransitioning: boolean - └─ _passModalNode: Node | null - - -LIVES STATE -│ -├─ localStorage: game_lives -│ └─ "10" (string representation of number) -│ -├─ Cached value (StorageManager._progressCache) -│ └─ Read on first access, cached for performance -│ -└─ Operations (immediate storage update) - ├─ getLives() → read from storage - ├─ setLives(n) → validate & write to storage - ├─ consumeLife() → getLives() - 1, setLives() - ├─ addLife() → getLives() + 1, setLives() - └─ hasLives() → getLives() > 0 - - -API CACHE -│ -├─ First Load (Startup) -│ ├─ HttpUtil.get(apiUrl, 8000ms timeout) -│ ├─ Retry: 2 times (1s delay between) -│ └─ Cache in LevelDataManager._apiData -│ -└─ Per-Level Resources (On-Demand) - ├─ Check _levelConfigs cache - ├─ If missing: assetManager.loadRemote(imageUrl) - ├─ Create SpriteFrame - └─ Cache in _imageCache -``` - ---- - -## View Stack & Navigation - -``` -VIEW STACK (LIFO - Last In First Out) -│ -├─ Level 0 (Bottom) -│ └─ PageHome -│ -├─ Level 1 (Middle) -│ └─ PageLevel -│ -├─ Level 2 (Top - Current) -│ └─ PassModal (temporary, on PageLevel) -│ -└─ Operations - ├─ open(viewId) → push to stack + show - ├─ back() → pop from stack + hide + show prev - ├─ replace(viewId) → pop + push new view - └─ close() → pop + show previous - - -CACHING BEHAVIOR -│ -├─ PageHome -│ ├─ cache: true -│ └─ Cached after first open, reused on back -│ -├─ PageLevel -│ ├─ cache: true -│ └─ Cached per instance (but reinitializes on each open) -│ -└─ PassModal - ├─ Dynamically instantiated - ├─ Not cached - └─ Destroyed after close -``` - ---- - -## Class Hierarchy - -``` -Component (Cocos) - │ - ├─ BaseView (Abstract base) - │ ├─ PageHome - │ ├─ PageLevel - │ └─ PassModal - │ - └─ Toast - - -Managers (Singleton) - │ - ├─ ViewManager - │ └─ Manages page lifecycle & navigation - │ - ├─ LevelDataManager - │ └─ Manages API data & asset loading - │ - ├─ StorageManager - │ └─ Manages user data persistence - │ - ├─ ToastManager - │ └─ Manages toast notifications - │ - └─ WxSDK - └─ Manages WeChat integration - - -Utilities - │ - ├─ HttpUtil - │ └─ Static HTTP methods (GET/POST) - │ - └─ LevelTypes - └─ TypeScript interfaces for API data -``` - ---- - -## Dependency Graph - -``` -main.ts - ├─ ViewManager - ├─ ToastManager - └─ [Page Prefabs] - ├─ PageHome - │ └─ WxSDK - │ - ├─ PageLevel - │ ├─ StorageManager - │ ├─ WxSDK - │ ├─ LevelDataManager - │ ├─ ToastManager - │ └─ PassModal - │ ├─ WxSDK - │ └─ BaseView - │ - └─ PageLoading - ├─ ViewManager - ├─ LevelDataManager - │ ├─ HttpUtil - │ │ └─ XMLHttpRequest - │ ├─ LevelTypes - │ └─ Cocos assetManager - └─ ToastManager - - -EXTERNAL APIS - │ - ├─ https://ilookai.cn/api/v1/wechat-game/levels - │ └─ Called by LevelDataManager._fetchApiData() - │ -└─ WeChat SDK (wx global) - ├─ wx.shareAppMessage() - ├─ wx.onShareAppMessage() - ├─ wx.showShareMenu() - ├─ wx.vibrateShort() - └─ wx.vibrateLong() -``` - ---- - -## Performance Considerations - -### Memory Management -- **Image Loading**: Remote images loaded on-demand with local caching -- **Level Configs**: Loaded incrementally, current + next level only -- **View Caching**: Pages cached after first load, reused on navigation back -- **Message Queuing**: No event queue; direct method calls - -### Network -- **Single API Call**: Startup only (all levels fetched at once) -- **Timeout**: 8 seconds for API requests -- **Retry Logic**: 2 attempts with 1-second delay -- **Cache Strategy**: In-memory cache, no expiration - -### Storage -- **localStorage**: 2 keys (lives + progress) -- **No Batching**: Each update writes immediately -- **Synchronous**: No async operations needed - -### UI/Graphics -- **Single Image**: Main puzzle image per level -- **Dynamic Input**: Input field size adjusts based on answer length -- **Audio**: One-shot playback, no loops -- **Animations**: Toast fade-out only - ---- - -## Security Considerations - -### Current Implementation -- ⚠️ All data stored locally (no encryption) -- ⚠️ No user authentication -- ⚠️ No server-side validation -- ⚠️ Progress can be manually edited via localStorage - -### Vulnerabilities -1. localStorage can be inspected/modified via browser console -2. No server-side checks on progress/lives -3. Can modify localStorage to skip levels -4. Can modify lives directly - -### Improvements Needed -- Server-side progress validation -- User authentication -- Encrypted storage -- Server-side truth for lives/progress - diff --git a/ARCHITECTURE_DIAGRAM.md b/ARCHITECTURE_DIAGRAM.md deleted file mode 100644 index fe321f2..0000000 --- a/ARCHITECTURE_DIAGRAM.md +++ /dev/null @@ -1,665 +0,0 @@ -# Architecture & Flow Diagrams - -## System Architecture - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ Cocos Creator Runtime │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ UI Layer (This Analysis) │ │ -│ ├──────────────────────────────────────────────────────────┤ │ -│ │ │ │ -│ │ ┌────────────────┐ ┌──────────────┐ │ │ -│ │ │ main.ts │────────▶│ ViewManager │ │ │ -│ │ │ (Bootstrap) │ │ (Singleton) │ │ │ -│ │ └────────────────┘ └──────────────┘ │ │ -│ │ │ │ │ -│ │ Pages (All extend BaseView): │ │ │ -│ │ ┌─────────────────────────────────┼──────────────┐ │ │ -│ │ │ • PageLoading (init data) │ │ │ │ -│ │ │ • PageHome (hub) │ │ │ │ -│ │ │ • PageLevel (game - complex) ◀┘ │ │ │ -│ │ │ • PagePreviewLevels (list) │ │ │ -│ │ │ • PassModal (modal) │ │ │ -│ │ └────────────────────────────────────────────────┘ │ │ -│ │ │ │ -│ │ ┌────────────────┐ ┌──────────────┐ │ │ -│ │ │ ToastManager │────────▶│ Toast │ │ │ -│ │ │ (Singleton) │ │ (Component) │ │ │ -│ │ └────────────────┘ └──────────────┘ │ │ -│ │ │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ Data Layer (External - Not Analyzed) │ │ -│ ├──────────────────────────────────────────────────────────┤ │ -│ │ • AuthManager • LevelDataManager │ │ -│ │ • StorageManager • UserAssetsManager │ │ -│ │ • ShareManager • WxSDK │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Page State Machine - -``` - ┌─────────────────────────────┐ - │ Application Starts │ - └──────────────┬──────────────┘ - │ - ▼ - ┌──────────────────────┐ - │ main.onLoad() │ - │ Registers all pages │ - └──────────┬───────────┘ - │ - ▼ - ┌──────────────────────────────┐ - │ PageLoading displayed │ - │ - Load auth + levels │ - │ - Sync progress │ - │ - Check share code │ - └──────────────┬───────────────┘ - │ - ┌──────────────┴──────────────┐ - │ Normal Mode │ Share Mode │ - ▼ ▼ │ - ┌──────────────────┐ ┌───────────────┐ │ - │ PageHome │ │ PageLevel │ │ - │ [Start] [PK] │ │ (in share) │ │ - └────┬─────┬───────┘ └───────┬───────┘ │ - │ │ │ │ - ┌────────▼─┐ └────────────┬──────┴────────┘ - │ │ │ - ▼ ▼ ▼ -PageLevel PageWrite (Repeat levels) -(game) Levels │ - │ (create) ▼ - │ │ PassModal - │ ▼ [Share] - │ PagePreview │ - │ Levels ▼ - │ (verify) Back to Home - │ │ - │ ▼ - │ PassModal - │ [Next/Share] - │ │ - └──────────┼─────────┐ - │ │ - ┌────▼──┐ ┌───▼────┐ - │ Next │ │ Share │ - │ Loop │ │ to WX │ - └───────┘ └────────┘ -``` - ---- - -## Page Stack Visualization - -### Session Trace - -``` -INITIALIZATION: -┌────────────────────────────────────────────┐ -│ Stack: [] │ -│ (Nothing loaded yet) │ -└────────────────────────────────────────────┘ - │ - ▼ main.onLoad() -┌────────────────────────────────────────────┐ -│ Stack: [] │ -│ ViewManager + pages registered in registry │ -└────────────────────────────────────────────┘ - │ - ▼ PageLoading.start() → ViewManager.open('PageHome') -┌────────────────────────────────────────────┐ -│ Stack: [PageHome] │ -│ PageHome._doShow() called │ -└────────────────────────────────────────────┘ - -USER CLICKS "START GAME": - ▼ PageHome._onStartGameClick() → ViewManager.open('PageLevel') -┌────────────────────────────────────────────┐ -│ Stack: [PageHome, PageLevel] │ -│ PageHome._doHide() called │ -│ PageLevel._doShow() called │ -└────────────────────────────────────────────┘ - -COMPLETES LEVEL: - ▼ PageLevel shows PassModal (not in stack, drawn on top) -┌────────────────────────────────────────────┐ -│ Stack: [PageHome, PageLevel] │ -│ UI Layer: [PageLevel, PassModal@z999] │ -│ PassModal shown with instantiate() │ -└────────────────────────────────────────────┘ - -CLICKS "NEXT LEVEL": - ▼ PassModal closed, PageLevel.nextLevel() -┌────────────────────────────────────────────┐ -│ Stack: [PageHome, PageLevel] │ -│ PageLevel loads next level config │ -│ PassModal destroyed │ -└────────────────────────────────────────────┘ - -CLICKS BACK FROM PageLevel: - ▼ PageLevel.onIconSettingClick() → ViewManager.back() -┌────────────────────────────────────────────┐ -│ Stack: [PageHome] │ -│ PageLevel._doHide() called │ -│ PageLevel destroyed (cache=true, not kept) │ -│ PageHome._doShow() called │ -└────────────────────────────────────────────┘ -``` - ---- - -## Component Communication Diagram - -``` - ┌─────────────────────┐ - │ ViewManager │ - │ (Singleton) │ - └──────────┬──────────┘ - │ - ┌──────────────┼──────────────┐ - │ │ │ - ▼ ▼ ▼ - open() back() replace() - │ │ │ - │ │ │ - ┌───────────┴────┐ ┌───┴────┐ ┌──┴───────┐ - │ │ │ │ │ │ - ▼ ▼ ▼ ▼ ▼ ▼ -PageHome PageLevel PassModal (back) (new page) - │ │ │ - │ ├─ Timer ────┐ - │ │ │ - │ ├─ Points │ - │ │ ↑ │ - │ │ │ │ - ▼ ▼ │ ▼ -ToastMgr Hints │ PassModal - │ │ │ - ├──────┤ └──▶ Share to WX - │ │ - Unlock Earn Points - Consume (UserAssets) - (check hasPoints) -``` - ---- - -## Data Flow: Level Completion - -``` -USER ENTERS ANSWER: -┌─────────────────────────────────────────────────────────┐ -│ PageLevel.onSubmitAnswer() │ -└─────────────────────────────────────────────────────────┘ - │ - ├─ getAnswer() ──▶ [Retrieve from EditBox] - │ - ├─ Compare with _currentConfig.answer - │ - ▼ - ┌────────────────────────────────────────┐ - │ CORRECT ANSWER │ - └────────────────────────────────────────┘ - │ - ├─ _isTransitioning = true [Prevent double-submit] - │ - ├─ stopCountdown() - │ - ├─ playSuccessSound() - │ - ├─ Calculate: timeSpent = 60 - _countdown - │ - ├─┐ Normal Mode: - │ │ - │ └─▶ UserAssetsManager.earnPoint(levelId, timeSpent) - │ │ - │ ▼ - │ └─ UPDATE SERVER + LOCAL - │ └─ Get points amount - │ └─ PageLevel.updatePointsLabel() - │ - ├─┐ Share Mode: - │ │ - │ └─▶ ShareManager.reportLevelProgress(...) - │ │ - │ ▼ - │ └─ SEND TO SERVER (fire-and-forget) - │ - ├─ _showPassModal() - │ │ - │ ├─ instantiate(passModalPrefab) - │ │ - │ ├─ Set z-index = 999 (topmost) - │ │ - │ ├─ Add to Canvas - │ │ - │ └─ Call passModal.onViewLoad() + onViewShow() - │ - └─ WAIT FOR USER: - │ - ├─ [Next Level] ──▶ PassModal closed - │ │ - │ ▼ - │ nextLevel() - │ │ - │ ┌────────┴────────┐ - │ │ No More Levels │ Next Level - │ ▼ ▼ - │ BACK HOME Load & Display - │ Restart Timer - │ - └─ [Share] ──▶ WxSDK.shareAppMessage() - │ - └─ Modal stays open -``` - ---- - -## Hint Unlocking Flow - -``` -USER CLICKS UNLOCK BUTTON: -┌──────────────────────────────────────────────┐ -│ PageLevel.onUnlockClue(index: 2 or 3) │ -└──────────────────────────────────────────────┘ - │ - ├─ Check: _isUnlocking == true? - │ └─ YES: Return immediately (prevent double-click) - │ └─ NO: Continue - │ - ├─ Check: StorageManager.hasPoints()? - │ └─ NO: Show toast "积分不足!" - │ └─ YES: Continue - │ - ├─ Set: _isUnlocking = true - │ - ├─ await UserAssetsManager.consumePoint(levelId, index) - │ │ - │ ├─ ASYNC: Contact server/storage - │ │ - │ └─ Returns: success boolean - │ - ├─ If success: - │ │ - │ ├─ updatePointsLabel() - │ │ - │ ├─ playClickSound() - │ │ - │ ├─ hideUnlockButton(index) - │ │ - │ ├─ showClue(index) - │ │ - │ └─ setClue(index, config.clueN) - │ - ├─ If failed: - │ │ - │ └─ Show toast "积分不足!" - │ - └─ Finally: _isUnlocking = false -``` - ---- - -## Timer Sequence Diagram - -``` -Timeline: 1s 2s 3s ... 59s 60s 61s - │ │ │ │ │ │ - ▼ ▼ ▼ ▼ ▼ ▼ -countdown: 60 59 58 1 0 (ended) - │ │ │ │ │ -display: "60s" "59s" "58s" ......... "1s" "0s" - │ │ │ │ │ - ▼ ▼ ▼ ▼ ▼ - -LEVEL LOAD: - │ - └─ startCountdown() - │ - ├─ _countdown = 60 - ├─ _isTimeUp = false - └─ schedule(onCountdownTick, 1.0) [Repeat every 1 sec] - │ - ▼ (Every 1 second) - onCountdownTick() - │ - ├─ if (_isTimeUp) return - │ - ├─ _countdown-- (60→59→58...→0) - │ - ├─ updateClockLabel() [Display: "59s"] - │ - └─ if (_countdown <= 0): - │ - ├─ _isTimeUp = true - │ - ├─ stopCountdown() - │ └─ unschedule(onCountdownTick) - │ - └─ onTimeUp() - │ - └─ playFailSound() - -ANSWER SUBMITTED (Correct): - │ - └─ showSuccess() - │ - ├─ stopCountdown() - │ └─ Timer stops, no more ticks - │ - └─ _showPassModal() - -OR: - -ANSWER SUBMITTED (Wrong): - │ - └─ Timer continues running... - │ - └─ User keeps playing -``` - ---- - -## Input Box Creation & Management - -``` -LEVEL LOADS: - │ - └─ _applyLevelConfig(config) - │ - └─ createSingleInput(config.answer.length) - │ - ├─ clearInputNodes() - │ │ - │ └─ Destroy old input nodes - │ (If level changed) - │ - ├─ Hide inputTemplate - │ - ├─ instantiate(inputTemplate) - │ - ├─ Set properties: - │ │ - │ ├─ active = true - │ ├─ name = 'singleInput' - │ ├─ position = (0, 0, 0) - │ │ - │ └─ Get EditBox component - │ │ - │ ├─ placeholder = "(Xlength个字)" - │ ├─ maxLength = answerLength - │ ├─ string = "" - │ │ - │ └─ Listen events: - │ ├─ TEXT_CHANGED → onInputTextChanged() - │ └─ EDITING_DID_ENDED → onInputEditingEnded() - │ - ├─ Calculate width: - │ └─ Math.min(600, Math.max(200, length*60+40)) - │ - ├─ Set UITransform contentSize to width x 100 - │ - ├─ Adjust underline width to match - │ - ├─ Add to inputLayout - │ - └─ Store in _inputNodes[0] - -USER ENTERS TEXT: - │ - ├─ EditBox updates string property - └─ TEXT_CHANGED event fires - -USER SUBMITS: - │ - ├─ getAnswer() - │ │ - │ └─ _inputNodes[0].getComponent(EditBox).string.trim() - │ - └─ Compare with config.answer - -USER NAVIGATES AWAY: - │ - └─ onViewDestroy() → clearInputNodes() - │ - └─ Destroy all input nodes -``` - ---- - -## Share Challenge Mode Flow - -``` -LAUNCH FROM WeChat LINK: - │ - └─ PageLoading.start() - │ - └─ _startPreload() - │ - ├─ Initialize auth + levels (parallel) - │ - ├─ WxSDK.getShareCodeFromLaunch() - │ │ - │ └─ Extract from wx.getLaunchOptionsSync().query - │ - ├─ if shareCode exists AND loginSuccess: - │ │ - │ └─ ShareManager.instance.joinShare(shareCode) - │ │ - │ ├─ ASYNC: Fetch share session from server - │ │ - │ ├─ Get shared level set - │ │ - │ └─ Return: success boolean - │ - ├─ if (joinSuccess): - │ │ - │ ├─ Set progress = 1 - │ │ - │ ├─ ViewManager.open('PageLevel', { - │ │ params: { shareMode: true } - │ │ }) - │ │ - │ └─ Destroy self (PageLoading) - │ - └─ else (normal flow) - │ - └─ Preload PageHome → Open PageHome - -IN SHARE MODE (PageLevel): - │ - ├─ onViewLoad(): - │ │ - │ ├─ params = getParams() - │ ├─ _isShareMode = params?.shareMode === true - │ │ - │ ├─ if shareMode: - │ │ └─ currentLevelIndex = 0 (start at first) - │ │ - │ └─ initLevel() loads from ShareManager, not LevelDataManager - │ - ├─ showSuccess(): - │ │ - │ ├─ if normal mode: - │ │ └─ earn points - │ │ - │ └─ if share mode: - │ │ - │ └─ ShareManager.reportLevelProgress(levelId, true, timeSpent) - │ │ - │ └─ FIRE-AND-FORGET (no await) - │ - ├─ nextLevel(): - │ │ - │ ├─ if NOT shareMode: - │ │ └─ StorageManager.onLevelCompleted() [Save progress] - │ │ - │ ├─ Check level count: - │ │ │ - │ │ ├─ Share: ShareManager.getShareLevelCount() - │ │ └─ Normal: LevelDataManager.getLevelCount() - │ │ - │ ├─ if (allLevelsComplete): - │ │ │ - │ │ ├─ if shareMode: - │ │ │ │ - │ │ │ ├─ ShareManager.clearShareMode() - │ │ │ │ - │ │ │ └─ ViewManager.replace('PageHome') - │ │ │ - │ │ └─ if normal: - │ │ └─ ViewManager.back() - │ │ - │ └─ else (more levels): - │ └─ Load next level - │ - └─ Back to PageHome (replaces, clears share state) -``` - ---- - -## ViewManager Caching Mechanism - -``` -OPEN 'PageHome' (first time): - │ - └─ ViewManager.open('PageHome') - │ - ├─ Check cache: _viewCache.get('PageHome') - │ └─ Cache miss (undefined) - │ - ├─ _instantiateView('PageHome', prefab) - │ │ - │ ├─ instantiate(prefab) - │ │ - │ ├─ Get BaseView component - │ │ - │ ├─ Set viewId, config, params - │ │ - │ ├─ Add to container - │ │ - │ ├─ Call view.onViewLoad() ◄─── Called once - │ │ - │ ├─ if (config.cache === true): ◄─── PageHome has cache:true - │ │ └─ _viewCache.set('PageHome', view) - │ │ - │ └─ _showView(view) - │ │ - │ └─ view._doShow() - │ └─ view.onViewShow() ◄─── Called now - │ - └─ Stack: [PageHome] - -OPEN 'PageLevel': - │ - └─ ViewManager.open('PageLevel') - │ - ├─ PageHome._doHide() - │ └─ view.onViewHide() ◄─── First hide - │ - └─ (Same as above for PageLevel) - │ - └─ Stack: [PageHome, PageLevel] - -BACK FROM 'PageLevel': - │ - └─ ViewManager.back() - │ - ├─ Pop stack: PageLevel - │ - ├─ PageLevel._doHide() - │ └─ view.onViewHide() - │ - ├─ Decide: shouldDestroy? - │ │ - │ └─ PageLevel has cache:true - │ └─ shouldDestroy = false (keep cached) - │ - ├─ Get current: PageHome - │ - ├─ PageHome._doShow() - │ └─ view.onViewShow() ◄─── Called again - │ - └─ Stack: [PageHome] - Caches: {PageHome, PageLevel} - -OPEN 'PageLevel' AGAIN (from cache): - │ - └─ ViewManager.open('PageLevel') - │ - ├─ Check cache: _viewCache.get('PageLevel') - │ └─ Cache hit! (PageLevel instance) - │ - ├─ _showView(cachedView) - │ │ - │ ├─ PageHome._doHide() - │ │ - │ └─ cachedView._doShow() - │ └─ view.onViewShow() ◄─── Called again (2nd time) - │ onViewLoad() NOT called again ◄─── Important! - │ - └─ Stack: [PageHome, PageLevel] - Same instance reused -``` - ---- - -## Error Handling Paths - -``` -LEVEL LOADING ERROR: - │ - ├─ initLevel() - │ │ - │ └─ await LevelDataManager.ensureLevelReady(index) - │ │ - │ └─ Returns null (error) - │ - └─ Log warning, return early - │ - └─ UI stays as-is (might be blank) - -INSUFFICIENT POINTS: - │ - └─ onUnlockClue() - │ - ├─ StorageManager.hasPoints() → false - │ - └─ ToastManager.show("积分不足!") - │ - └─ User sees notification, unlock blocked - -DOUBLE-SUBMIT PREVENTION: - │ - ├─ onSubmitAnswer() - │ │ - │ └─ if (_isTransitioning) return ◄─── Blocks multiple calls - │ - └─ showSuccess() - │ - └─ _isTransitioning = true ◄─── Set to block - │ - └─ After modal shown/closed - └─ (Would be reset on nextLevel) - -DOUBLE-CLICK UNLOCK: - │ - └─ onUnlockClue() - │ - └─ if (_isUnlocking) return ◄─── Blocks rapid clicks - │ - └─ _isUnlocking = true at start - └─ Finally: _isUnlocking = false ◄─── Reset after async -``` - diff --git a/DOCS_INDEX.md b/DOCS_INDEX.md deleted file mode 100644 index e95a4ee..0000000 --- a/DOCS_INDEX.md +++ /dev/null @@ -1,288 +0,0 @@ -# Documentation Index - -This directory contains comprehensive analysis of the mp-xieyingeng (写英语) Cocos Creator game point/score system. - -## 📚 Documentation Files - -### 1. **GAME_ANALYSIS.md** (19 KB) - COMPREHENSIVE ANALYSIS -The complete technical analysis of the game's point and score system. **Start here for complete understanding.** - -**Contents:** -- PART 1: Points/Asset System (Lives currency) -- PART 2: Level Completion & Rewards (+1 life per pass) -- PART 3: Hint/Clue System (Costs 1 life each) -- PART 4: Game Play Logic (Answer validation, timing) -- PART 5: Loading Page Logic (Initialization flow) -- PART 6: API & Network Requests (Backend endpoint) -- PART 7: User Data Storage (localStorage schema) -- PART 8: WeChat Mini-Game SDK Usage -- PART 9: Complete Game Flow Diagram -- PART 10: Complete Resource Flow -- PART 11: Key Observations & Implementation Gaps -- PART 12: Network Architecture -- Summary table of all features - -### 2. **QUICK_REFERENCE.md** (8.6 KB) - QUICK LOOKUP GUIDE -Fast reference guide for developers. Use this for quick lookups. - -**Contents:** -- Core Currency Overview (Lives system) -- Flow Charts (Life economics, answer submission, app startup) -- Data Structures (localStorage, API response) -- Key Functions & Methods -- Missing Features & Implementation Gaps -- Network Calls Summary -- Game Loop Per Level -- Data Integrity Notes -- WeChat Features Status -- Extension Ideas (Easy/Medium/Complex) - -### 3. **ARCHITECTURE.md** (26 KB) - SYSTEM DESIGN -Detailed architecture diagrams and system design documentation. - -**Contents:** -- System Architecture Diagram (layered view) -- Data Flow Diagram (complete user session) -- State Management Flow -- View Stack & Navigation -- Class Hierarchy -- Dependency Graph -- Performance Considerations -- Security Considerations & Vulnerabilities - -## 🎯 Quick Navigation - -### "I want to understand..." - -**...how the point system works** -→ Read: QUICK_REFERENCE.md "THE COMPLETE PICTURE" - -**...the complete game flow** -→ Read: GAME_ANALYSIS.md "PART 9: Complete Game Flow Diagram" - -**...how lives are stored and managed** -→ Read: GAME_ANALYSIS.md "PART 1: Points/Asset System" + PART 7: User Data Storage" - -**...what happens when a user completes a level** -→ Read: GAME_ANALYSIS.md "PART 2: Level Completion & Rewards" - -**...how hints work** -→ Read: GAME_ANALYSIS.md "PART 3: Hint/Clue System" - -**...the API integration** -→ Read: GAME_ANALYSIS.md "PART 6: API & Network Requests" - -**...the code organization** -→ Read: ARCHITECTURE.md "System Architecture Diagram" + "Dependency Graph" - -**...what's missing in the implementation** -→ Read: GAME_ANALYSIS.md "PART 11: Key Observations & Gaps" - -**...where to add new features** -→ Read: QUICK_REFERENCE.md "EXTENSION IDEAS" - ---- - -## 🔑 Key Findings Summary - -### THE POINT SYSTEM -- **Currency**: Lives (生命值) -- **Default**: 10 lives -- **Earn**: +1 per level pass -- **Spend**: -1 per hint unlock (Hint 2 or 3 only) -- **Storage**: localStorage under key `game_lives` -- **No other currency**: No points, coins, or score counter - -### LEVEL COMPLETION -- **Reward**: +1 life (only reward) -- **No time bonus**: Same reward regardless of speed -- **No partial credit**: Exact case-sensitive match required -- **Unlimited retries**: Can retry wrong answers indefinitely -- **Timeout incomplete**: 60s countdown exists but doesn't prevent submission - -### HINT SYSTEM -- **Hint 1**: Free (always shown) -- **Hint 2**: Costs 1 life (unlock button) -- **Hint 3**: Costs 1 life (unlock button) -- **Max loss per level**: 2 lives (both hints) -- **Net per level**: -1 to +1 depending on hints used - -### DATA STORAGE -- **Lives**: localStorage["game_lives"] -- **Progress**: localStorage["game_progress"] with currentLevelIndex & maxUnlockedLevelIndex -- **All local**: No server-side sync -- **No encryption**: Direct access via console -- **Immediate writes**: Each update written to storage immediately - -### API INTEGRATION -- **Single endpoint**: GET https://ilookai.cn/api/v1/wechat-game/levels -- **Startup only**: Called once during initialization -- **Retry**: 2 attempts with 1s delay -- **Timeout**: 8 seconds -- **No other backend calls**: No score submission, no analytics, no leaderboard - -### WECHAT FEATURES -- ✅ Sharing (with level parameter) -- ✅ Haptic feedback (vibration on errors) -- ❌ No authentication -- ❌ No cloud save -- ❌ No leaderboard - ---- - -## 📁 Source Files Referenced - -All 16 TypeScript files in the project are analyzed: - -**Core Pages:** -- `PageLoading.ts` - Loading screen & initialization -- `PageHome.ts` - Home menu page -- `PageLevel.ts` - Main game level (where all game logic happens) -- `PassModal.ts` - Level completion modal - -**Management Systems:** -- `ViewManager.ts` - Page navigation & lifecycle -- `StorageManager.ts` - Lives & progress persistence -- `LevelDataManager.ts` - API integration & asset loading - -**Utilities:** -- `BaseView.ts` - Base class for pages -- `HttpUtil.ts` - HTTP request wrapper -- `WxSDK.ts` - WeChat SDK integration -- `ToastManager.ts` - Toast notifications -- `Toast.ts` - Toast component -- `LevelTypes.ts` - TypeScript interfaces -- `RoundedRectMask.ts` - UI utility -- `BackgroundScaler.ts` - UI utility -- `main.ts` - App entry point - ---- - -## 🔬 Analysis Methodology - -This documentation was created by: -1. Finding all 16 TypeScript files in the assets/ directory -2. Reading and analyzing each file for: - - Score/points logic - - Currency/asset management - - Level completion mechanics - - Hint/cost systems - - API calls - - Storage mechanisms - - WeChat SDK usage - - Data flows -3. Mapping dependencies between files -4. Creating flowcharts and diagrams -5. Documenting observations and gaps - ---- - -## 🚀 Using This Documentation - -### For Understanding the System -1. Start with QUICK_REFERENCE.md for overview -2. Read GAME_ANALYSIS.md for detailed understanding -3. Refer to ARCHITECTURE.md for system design - -### For Making Changes -1. Check ARCHITECTURE.md "Dependency Graph" -2. Review relevant code sections in GAME_ANALYSIS.md -3. Use QUICK_REFERENCE.md to find specific methods - -### For Adding Features -1. Review QUICK_REFERENCE.md "EXTENSION IDEAS" -2. Check ARCHITECTURE.md "Security Considerations" -3. Plan changes against current dependencies - -### For Debugging -1. Review GAME_ANALYSIS.md "PART 9: Game Flow Diagram" -2. Check ARCHITECTURE.md "Data Flow Diagram" -3. Trace through StorageManager and LevelDataManager - ---- - -## ⚠️ Important Notes - -### Security Issues -- ⚠️ All data stored locally without encryption -- ⚠️ No server-side validation of progress -- ⚠️ Users can modify localStorage directly -- ⚠️ Can skip levels by editing progress - -### Implementation Gaps -- ❌ No points/coins display -- ❌ No time-based bonuses -- ❌ Timeout doesn't prevent submission -- ❌ No server-side progress sync -- ❌ No analytics tracking - -### To Improve -- Add server-side progress validation -- Implement user authentication -- Add score API endpoint -- Track time-to-completion -- Consider leaderboard system - ---- - -## 📝 Document Versions - -- Created: April 5, 2026 -- Cocos Creator Version: 3.8.8 -- Project: mp-xieyingeng (写英语) -- Platform: WeChat Mini-Game -- Analysis Coverage: 100% of TypeScript codebase (16 files) - ---- - -## 📞 Questions Answered - -This documentation answers: -- ✅ What is the points/score system? -- ✅ How do users earn points? -- ✅ How are points spent? -- ✅ What happens on level completion? -- ✅ How do hints work and cost lives? -- ✅ Where is data stored? -- ✅ What API calls are made? -- ✅ How does WeChat integration work? -- ✅ What's the complete game flow? -- ✅ What features are missing? -- ✅ What's the system architecture? -- ✅ Where are the security issues? - ---- - -## 🔗 Cross-References - -| Topic | Main Document | Quick Ref | Architecture | -|-------|---------------|-----------|--------------| -| Lives System | PART 1 | Overview | State Mgmt | -| Level Rewards | PART 2 | Economics | Data Flow | -| Hints & Costs | PART 3 | Game Loop | Dependencies | -| API | PART 6 | Network | External | -| Storage | PART 7 | Data Structures | State Mgmt | -| WeChat | PART 8 | Features | Dependencies | -| Game Flow | PART 9 | Game Loop | Data Flow | -| Features | PART 11 | Missing | Performance | - ---- - -## 📊 Statistics - -- **TypeScript Files Analyzed**: 16 -- **Lines of Code Reviewed**: ~1,800 -- **API Endpoints**: 1 -- **Storage Keys**: 2 -- **External SDKs**: 1 (WeChat) -- **Currency Types**: 1 (Lives) -- **Hint Levels**: 3 -- **Levels Supported**: 100+ (from API) -- **Player Lives**: 10 (default) -- **Time Per Level**: 60 seconds -- **Max Hint Cost Per Level**: 2 lives -- **Max Life Gain Per Level**: 1 life - ---- - -**For questions or clarifications, refer to the specific document sections listed above.** diff --git a/GAME_ANALYSIS.md b/GAME_ANALYSIS.md deleted file mode 100644 index 3409ead..0000000 --- a/GAME_ANALYSIS.md +++ /dev/null @@ -1,504 +0,0 @@ -# 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`) -- 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=` - -### 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(url: string, timeout: number = 10000): Promise - -// POST request -HttpUtil.post(url: string, data: object, timeout: number = 10000): Promise -``` - -### 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 - diff --git a/POINTS_FLOW_DIAGRAM.md b/POINTS_FLOW_DIAGRAM.md deleted file mode 100644 index 39f838c..0000000 --- a/POINTS_FLOW_DIAGRAM.md +++ /dev/null @@ -1,494 +0,0 @@ -# Complete Points/Lives Flow Diagram - -## 🔴 INITIALIZATION PHASE - -``` -┌─────────────────────────────────────┐ -│ GAME STARTS │ -│ PageLoading initializes │ -└──────────────┬──────────────────────┘ - │ - ↓ - ┌──────────────────────────┐ - │ Check localStorage │ - │ "game_lives" key │ - └────┬──────────────┬──────┘ - │ │ - NOT SET EXISTS - (New User) (Returning) - │ │ - ↓ ↓ - ┌────────────┐ ┌────────────┐ - │ 10 Lives │ │Parse Value │ - │(DEFAULT) │ │from Storage│ - └────┬───────┘ └────┬───────┘ - │ │ - └────────┬───────┘ - ↓ - ┌────────────────────────┐ - │ LOAD LEVEL DATA │ - │ (API fetch) │ - │ https://ilookai.cn ... │ - └────────┬───────────────┘ - │ - ↓ - ┌────────────────────────┐ - │ Display PageHome │ - │ (with shared config) │ - └────────┬───────────────┘ - │ - ↓ - ┌────────────────────────┐ - │ READY TO PLAY │ - │ Lives: X | Level: Y │ - └────────────────────────┘ -``` - ---- - -## 🎮 GAMEPLAY PHASE (Single Level) - -``` -┌────────────────────────────────────────┐ -│ LEVEL STARTS │ -│ Load level data from cache │ -│ Display image + Clue 1 (FREE) │ -│ Start 60-second countdown │ -└──────────────┬─────────────────────────┘ - │ - ├─────────────────────────────────────┐ - │ │ - ↓ ↓ - ┌──────────────────┐ ┌──────────────────────┐ - │ UNLOCK CLUE 2 │ │ SUBMIT ANSWER │ - │ onUnlockClue(2) │ │ onSubmitAnswer() │ - │ │ │ │ - │ Check: hasLives()├──NO──→ │ Compare: │ - │ │ │ │ input === │ - │ YES │ │ correctAnswer │ - │ │ │ │ │ - │ ↓ │ │ ├──YES──→┐ │ - │ consumeLife() │ │ │ │ │ - │ Lives: -1 │ │ │ │ │ - │ │ │ │ │ │ │ - │ ↓ │ │ │ ┌┴─────────┐ - │ Display Clue 2 │ │ │ │ │ - │ Refresh Label │ │ │ ↓ │ - │ │ │ │ │ SUCCESS! │ - │ ↓ │ │ │ showSuccess() │ - │ Continue Game │ │ │ │ │ - └──────────────────┘ │ │ ↓ │ - │ │ │ Play success │ - │ │ │ sound │ - ↓ │ │ Stop timer │ - ┌──────────────────┐ │ │ addLife() │ - │ UNLOCK CLUE 3 │ │ │ Lives: +1 │ - │ onUnlockClue(3) │ │ │ │ │ - │ │ │ │ ↓ │ - │ Check: hasLives()├──NO──→ │ │ Show PassModal │ - │ │ │ │ │ │ │ - │ YES │ │ │ ├─►[NEXT] │ - │ │ │ │ │ │ │ - │ ↓ │ │ │ └─►[SHARE] │ - │ consumeLife() │ │ │ │ - │ Lives: -1 │ │ └──NO──────────────┘ - │ │ │ │ │ - │ ↓ │ │ ↓ - │ Display Clue 3 │ │ ERROR! - │ Refresh Label │ │ showError() - │ │ │ │ │ - │ ↓ │ │ ↓ - │ Continue Game │ │ Play fail sound - │ │ │ Vibrate long - │ │ │ Show toast: - │ │ │ "答案错误, - └──────────────────┘ │ 再试试吧!" - │ │ Lives unchanged - │ │ │ - │ │ ↓ - │ │ Can retry - │ │ (same level) - │ │ - └──────────────────────┘ - │ - ↓ - ┌──────────────────────┐ - │ TIME UP? │ - └──────────────────────┘ - │ - ↓ - ┌─────────────────────┐ - │ Play fail sound │ - │ Stop countdown │ - │ Can still retry │ - │ Lives unchanged │ - └─────────────────────┘ -``` - ---- - -## 📊 LIVES TRANSACTION FLOW - -``` - LEVEL START - │ - ┌────────┴─────────┐ - │ │ - OPTIONAL MANDATORY - (Hints) (Level) - │ │ - ┌───────┴────────┐ │ - │ │ │ - HINT 2 HINT 3 SUBMISSION - -1 Life -1 Life │ - │ │ │ - └────────┬───────┴─┬───────┤ - │ │ │ - (Spent) (Spent) ANSWER - │ │ │ - └────┬────┴───┬───┘ - │ │ - 0-2 LIVES SPENT - (Depends on choices) - │ │ - ┌─────────┼────────┼─────────┐ - │ │ │ │ - CORRECT WRONG TIMEOUT ... - │ │ │ - │ │ │ - +1 LIFE 0 LIVES 0 LIVES - │ │ │ - ↓ ↓ ↓ - ┌───────────────────────────────┐ - │ NEW LIVES BALANCE │ - │ │ - │ Formula: │ - │ newLives = │ - │ oldLives │ - │ - (hintsUnlocked × 1) │ - │ + (if levelWon ? 1 : 0) │ - │ │ - │ Examples: │ - │ 10 - 0 + 1 = 11 (no hints) │ - │ 10 - 1 + 1 = 10 (1 hint) │ - │ 10 - 2 + 1 = 9 (2 hints) │ - │ 10 - 0 + 0 = 10 (retry) │ - └───────────────────────────────┘ -``` - ---- - -## 🏆 LEVEL PROGRESSION & PERSISTENCE - -``` - LEVEL WON - │ - ↓ - ┌────────────────────────────┐ - │ StorageManager. │ - │ onLevelCompleted() │ - │ │ - │ Takes: levelIndex (0-based) │ - └────────────┬────────────────┘ - │ - ┌────────┴────────┐ - │ │ - ↓ ↓ - ┌────────────┐ ┌──────────────────┐ - │ currentLevel │ maxUnlocked │ - │ = N + 1 │ = max(N, current)│ - └────────────┘ └──────────────────┘ - │ │ - └────────┬────────┘ - │ - ↓ - ┌──────────────────────────────┐ - │ Save to localStorage: │ - │ "game_progress" = │ - │ { │ - │ currentLevelIndex: N+1, │ - │ maxUnlockedLevelIndex: N │ - │ } │ - └────────┬─────────────────────┘ - │ - ↓ - ┌──────────────────────────────┐ - │ NEXT GAME SESSION: │ - │ Restore from localStorage │ - │ Resume at Level N+1 │ - │ Can replay Levels 0 to N │ - └──────────────────────────────┘ -``` - ---- - -## 💾 DATA PERSISTENCE - -``` -┌─────────────────────────────────────────┐ -│ localStorage (Browser) │ -├─────────────────────────────────────────┤ -│ │ -│ KEY: "game_lives" │ -│ VALUE: "10" (string number) │ -│ Type: String │ -│ Managed by: StorageManager │ -│ Accessed by: PageLevel, PassModal │ -│ │ -├─────────────────────────────────────────┤ -│ │ -│ KEY: "game_progress" │ -│ VALUE: JSON string │ -│ { │ -│ "currentLevelIndex": 2, │ -│ "maxUnlockedLevelIndex": 4 │ -│ } │ -│ Type: String (JSON) │ -│ Managed by: StorageManager │ -│ Accessed by: PageLevel, ViewManager │ -│ │ -└─────────────────────────────────────────┘ -``` - ---- - -## 🌐 API & LEVEL DATA FLOW - -``` -┌──────────────────────────────────────────┐ -│ Remote API Server │ -│ https://ilookai.cn/api/v1/... │ -│ │ -│ Returns: Array │ -│ Fields: id, level, imageUrl, hint1-3, │ -│ answer, sortOrder │ -└────────────────┬──────────────────────────┘ - │ - ↓ (1st app load only) - HttpUtil.get() - │ - ↓ - ┌──────────────────────────┐ - │ LevelDataManager │ - │ _fetchApiData() │ - │ │ - │ Retry: 2 attempts │ - │ Timeout: 8000ms │ - └────────┬─────────────────┘ - │ - ┌────┴────┐ - │ │ - FAIL SUCCESS - │ │ - │ ↓ - │ ┌──────────────────┐ - │ │ Cache all level │ - │ │ metadata in │ - │ │ memory │ - │ │ (_apiData) │ - │ └────┬─────────────┘ - │ │ - │ ↓ - │ ┌──────────────────┐ - │ │ Preload Level 0 │ - │ │ image │ - │ │ (_levelConfigs) │ - │ └────┬─────────────┘ - │ │ - └────┬────┘ - │ - ↓ - ┌──────────────────────────┐ - │ PageLevel ready │ - │ Display first level │ - └──────────────────────────┘ -``` - ---- - -## ⏰ COUNTDOWN TIMER FLOW - -``` -┌──────────────────┐ -│ startCountdown() │ -│ _countdown = 60 │ -│ _isTimeUp = false│ -└────────┬─────────┘ - │ - ↓ - ┌─────────────────────────┐ - │ schedule( │ - │ onCountdownTick, 1 │ ← Every 1 second - │ ) │ - └────────┬────────────────┘ - │ - ├─────────────────────────────┐ - │ EVERY SECOND │ - │ (if _isTimeUp = false) │ - │ │ - ├─→ _countdown-- │ - ├─→ updateClockLabel() │ - │ (display "59s", "58s"...) │ - │ │ - └────────┬────────────────────┘ - │ - ├─────────────────────────┐ - │ │ - _countdown > 0 _countdown <= 0 - │ │ - │ ↓ - │ _isTimeUp = true - │ stopCountdown() - │ onTimeUp() - │ playFailSound() - │ │ - │ ↓ - │ Can still submit! - │ Lives unchanged - │ │ - └──────────┬──────────────┘ - │ - ↓ - ┌────────────────┐ - │ PLAYER ACTION: │ - │ Submit Answer │ - └────────────────┘ -``` - ---- - -## 📱 WECHAT INTEGRATION POINTS - -``` -┌──────────────────────────────────────┐ -│ GAME START (PageHome) │ -├──────────────────────────────────────┤ -│ WxSDK.initShare({ │ -│ title: "写英语", │ -│ query: "" │ -│ }) │ -│ └─► Enable share menu in header │ -└──────────────────────────────────────┘ - -┌──────────────────────────────────────┐ -│ LEVEL WON (PassModal) │ -├──────────────────────────────────────┤ -│ User clicks "Share" │ -│ WxSDK.shareAppMessage({ │ -│ title: "快来一起玩...", │ -│ query: "level=" │ -│ }) │ -│ └─► Opens share dialog │ -│ with level param │ -└──────────────────────────────────────┘ - -┌──────────────────────────────────────┐ -│ WRONG ANSWER (PageLevel) │ -├──────────────────────────────────────┤ -│ showError() │ -│ WxSDK.vibrateLong() │ -│ └─► 400ms vibration feedback │ -└──────────────────────────────────────┘ -``` - ---- - -## 🔄 Complete Player Journey - -``` -START - ↓ -┌─────────────────────┐ -│ Check localStorage │ -│ game_lives: 10 │ -│ game_progress: {0,0}│ -└──────────┬──────────┘ - ↓ - ┌─────────────────┐ - │ PageHome │ - │ Start Button │ - └──────────┬──────┘ - ↓ - ┌──────────────────────────┐ - │ PageLevel: LEVEL 1 │ - │ Lives: 10 │ Answer: __ │ - │ Clue 1: ✓ (FREE) │ - │ Clue 2: Unlock? (-1 life)│ - │ Clue 3: Unlock? (-1 life)│ - │ ⏱️ 60s countdown... │ - └──────────┬───────────────┘ - │ - ┌──────┴──────┬────────────────┐ - │ │ │ - UNLOCK SUBMIT TIMEOUT - CLUE 2 ANSWER (still ok) - -1 Life │ - │ ├─ CORRECT: +1 Life - │ │ ↓ - │ │ PassModal - │ │ ├─ NEXT → Level 2 - │ │ └─ SHARE → wx.share - │ │ - │ └─ WRONG: Lives stay - │ ↓ - │ Retry same level - │ - └─ UNLOCK - CLUE 3 - -1 Life - │ - ↓ - (Try answer) - │ - ├─ CORRECT: +1 Life - │ ↓ - │ PassModal - │ └─ NEXT → Level 2 - │ - └─ WRONG: Lives stay - ↓ - Retry - -PROGRESSION CONTINUES... - -BEAT ALL LEVELS - ↓ - Return to PageHome - (Save progress in localStorage) - ↓ - COMPLETE -``` - ---- - -## 📈 Lives Over Time (Example Scenario) - -``` -Session 1: No hints used -━━━━━━━━━━━━━━━━━━━━━ -Level 1: 10 Lives → Correct → 11 Lives -Level 2: 11 Lives → Correct → 12 Lives -Level 3: 12 Lives → Correct → 13 Lives -Session 1 ends: 13 Lives saved - -Session 2: Some hints used -━━━━━━━━━━━━━━━━━━━━━ -Start: 13 Lives -Level 4: 13 - 1 (hint) + 1 (win) = 13 Lives -Level 5: 13 - 2 (hints) + 1 (win) = 12 Lives -Level 6: 12 - 0 (no hints) + 1 (win) = 13 Lives -Level 7: 13 - 0 (no hints) + 1 (win) = 14 Lives -Session 2 ends: 14 Lives saved - -Session 3: Struggling with hints -━━━━━━━━━━━━━━━━━━━━━ -Start: 14 Lives -Level 8: 14 - 2 (hints) + 1 (win) = 13 Lives -Level 9: 13 - 2 (hints) + 1 (win) = 12 Lives -Level 10: 12 - 0 (retry, no hints) + 1 (eventually win) = 13 Lives -Session 3 ends: 13 Lives saved - -PATTERN: Lives stay stable or grow over time - depending on hint usage -``` - diff --git a/POINTS_SYSTEM_INDEX.md b/POINTS_SYSTEM_INDEX.md deleted file mode 100644 index 23f3b98..0000000 --- a/POINTS_SYSTEM_INDEX.md +++ /dev/null @@ -1,336 +0,0 @@ -# 📚 Game Points/Lives System - Documentation Index - -> Complete analysis of the Cocos Creator WeChat Mini-Game points/score/lives system - -**Generated:** April 5, 2026 -**Scope:** All 21 TypeScript source files analyzed -**Total Documentation:** ~45KB - ---- - -## 📄 Documentation Files - -### 1. **SUMMARY.md** (11 KB) ⭐ **START HERE** -**Best for:** High-level overview and executive understanding - -**Contains:** -- Executive summary of the lives-based economy -- 21 files analyzed (categorized) -- Complete points/lives flow table -- Data persistence details -- Level progression mechanics -- Hint system mechanics -- Gameplay loop description -- API integration overview -- WeChat integration features -- User journey example -- Testing scenarios -- Business logic highlights - -**Read this if:** You want a quick understanding of how the entire system works - ---- - -### 2. **GAME_ANALYSIS.md** (13 KB) -**Best for:** Deep technical analysis of every system component - -**Contains 17 Sections:** -1. Project Overview -2. Lives/Resource System (complete) -3. Level Progression System -4. Game Level Data & API -5. Gameplay Loop -6. Winning & Rewards -7. Losing & Consequences -8. Hint/Clue System -9. Network & API Communication -10. WeChat SDK Integration -11. Game State Management -12. Loading Page Logic -13. Complete Points Flow Diagram -14. Key Files Summary (table) -15. Important Constants -16. Missing Features -17. Data Flow Summary & Business Logic - -**Read this if:** You need comprehensive technical details on every aspect - ---- - -### 3. **QUICK_REFERENCE.md** (5.6 KB) -**Best for:** Quick lookup while coding - -**Contains:** -- What is the currency? (Lives) -- Lives management methods -- How lives are spent (table) -- How lives are earned (table) -- Level progression mechanics -- Level data structure (from API) -- Gameplay mechanics (time limit, input, conditions) -- Rewards & penalties (table) -- Economy balance formulas -- API integration details -- Storage schema -- WeChat integration points -- Key files list -- Important constants -- What's NOT implemented - -**Read this if:** You need quick facts/numbers without reading full docs - ---- - -### 4. **POINTS_FLOW_DIAGRAM.md** (22 KB) -**Best for:** Visual understanding of system flows - -**Contains ASCII Diagrams For:** -1. Initialization Phase -2. Gameplay Phase (single level) -3. Lives Transaction Flow -4. Level Progression & Persistence -5. Data Persistence (localStorage) -6. API & Level Data Flow -7. Countdown Timer Flow -8. WeChat Integration Points -9. Complete Player Journey -10. Lives Over Time (example scenario) - -**Read this if:** You prefer visual diagrams over text explanations - ---- - -### 5. **POINTS_SYSTEM_INDEX.md** (This File!) -**Best for:** Navigation and understanding what exists where - -**Contains:** -- Overview of all documentation -- Quick navigation by topic -- Search guide -- Related code file references - ---- - -## 🎯 Quick Navigation by Topic - -### I want to understand... - -**The Lives Economy** -- → SUMMARY.md → "The Complete Points/Lives Flow" -- → QUICK_REFERENCE.md → "Economy Balance" -- → GAME_ANALYSIS.md → Section 1 & 2 - -**How to Earn Lives** -- → SUMMARY.md → "EARNING Lives table" -- → QUICK_REFERENCE.md → "How Lives Are Earned" -- → POINTS_FLOW_DIAGRAM.md → "Complete Player Journey" - -**How to Spend Lives** -- → SUMMARY.md → "SPENDING Lives table" -- → QUICK_REFERENCE.md → "How Lives Are Spent" -- → GAME_ANALYSIS.md → Section 7 "Hint/Clue System" - -**Level Progression** -- → SUMMARY.md → "Level Progression System" -- → QUICK_REFERENCE.md → "Level Progression" -- → GAME_ANALYSIS.md → Section 2 -- → POINTS_FLOW_DIAGRAM.md → "Level Progression & Persistence" - -**Data Storage** -- → SUMMARY.md → "Data Persistence" -- → QUICK_REFERENCE.md → "Storage Schema" -- → GAME_ANALYSIS.md → Section 2 "Storage & Persistence" -- → POINTS_FLOW_DIAGRAM.md → "Data Persistence" - -**API Integration** -- → SUMMARY.md → "API Integration" -- → GAME_ANALYSIS.md → Section 3 & 4 -- → POINTS_FLOW_DIAGRAM.md → "API & Level Data Flow" - -**WeChat Integration** -- → SUMMARY.md → "WeChat Integration" -- → GAME_ANALYSIS.md → Section 9 -- → POINTS_FLOW_DIAGRAM.md → "WeChat Integration Points" - -**Complete Gameplay Flow** -- → SUMMARY.md → "Gameplay Loop" -- → GAME_ANALYSIS.md → Section 4 & 5 & 6 -- → POINTS_FLOW_DIAGRAM.md → "Gameplay Phase" - -**Testing/Validation** -- → SUMMARY.md → "Testing Scenarios" -- → QUICK_REFERENCE.md → Check "What's NOT implemented" section - ---- - -## 📖 Code References - -### Core Files & What They Do - -**StorageManager.ts** (Lives + Progress) -- `getLives()` / `setLives()` / `consumeLife()` / `addLife()` -- `getCurrentLevelIndex()` / `onLevelCompleted()` -- Referenced in: GAME_ANALYSIS §2, SUMMARY "Data Persistence" - -**PageLevel.ts** (Main Gameplay) -- `onUnlockClue()` / `onSubmitAnswer()` / `showSuccess()` / `showError()` -- `startCountdown()` / `onCountdownTick()` -- Referenced in: GAME_ANALYSIS §4-7, SUMMARY "Gameplay Loop" - -**LevelDataManager.ts** (API + Caching) -- `initialize()` / `ensureLevelReady()` / `preloadNextLevel()` -- Referenced in: GAME_ANALYSIS §3, SUMMARY "API Integration" - -**PassModal.ts** (Victory Screen) -- Victory UI and rewards display -- Referenced in: GAME_ANALYSIS §5 - -**WxSDK.ts** (WeChat Integration) -- `shareAppMessage()` / `vibrateLong()` / etc. -- Referenced in: GAME_ANALYSIS §9 - -**ViewManager.ts** (Page Navigation) -- Page stack management -- Referenced in: GAME_ANALYSIS §10 - ---- - -## 🔑 Key Numbers & Values - -| Item | Value | Reference | -|------|-------|-----------| -| Default Lives | 10 | QUICK_REFERENCE, SUMMARY | -| Min Lives | 0 | QUICK_REFERENCE, SUMMARY | -| Hint Cost | 1 life each | QUICK_REFERENCE, SUMMARY | -| Win Reward | 1 life | QUICK_REFERENCE, SUMMARY | -| Level Time | 60 seconds | QUICK_REFERENCE, SUMMARY | -| API Timeout | 8000ms | QUICK_REFERENCE, GAME_ANALYSIS §3 | -| API Retries | 2 attempts | QUICK_REFERENCE, GAME_ANALYSIS §3 | - ---- - -## 🧩 How Systems Interact - -``` -StorageManager (Lives + Progress) - ↓ -PageLevel (Gameplay) - ├─ Uses Lives for: Hint Unlocks - ├─ Updates Lives on: Level Complete - ├─ Uses Progress for: Level Selection - └─ Updates Progress on: Level Complete - ↓ -LevelDataManager (API + Cache) - ├─ Fetches: Level Data + Images - └─ Serves: Hints + Answers + Images to PageLevel - ↓ -WxSDK (WeChat) - ├─ Shares: Victory with Level Param - └─ Vibrates: On Error - ↓ -ViewManager (Navigation) - └─ Manages: Page Stack + State -``` - ---- - -## ✅ Document Completeness Checklist - -- [x] Lives earning mechanics -- [x] Lives spending mechanics -- [x] Level progression system -- [x] Hint/clue system -- [x] API integration -- [x] Data persistence -- [x] WeChat features -- [x] Gameplay loop -- [x] Win/lose conditions -- [x] User journey examples -- [x] Flow diagrams -- [x] Code references -- [x] Constants & values -- [x] Error handling -- [x] Performance optimizations -- [x] Missing features list -- [x] Testing scenarios - -**Coverage:** 100% of points/lives/score system - ---- - -## 🚀 Using This Documentation - -### For Understanding -1. Start with SUMMARY.md (5 min read) -2. Read QUICK_REFERENCE.md (3 min read) -3. Review POINTS_FLOW_DIAGRAM.md (5 min read) -4. Deep dive into GAME_ANALYSIS.md (15 min read) - -### For Maintenance -- QUICK_REFERENCE.md is your quick lookup -- GAME_ANALYSIS.md has implementation details -- Code references show where logic lives - -### For Modifications -- "Data Persistence" section for storage changes -- "Lives Transaction Flow" for economy tweaks -- "Constants" section for balance adjustments -- Individual sections in GAME_ANALYSIS for specific systems - ---- - -## ❓ FAQ - -**Q: Where is the lives starting value defined?** -A: `StorageManager.ts` line 25, `DEFAULT_LIVES = 10` - -**Q: How do players earn lives?** -A: By completing levels correctly. See SUMMARY.md "Earning Lives" - -**Q: Can players lose lives?** -A: Only by unlocking hints (-1 each). Wrong answers don't penalize. See SUMMARY.md "Spending Lives" - -**Q: Where is progress stored?** -A: Browser localStorage under keys "game_lives" and "game_progress". See POINTS_FLOW_DIAGRAM.md "Data Persistence" - -**Q: What happens when time runs out?** -A: Game plays fail sound but doesn't end. Players can still submit. See GAME_ANALYSIS.md Section 6 - -**Q: How does the hint system work?** -A: Hint 1 is free, Hints 2 & 3 cost 1 life each. See GAME_ANALYSIS.md Section 7 - -**Q: Is there backend sync?** -A: No. Progress is local-only. See SUMMARY.md "Missing Features" - ---- - -## 📞 Document Metadata - -| Property | Value | -|----------|-------| -| Total Files Analyzed | 21 TypeScript files | -| Total Lines of Code | ~2,500 lines | -| Documentation Generated | 4 files, ~45KB | -| Coverage | Complete (100%) | -| Last Updated | April 5, 2026 | -| Scope | Points/Lives/Score System | - ---- - -## 🎓 Summary of Key Takeaways - -1. **Currency:** LIVES (renewable resource), not points -2. **Starting:** 10 lives for new players -3. **Economy:** +1 life per win, -1 per hint unlock -4. **Storage:** localStorage (persistent, survives app close) -5. **API:** Fetches level data on app start, cached in memory -6. **Progression:** Sequential level unlocking with level 1 always available -7. **Strategy:** No penalties for wrong answers, encourages replayability -8. **WeChat:** Sharing with level referral parameter -9. **Scope:** No backend sync, no authentication, local-only progress -10. **Design:** Simple, effective, encourages skill-based progression - ---- - -**Questions?** All answers are in one of these 4 documentation files! - diff --git a/PROJECT_EXPLORATION_REPORT.md b/PROJECT_EXPLORATION_REPORT.md deleted file mode 100644 index f8c96fb..0000000 --- a/PROJECT_EXPLORATION_REPORT.md +++ /dev/null @@ -1,347 +0,0 @@ -# mp-xieyingeng 项目完整探索报告 - -## 📋 项目概述 -**项目名称**: 写英语谐音梗小游戏 (mp-xieyingeng) -**引擎**: Cocos Creator 3.x (TypeScript) -**平台**: 微信小游戏 -**架构**: 分布式页面管理系统 + 单例工具类 - ---- - -## 📁 完整目录结构 - -``` -assets/ -├── main.ts # 主入口脚本,初始化 ViewManager -├── PageLoading.ts # 加载页面,处理登录、资源预加载、分享码检测 -├── prefabs/ # 页面预制体和组件 -│ ├── PageHome.ts # 首页 -│ ├── PageLevel.ts # 关卡页面(核心游戏逻辑) -│ ├── PageWriteLevels.ts # 写关卡页面(创建/编辑关卡) -│ ├── PagePreviewLevels.ts # 预览关卡页面 -│ ├── PagePKData.ts # 挑战数据页面 -│ ├── PagePKDetail.ts # 挑战详情页面 -│ ├── PassModal.ts # 通关弹窗 -│ └── Toast.ts # Toast 提示组件 -├── scripts/ -│ ├── config/ -│ │ └── ApiConfig.ts # API 配置(端点、超时时间等) -│ ├── core/ -│ │ ├── BaseView.ts # 页面基类(生命周期管理) -│ │ └── ViewManager.ts # 页面管理器(单例,栈管理) -│ ├── types/ -│ │ ├── LevelTypes.ts # 关卡数据类型定义 -│ │ └── ApiTypes.ts # API 响应类型定义 -│ └── utils/ # 工具类(单例) -│ ├── HttpUtil.ts # HTTP 请求工具(GET/POST + JWT 认证) -│ ├── ShareManager.ts # 分享管理器(创建分享、加入分享) -│ ├── LevelDataManager.ts # 关卡数据管理器(API 拉取、缓存、图片加载) -│ ├── UserAssetsManager.ts # 用户资产管理器(积分同步) -│ ├── AuthManager.ts # 认证管理器(微信登录、Token 管理) -│ ├── StorageManager.ts # 本地存储管理器(进度、积分、Token) -│ ├── WxSDK.ts # 微信 SDK 封装(登录、分享、震动等) -│ ├── ToastManager.ts # Toast 管理器 -│ └── [其他工具类...] -└── resources/ # 游戏资源 - ├── audios/ # 音效文件 - ├── images/ - │ ├── level/ # 关卡图片 - │ ├── pageHome/ # 首页图片 - │ ├── pageLevel/ # 关卡页面图片 - │ └── pageLoading/ # 加载页面图片 -``` - ---- - -## 🎯 所有 TypeScript 文件列表 - -### 核心入口 -- `assets/main.ts` - 主脚本,注册所有页面到 ViewManager -- `assets/PageLoading.ts` - 加载页面,并行执行登录 + 资源加载 - -### 页面组件(每个继承 BaseView) -- `assets/prefabs/PageHome.ts` - 首页 -- `assets/prefabs/PageLevel.ts` - **核心游戏逻辑** -- `assets/prefabs/PageWriteLevels.ts` - 写关卡 -- `assets/prefabs/PagePreviewLevels.ts` - 预览关卡 -- `assets/prefabs/PagePKData.ts` - PK 数据展示 -- `assets/prefabs/PagePKDetail.ts` - PK 详情 -- `assets/prefabs/PassModal.ts` - 通关弹窗 -- `assets/prefabs/Toast.ts` - Toast 提示 - -### 核心框架 -- `assets/scripts/core/BaseView.ts` - 页面基类(onViewLoad、onViewShow、onViewHide、onViewDestroy) -- `assets/scripts/core/ViewManager.ts` - 页面管理器(栈管理、缓存、生命周期调用) - -### 配置 -- `assets/scripts/config/ApiConfig.ts` - API 端点 + 超时配置 - -### 类型定义 -- `assets/scripts/types/ApiTypes.ts` - API 响应类型 -- `assets/scripts/types/LevelTypes.ts` - 关卡数据类型 - -### 工具类(单例) -- `assets/scripts/utils/HttpUtil.ts` - **HTTP 请求**(GET/POST + JWT) -- `assets/scripts/utils/ShareManager.ts` - **分享系统** -- `assets/scripts/utils/LevelDataManager.ts` - **关卡数据管理** -- `assets/scripts/utils/UserAssetsManager.ts` - 积分管理 -- `assets/scripts/utils/AuthManager.ts` - **认证 + 登录** -- `assets/scripts/utils/StorageManager.ts` - 本地存储 -- `assets/scripts/utils/WxSDK.ts` - 微信 SDK 封装 -- `assets/scripts/utils/ToastManager.ts` - Toast 管理 -- 其他:RoundedRectMask、BackgroundScaler 等 - ---- - -## 💾 关卡通关逻辑核心 - -### 1. 通关判定(PageLevel.ts 第 660-673 行) -```typescript -onSubmitAnswer(): void { - const userAnswer = this.getAnswer(); - if (userAnswer === this._currentConfig.answer) { - this.showSuccess(); - } else { - this.showError(); - } -} -``` - -### 2. 通关奖励(PageLevel.ts 第 679-701 行) -```typescript -private async showSuccess(): Promise { - // 停止倒计时 - this.stopCountdown(); - - // 非分享模式才增加积分 - if (!this._isShareMode) { - const levelId = this._currentConfig?.id; - const timeSpent = 60 - this._countdown; - await UserAssetsManager.instance.earnPoint(levelId, timeSpent); - } - - // 显示通关弹窗 - this._showPassModal(); -} -``` - -### 3. 进度保存(PageLevel.ts 第 785-816 行) -```typescript -private async nextLevel(): Promise { - // 非分享模式才保存进度 - if (!this._isShareMode) { - StorageManager.onLevelCompleted(this.currentLevelIndex); - } - - this.currentLevelIndex++; - - // 检查是否全部通关 - const totalLevels = this._isShareMode - ? ShareManager.instance.getShareLevelCount() - : LevelDataManager.instance.getLevelCount(); - - if (this.currentLevelIndex >= totalLevels) { - // 全部通关,返回首页 - ViewManager.instance.back(); - } else { - // 加载下一关 - await this.initLevel(); - this.startCountdown(); - } -} -``` - -### 4. 已通关关卡同步(PageLoading.ts 第 115-139 行) -```typescript -private _syncProgressFromServer(): void { - // 服务端返回的已通关关卡 ID - const completedIds = AuthManager.instance.completedLevelIds; - - // 转换为本地索引 - const maxCompletedIndex = LevelDataManager.instance.getMaxCompletedIndex(completedIds); - - // 取较大值,防止进度回退 - if (maxCompletedIndex > localMax) { - StorageManager.onLevelCompleted(maxCompletedIndex); - } -} -``` - ---- - -## 🔗 API 端点一览 - -### 已配置的 API(ApiConfig.ts) -``` -API_BASE = 'https://ilookai.cn/api/v1' - -端点: -- POST /auth/wx-login # 微信登录 -- GET /user/assets # 获取用户积分 -- POST /user/assets/consume # 消耗积分(解锁线索) -- POST /user/assets/earn # 获得积分(通关奖励) -- GET /user/game-data # 获取用户数据(进度 + 积分) -- GET /wechat-game/levels # 获取所有关卡 -- POST /share # 创建分享 -- POST /share/{code}/join # 加入分享 -- GET /user/info # 获取用户信息 -``` - ---- - -## 🏗️ 架构特点 - -### 1. 单例模式 -- `ViewManager` - 页面管理 -- `LevelDataManager` - 关卡数据 -- `ShareManager` - 分享管理 -- `UserAssetsManager` - 积分管理 -- `AuthManager` - 认证 -- `StorageManager` - 本地存储(全静态) -- `ToastManager` - Toast 管理 -- `WxSDK` - 微信 SDK(全静态) -- `HttpUtil` - HTTP 工具(全静态) - -### 2. 页面栈管理 -- `ViewManager` 维护页面栈 -- `open()` - 打开页面,压栈 -- `back()` - 返回上一页,出栈 -- `replace()` - 替换当前页,不压栈 - -### 3. 生命周期 -每个页面 4 个阶段: -- `onViewLoad()` - 首次创建时调用 -- `onViewShow()` - 每次显示时调用 -- `onViewHide()` - 隐藏时调用 -- `onViewDestroy()` - 销毁时调用 - ---- - -## 🎯 关键发现 - -### ✅ 已有的工具类 - -#### HTTP 请求 -- **HttpUtil.ts** - GET/POST + JWT 认证 - - 支持自定义超时时间 - - 自动添加 Authorization 头 - - 泛型支持 - -#### 数据管理 -- **LevelDataManager.ts** - 关卡数据 + 图片缓存 - - API 请求重试机制 - - 图片懒加载(预加载下一关) - - URL -> SpriteFrame 缓存 - -- **UserAssetsManager.ts** - 积分管理 - - 消耗积分(解锁线索) - - 获得积分(通关奖励) - - 自动降级本地处理 - -- **AuthManager.ts** - 认证 + 登录 - - 微信登录流程 - - Token 恢复与验证 - - 获取通关进度 - -- **StorageManager.ts** - 本地存储 - - 积分、Token、进度、用户信息 - - 全静态工具类 - -#### 游戏逻辑 -- **ShareManager.ts** - 分享系统 - - 创建分享、加入分享 - - 独立数据管理,不影响正常进度 - -- **ViewManager.ts** - 页面管理 - - 栈管理 - - 缓存机制 - - 生命周期管理 - -- **WxSDK.ts** - 微信 API 封装 - - 登录、分享、震动 - - 隐私授权、用户信息 - - 非微信环境自动降级 - ---- - -## 📊 游戏流程图 - -### 正常模式 -``` -启动 → PageLoading - ├─ 并行加载:登录 + 关卡数据 - ├─ 检测分享码(无) - └─ 打开 PageHome - ├─ 开始游戏 → PageLevel - │ ├─ 加载关卡 - │ ├─ 倒计时 60s - │ ├─ 提交答案 - │ ├─ 判定成功/失败 - │ ├─ 通关 → PassModal - │ │ ├─ earnPoint()(加积分) - │ │ ├─ onLevelCompleted()(保存进度) - │ │ └─ nextLevel() - │ └─ 重复 2-7 - └─ PK → PageWriteLevels -``` - -### 分享模式 -``` -微信分享链接(含 shareCode) - ↓ -PageLoading - ├─ 检测到分享码 - └─ ShareManager.joinShare(code) - ↓ - PageLevel(shareMode=true) - ├─ 从 ShareManager 加载关卡 - ├─ 不增加积分 - ├─ 不保存本地进度 - └─ 全部通关 → 返回首页 -``` - ---- - -## 💡 核心要点 - -1. **HTTP 工具** ✅ - - HttpUtil.ts 已完整实现 - - 支持 JWT 认证 - - 可配置超时 - -2. **关卡数据** ✅ - - LevelDataManager 从 API 拉取 - - 支持图片缓存 + 懒加载 - - 支持重试机制 - -3. **通关逻辑** ✅ - - 判定:字符串比对(PageLevel.ts:660) - - 奖励:earnPoint()(PageLevel.ts:679) - - 进度:StorageManager.onLevelCompleted()(PageLevel.ts:785) - - 服务端同步:PageLoading.ts:115 - -4. **分享系统** ✅ - - 创建 + 加入完全独立 - - 不影响正常游戏进度 - - 懒加载关卡数据 - -5. **页面管理** ✅ - - ViewManager 栈管理 - - 4 个生命周期回调 - - 缓存机制 - -6. **认证系统** ✅ - - 微信登录 + Token 恢复 - - 自动添加授权头 - - 获取用户通关记录 - -7. **本地存储** ✅ - - StorageManager 集中管理 - - 支持进度 + 积分 + Token - - 带有默认值和数据校验 - -8. **微信 API** ✅ - - WxSDK 完整封装 - - 非微信环境自动降级 - - 支持分享、登录、震动等 - diff --git a/QUICK_FILE_REFERENCE.md b/QUICK_FILE_REFERENCE.md deleted file mode 100644 index e78e815..0000000 --- a/QUICK_FILE_REFERENCE.md +++ /dev/null @@ -1,272 +0,0 @@ -# 快速文件参考指南 - -## 🚀 最重要的 5 个文件 - -### 1. **PageLevel.ts** - 核心游戏逻辑 -位置:`assets/prefabs/PageLevel.ts` -``` -关键方法: -- onViewLoad() → 初始化关卡 -- onSubmitAnswer() → 第 660 行,提交答案判定 -- showSuccess() → 第 679 行,通关逻辑 -- nextLevel() → 第 785 行,进度保存 -``` - -### 2. **HttpUtil.ts** - HTTP 请求工具 -位置:`assets/scripts/utils/HttpUtil.ts` -``` -关键方法: -- get(url, timeout) → GET 请求 -- post(url, data, timeout) → POST 请求 -- setAuthToken(token) → 设置 JWT -``` - -### 3. **LevelDataManager.ts** - 关卡数据管理 -位置:`assets/scripts/utils/LevelDataManager.ts` -``` -关键方法: -- initialize(onProgress) → API 加载 + 预加载第一关 -- ensureLevelReady(index) → 按需加载关卡 -- preloadNextLevel(index) → 静默预加载下一关 -- getMaxCompletedIndex(ids) → 获取已通关索引 -``` - -### 4. **ShareManager.ts** - 分享系统 -位置:`assets/scripts/utils/ShareManager.ts` -``` -关键方法: -- createShare(title, levelIds) → 创建分享 -- joinShare(code) → 加入分享 -- triggerWxShare(title, code) → 触发分享 -- isShareMode → 判断是否分享模式 -``` - -### 5. **AuthManager.ts** - 认证管理 -位置:`assets/scripts/utils/AuthManager.ts` -``` -关键方法: -- initialize() → 初始化(恢复 token 或微信登录) -- wxLogin() → 微信登录 -- validateToken() → 验证 token 有效性 -``` - ---- - -## 📝 TypeScript 文件快速导航 - -### 文件位置速查表 - -| 文件名 | 路径 | 用途 | -|------|------|------| -| main.ts | assets/ | 主入口,初始化 ViewManager | -| PageLoading.ts | assets/ | 加载页面,登录 + 资源加载 | -| PageHome.ts | assets/prefabs/ | 首页 | -| PageLevel.ts | assets/prefabs/ | 关卡页面 **⭐** | -| PageWriteLevels.ts | assets/prefabs/ | 写关卡页面 | -| PagePreviewLevels.ts | assets/prefabs/ | 预览关卡 | -| PassModal.ts | assets/prefabs/ | 通关弹窗 | -| Toast.ts | assets/prefabs/ | Toast 提示 | -| BaseView.ts | assets/scripts/core/ | 页面基类 | -| ViewManager.ts | assets/scripts/core/ | 页面管理器 | -| ApiConfig.ts | assets/scripts/config/ | API 配置 | -| ApiTypes.ts | assets/scripts/types/ | API 类型定义 | -| LevelTypes.ts | assets/scripts/types/ | 关卡类型定义 | -| HttpUtil.ts | assets/scripts/utils/ | HTTP 工具 **⭐** | -| ShareManager.ts | assets/scripts/utils/ | 分享管理 **⭐** | -| LevelDataManager.ts | assets/scripts/utils/ | 关卡数据 **⭐** | -| UserAssetsManager.ts | assets/scripts/utils/ | 积分管理 | -| AuthManager.ts | assets/scripts/utils/ | 认证管理 **⭐** | -| StorageManager.ts | assets/scripts/utils/ | 本地存储 | -| WxSDK.ts | assets/scripts/utils/ | 微信 SDK | -| ToastManager.ts | assets/scripts/utils/ | Toast 管理 | - ---- - -## 🔧 API 端点速查 - -``` -基础地址:https://ilookai.cn/api/v1 - -认证相关: -- POST /auth/wx-login 微信登录 - -用户资产: -- GET /user/assets 获取积分 -- POST /user/assets/consume 消耗积分(解锁线索) -- POST /user/assets/earn 获得积分(通关) -- GET /user/game-data 获取用户数据 - -关卡相关: -- GET /wechat-game/levels 获取所有关卡 - -分享相关: -- POST /share 创建分享 -- POST /share/{code}/join 加入分享 - -用户相关: -- GET /user/info 获取用户信息 -``` - ---- - -## 💾 关键数据结构 - -### 关卡配置 -```typescript -interface RuntimeLevelConfig { - id: string; - name: string; - spriteFrame: SpriteFrame | null; - clue1: string; - clue2: string; - clue3: string; - answer: string; -} -``` - -### API 响应 -```typescript -interface ApiEnvelope { - success: boolean; - data: T | null; - message: string | null; -} -``` - -### 用户进度 -```typescript -interface UserProgress { - currentLevelIndex: number; // 当前关卡(0-based) - maxUnlockedLevelIndex: number; // 最高已解锁 -} -``` - ---- - -## 🎮 游戏流程关键代码位置 - -| 步骤 | 文件 | 行数 | 描述 | -|-----|------|------|------| -| 初始化 | main.ts | 34-90 | 注册页面 | -| 加载 | PageLoading.ts | 27-90 | 并行登录 + 加载关卡 | -| 分享检测 | PageLoading.ts | 61-77 | 检测分享码 | -| 进度同步 | PageLoading.ts | 115-139 | 服务端进度同步 | -| 加载关卡 | PageLevel.ts | 175-197 | 初始化关卡 | -| 倒计时 | PageLevel.ts | 593-641 | 60 秒计时 | -| 提交答案 | PageLevel.ts | 660-673 | 判定答案 | -| 通关处理 | PageLevel.ts | 679-701 | 播放音效 + 加积分 + 弹窗 | -| 进度保存 | PageLevel.ts | 785-816 | 下一关 + 进度保存 | - ---- - -## 🔗 工具类单例调用方式 - -```typescript -// HTTP 请求 -HttpUtil.get(url, timeout) -HttpUtil.post(url, data, timeout) -HttpUtil.setAuthToken(token) - -// 关卡数据 -LevelDataManager.instance.initialize(onProgress) -LevelDataManager.instance.ensureLevelReady(index) -LevelDataManager.instance.getLevelCount() - -// 用户资产 -UserAssetsManager.instance.fetchPoints() -UserAssetsManager.instance.consumePoint(levelId, hintIndex) -UserAssetsManager.instance.earnPoint(levelId, timeSpent) - -// 认证 -AuthManager.instance.initialize() -AuthManager.instance.isLoggedIn -AuthManager.instance.userId - -// 本地存储 -StorageManager.getPoints() -StorageManager.setPoints(points) -StorageManager.getCurrentLevelIndex() -StorageManager.onLevelCompleted(index) - -// 分享 -ShareManager.instance.createShare(title, levelIds) -ShareManager.instance.joinShare(code) -ShareManager.instance.isShareMode - -// 微信 API -WxSDK.isWechat() -WxSDK.login() -WxSDK.shareAppMessage(config) - -// 页面管理 -ViewManager.instance.open(viewId, options) -ViewManager.instance.back() -ViewManager.instance.replace(viewId, options) -``` - ---- - -## 📊 优先级指南(修改时参考) - -### 优先级 A(核心,谨慎修改) -- `PageLevel.ts` - 游戏逻辑 -- `HttpUtil.ts` - HTTP 请求 -- `LevelDataManager.ts` - 数据加载 -- `ViewManager.ts` - 页面管理 - -### 优先级 B(重要) -- `ShareManager.ts` - 分享系统 -- `AuthManager.ts` - 认证系统 -- `StorageManager.ts` - 本地存储 -- `UserAssetsManager.ts` - 积分管理 - -### 优先级 C(可选) -- `WxSDK.ts` - 微信 API -- `ToastManager.ts` - 提示管理 -- 其他 UI 文件 - ---- - -## 🎯 常见修改清单 - -### 修改 API 端点 -→ `assets/scripts/config/ApiConfig.ts` - -### 修改通关逻辑 -→ `assets/prefabs/PageLevel.ts`(第 660-816 行) - -### 修改关卡数据加载 -→ `assets/scripts/utils/LevelDataManager.ts` - -### 修改积分系统 -→ `assets/scripts/utils/UserAssetsManager.ts` - -### 修改分享流程 -→ `assets/scripts/utils/ShareManager.ts` - -### 修改页面流转 -→ `assets/scripts/core/ViewManager.ts` - -### 修改本地存储 -→ `assets/scripts/utils/StorageManager.ts` - ---- - -## 📞 快速查找 - -**如果要找 xxx 功能:** - -| 功能 | 查看 | -|-----|------| -| HTTP 请求 | HttpUtil.ts | -| 关卡加载 | LevelDataManager.ts | -| 通关判定 | PageLevel.ts:660 | -| 通关奖励 | PageLevel.ts:679 | -| 进度保存 | PageLevel.ts:785 | -| 分享功能 | ShareManager.ts | -| 登录流程 | AuthManager.ts | -| 本地数据 | StorageManager.ts | -| 页面跳转 | ViewManager.ts | -| 微信功能 | WxSDK.ts | -| Toast 提示 | ToastManager.ts | - diff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md deleted file mode 100644 index 055d7b6..0000000 --- a/QUICK_REFERENCE.md +++ /dev/null @@ -1,348 +0,0 @@ -# UI Components - Quick Reference Guide - -## Component Overview - -| Component | Purpose | Extends | Key Responsibilities | -|-----------|---------|---------|----------------------| -| **main.ts** | App bootstrap | Component | Register pages, init ViewManager & ToastManager | -| **PageLoading.ts** | Splash screen | Component | Load data, sync progress, detect share code | -| **PageHome.ts** | Landing page | BaseView | Navigation hub (game/PK buttons) | -| **PageLevel.ts** | Game level | BaseView | Core gameplay (timer, hints, scoring, input) | -| **PagePreviewLevels.ts** | Level preview | BaseView | Scrollable list of selected levels | -| **PassModal.ts** | Completion modal | BaseView | Next level & share buttons, success sound | -| **Toast.ts** | Notification | Component | Display message with fade animation | -| **ToastManager.ts** | Toast system | Singleton | Centralized toast creation & display | -| **BaseView.ts** | Page base class | Component | Lifecycle interface for all pages | -| **ViewManager.ts** | Page manager | Singleton | Navigation, stacking, caching, lifecycle | - ---- - -## Lifecycle Stages - -### Page Lifecycle (BaseView) -``` -onViewLoad() → onViewShow() → onViewHide() → onViewDestroy() - (once) (each show) (each hide) (on destroy) -``` - -### App Startup Flow -``` -main.onLoad() - ↓ (Initialize ViewManager + register pages) -PageLoading.start() - ↓ (Load auth + levels in parallel, check share code) -PageHome (normal) OR PageLevel (share mode) -``` - ---- - -## Key Data Flows - -### Level Progression -``` -PageLevel loads config → Displays image + clue1 → User enters answer - ↓ (correct) → Earn points → Show PassModal → Next level - ↓ (wrong) → Show toast → Continue playing - ↓ (timeout) → Play fail sound -``` - -### Hint Unlocking -``` -User clicks unlock button → Check points available - ↓ (yes) → Consume point from UserAssetsManager - ↓ → Hide unlock button, show clue - ↓ (no) → Show "insufficient points" toast -``` - -### Share Challenge Mode -``` -WeChat link → PageLoading detects share code - ↓ → ShareManager.joinShare(code) - ↓ → Play through shared levels (doesn't save local progress) - ↓ → Report progress to server - ↓ → Return to PageHome -``` - ---- - -## Component Properties Summary - -### PageLevel (Most Complex) -**UI Elements:** -- inputLayout, submitButton, inputTemplate, actionNode -- iconSetting (back button) -- tipsLayout, tipsItem1/2/3 (clues) -- unLockItem2/3 (unlock buttons) -- clockLabel (timer), liveLabel (points) -- mainImage (level image) - -**Audio:** -- clickAudio, successAudio, failAudio - -**Internal State:** -- _countdown (60 sec timer) -- _isTimeUp (timer expired flag) -- _currentConfig (level data) -- _isTransitioning (prevent double-submit) -- _isUnlocking (prevent double-click unlock) -- _passModalNode (modal reference) -- _isShareMode (shared vs normal) - -**Key Methods:** -- onViewLoad/Show/Hide/Destroy (lifecycle) -- initLevel() (async load config) -- createSingleInput(length) (input box) -- onUnlockClue(index) (consume points for hints) -- onSubmitAnswer() (check answer) -- showSuccess() (correct: earn points, show modal) -- showError() (wrong: toast + vibration) -- nextLevel() (progress to next) -- startCountdown() / onCountdownTick() (60s timer) - ---- - -## Points & Scoring System - -**Earning Points:** -- `UserAssetsManager.earnPoint(levelId, timeSpent)` -- Called in PageLevel.showSuccess() -- Time spent = 60 - countdown - -**Consuming Points:** -- `UserAssetsManager.consumePoint(levelId, hintIndex)` -- Called in PageLevel.onUnlockClue() -- Check `StorageManager.hasPoints()` before consuming - -**Displaying Points:** -- `StorageManager.getPoints()` → Display as "x {points}" -- Called in PageLevel.updatePointsLabel() - ---- - -## Timer System - -**60-Second Countdown:** -- Starts when level loads: `startCountdown()` -- Ticks every 1 second: `onCountdownTick()` -- Displayed as: "60s", "59s", ..., "0s" -- On timeout: plays fail sound, doesn't end game - -**Stop Timer:** -- `stopCountdown()` called when: - - Answer submitted (correct) - - Page destroyed - - All levels completed - ---- - -## Hint/Clue System - -**3 Clues Per Level:** -1. **Clue 1** - Always visible (free) -2. **Clue 2** - Unlock button, costs points -3. **Clue 3** - Unlock button, costs points - -**Display:** -- Text in tipsItem1/2/3 nodes -- Hidden if locked (tipsItem.active = false) -- Unlock button visible if locked - -**Unlock Flow:** -1. User clicks unLockItem2 or unLockItem3 -2. Check points: `StorageManager.hasPoints()` -3. Consume: `UserAssetsManager.consumePoint(levelId, index)` -4. Hide button, show clue, update UI - ---- - -## Navigation Stack - -**Structure:** -- LIFO stack managed by ViewManager -- Each open() adds to stack -- back() pops from stack -- replace() swaps top - -**Example Session:** -``` -[PageHome] - ↓ open PageLevel -[PageHome, PageLevel] - ↓ open PassModal (or inline) -[PageHome, PageLevel, PassModal?] - ↓ close/back -[PageHome] -``` - -**Methods:** -- `ViewManager.open(viewId, options?)` - Push to stack -- `ViewManager.back()` - Pop from stack, show previous -- `ViewManager.replace(viewId, options?)` - Pop current, push new - ---- - -## Caching Strategy - -**Cached Views (default):** -- Instance reused on re-open -- onViewLoad() called once -- onViewShow() called each time -- Memory persists - -**Non-Cached Views:** -- New instance each time -- onViewLoad() called each time -- Memory released on close - -**Configuration:** -```typescript -// In main.ts registration -ViewManager.instance.register('PageHome', { - prefab: pageHomePrefab, - cache: true, // ← Reuse instance - zIndex: 0 // ← Layer depth -}); -``` - ---- - -## Toast System - -**Show Toast:** -```typescript -ToastManager.show("消息内容", 2000); // duration in ms -``` - -**Initialization (main.ts):** -```typescript -ToastManager.instance.init(toastPrefab, canvasNode); -``` - -**Toast Behavior:** -- Creates Toast instance from prefab -- Displays for duration (default 2000ms) -- Fades out over 300ms -- Auto-destroys - ---- - -## WeChat Integration - -**Share Initialization (PageHome):** -```typescript -WxSDK.initShare({ - title: '写英语', - imageUrl: '', - query: '' -}); -``` - -**Share in Challenge (PassModal):** -```typescript -WxSDK.shareAppMessage({ - title: '快来一起玩这款游戏吧', - query: `level={levelIndex}` -}); -``` - -**Share Code Detection (PageLoading):** -```typescript -const shareCode = WxSDK.getShareCodeFromLaunch(); -if (shareCode) { - const joined = await ShareManager.instance.joinShare(shareCode); - // Launch PageLevel in share mode -} -``` - -**Other WeChat Features:** -- `WxSDK.vibrateLong()` - Haptic feedback on wrong answer -- `WxSDK.isWechat()` - Check if running in WeChat -- `checkPrivacySetting()` - Check privacy authorization -- `requirePrivacyAuthorize()` - Prompt for privacy consent - ---- - -## Error Handling & Edge Cases - -**Level Loading:** -- Try cache first, then async load -- Preload next level without blocking -- Handle missing config gracefully - -**Point Consumption:** -- Check availability before consuming -- Show toast if insufficient -- Prevent double-click with flag - -**Answer Submission:** -- Prevent multiple submissions with transition flag -- Trim whitespace from answer -- Case-sensitive comparison - -**Page Navigation:** -- Share mode: clears share state before returning home -- Normal mode: simple back() navigation -- Modal: stays on top (z-index 999) - ---- - -## Common Tasks - -### Add Toast -```typescript -ToastManager.show('提示信息'); -``` - -### Navigate to Page -```typescript -ViewManager.instance.open('PageLevel'); // Push -ViewManager.instance.back(); // Pop -ViewManager.instance.replace('PageHome');// Swap -``` - -### Get Current Level -```typescript -const index = StorageManager.getCurrentLevelIndex(); -``` - -### Check/Consume Points -```typescript -if (StorageManager.hasPoints()) { - await UserAssetsManager.consumePoint(levelId, hintIndex); -} -``` - -### Start Game -```typescript -// From PageHome -ViewManager.instance.open('PageLevel'); -``` - -### Create & Share Challenge -```typescript -// User flow: -PageHome → [PK Button] → PageWriteLevels [Select 6] - → PagePreviewLevels [Verify] - → PassModal [Share Button] - → WxSDK.shareAppMessage() -``` - ---- - -## File Size & Complexity - -| File | Lines | Complexity | -|------|-------|-----------| -| main.ts | 92 | Simple (init only) | -| PageLoading.ts | 141 | Medium (parallel loading, sync) | -| PageHome.ts | 119 | Simple (two buttons) | -| PageLevel.ts | 823 | **Very High** (core game logic) | -| PagePreviewLevels.ts | 247 | Medium (scroll list) | -| PassModal.ts | 155 | Simple (two buttons) | -| Toast.ts | 50 | Simple (fade animation) | -| ToastManager.ts | 59 | Simple (factory pattern) | -| BaseView.ts | 132 | Medium (lifecycle) | -| ViewManager.ts | 320 | High (stack navigation) | - -**Total: ~2,138 lines** | **Focal Point: PageLevel.ts (38% of code)** - diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index 4c0ba2d..0000000 --- a/SUMMARY.md +++ /dev/null @@ -1,416 +0,0 @@ -# Game System Summary - Points/Score/Lives Analysis - -**Project:** Cocos Creator WeChat Mini-Game ("写英语" - Write English) -**Date Analyzed:** April 5, 2026 -**Analysis Scope:** Complete TypeScript codebase (21 source files) - ---- - -## Executive Summary - -This game uses a **LIVES-based economy** rather than traditional points/coins: - -- **Currency:** Lives (renewable resource) -- **Starting Value:** 10 lives per new player -- **Earning Mechanic:** +1 life per completed level -- **Spending Mechanic:** -1 life per hint unlock (optional, 2 hints available) -- **Net Effect:** Players naturally gain lives if they solve without hints, stay stable with 1 hint/level, or lose lives if using both hints - -The system is **fully persistent** (saved to localStorage) and **progression-aware** (can replay earlier levels while advancing). - ---- - -## Files Analyzed (21 total) - -### Core Game Logic (4 files) -1. **PageLevel.ts** (786 lines) - Main gameplay, hint system, answer validation -2. **LevelDataManager.ts** (312 lines) - API calls, level caching, image loading -3. **StorageManager.ts** (240 lines) - Lives & progress persistence -4. **PageLoading.ts** (88 lines) - App initialization, preloading - -### UI/Navigation (3 files) -5. **ViewManager.ts** (320 lines) - Page stack management -6. **BaseView.ts** (132 lines) - View lifecycle -7. **PassModal.ts** (155 lines) - Victory screen with rewards - -### Utilities (5 files) -8. **ToastManager.ts** (59 lines) - Toast notifications -9. **WxSDK.ts** (188 lines) - WeChat API wrapper -10. **HttpUtil.ts** (76 lines) - HTTP requests -11. **Toast.ts** (50 lines) - Toast display component -12. **LevelTypes.ts** (59 lines) - TypeScript interfaces - -### Prefabs & Entry Points (3 files) -13. **PageHome.ts** (78 lines) - Home page -14. **main.ts** (59 lines) - App entry point -15. Plus UI utilities (BackgroundScaler, RoundedRectMask) - ---- - -## The Complete Points/Lives Flow - -### 🟢 EARNING Lives -| Event | Amount | Condition | -|-------|--------|-----------| -| Correct Answer | +1 | Submitted answer matches API answer exactly (case-sensitive, trimmed) | -| New Game | +10 | First time players only (default) | -| **Cannot Earn** | 0 | Wrong answers, timeouts, hint unlocks, returning players don't reset | - -### 🔴 SPENDING Lives -| Event | Amount | Condition | -|--------|--------|-----------| -| Unlock Hint 2 | -1 | Player clicks unlock, must have lives > 0 | -| Unlock Hint 3 | -1 | Player clicks unlock, must have lives > 0 | -| No Other Costs | - | No penalties for wrong answers or timeouts | - -### 📊 Net Economy Per Level -``` -Best Case (No Hints): +1 life/level → Infinite scaling -Average Case (1 Hint): 0 lives/level → Stable indefinitely -Hard Case (2 Hints): -1 life/level → Runs out after 10-20 levels -``` - ---- - -## Data Persistence - -### localStorage Keys -```javascript -{ - "game_lives": "10", // Current lives count - "game_progress": "{ // Player progression - \"currentLevelIndex\": 0, - \"maxUnlockedLevelIndex\": 0 - }" -} -``` - -### Lifespan -- **Initial:** Set when first loading game -- **Updates:** Every level completion or hint unlock -- **Persistence:** Survives app close/reopen (native browser storage) -- **Manual Reset:** Available via `StorageManager.resetAll()` - ---- - -## Level Progression System - -### Mechanics -1. **Level 1 Always Unlocked** - New players start here -2. **Sequential Unlocking** - Beat level N to unlock level N+1 -3. **Non-Linear Progress** - Can replay earlier levels anytime -4. **Max Tracking** - Tracks highest level reached for achievements - -### Data Structure -```typescript -{ - currentLevelIndex: number, // Which level to play next (0-based) - maxUnlockedLevelIndex: number // Highest level player has beaten -} -``` - -### Example Progression -``` -User beats Level 3 (index 2) -↓ -currentLevelIndex → 2+1 = 3 (level 4) -maxUnlockedLevelIndex → max(2, previous) = 2 -↓ -Can now play levels 0-3 -Must complete level 3 to unlock level 4 -``` - ---- - -## Hint System - -### Mechanics -- **Hint 1:** Always visible, completely free -- **Hint 2:** Hidden by default, costs 1 life to unlock -- **Hint 3:** Hidden by default, costs 1 life to unlock - -### Implementation -```typescript -onUnlockClue(index: 2|3) { - if (!hasLives()) return; // Must have >= 1 life - consumeLife(); // Deduct 1 life - showClue(index); // Reveal the clue content -} -``` - -### Strategic Element -Players must decide if they want to spend lives for hints or solve blindly to accumulate more lives for future levels. - ---- - -## Gameplay Loop - -### Per-Level Sequence -1. Load level image from API cache -2. Display Hint 1 (free) + two "Unlock" buttons -3. Start 60-second countdown -4. Player can: - - Unlock hints by spending lives (optional, repeatable) - - Type their answer in single text box - - Submit answer -5. Outcome: - - **Correct:** Reward +1 life, show victory modal - - **Wrong:** No penalty, show error toast, can retry - - **Timeout:** Play fail sound, can still submit - -### Victory Rewards -``` -onSubmitAnswer(userAnswer) { - if (userAnswer.trim() === correctAnswer) { - playSuccessSound(); - addLife(); // +1 life - StorageManager.onLevelCompleted(currentLevel); - showPassModal(); // Next/Share buttons - } -} -``` - ---- - -## API Integration - -### Endpoint -``` -GET https://ilookai.cn/api/v1/wechat-game/levels -``` - -### Response Format -```typescript -{ - success: boolean, - message: string | null, - data: { - levels: [ - { - id: "uuid", - level: 1, - imageUrl: "https://...", - hint1: "Free clue", - hint2: "Paid clue", - hint3: "Paid clue", - answer: "CORRECT_ANSWER", - sortOrder: 1 - }, - // ... more levels - ], - total: number - } -} -``` - -### Reliability Features -- **Retry Logic:** 2 attempts on failure -- **Timeout:** 8 seconds per request -- **Fallback:** Shows error message if all retries fail -- **Caching:** All levels cached in memory after first load - ---- - -## WeChat Integration - -### Features Implemented -1. **Sharing** - Share game to friends with level parameter -2. **Vibration** - Haptic feedback on errors -3. **Platform Detection** - Gracefully degrade on non-WeChat platforms - -### Share Implementation -```typescript -// On victory, user can click "Share" -WxSDK.shareAppMessage({ - title: "快来一起玩这款游戏吧", - query: `level=${victoryLevelIndex}` -}); -// Opens WeChat share dialog with referral link -``` - -### Not Implemented -- ❌ User authentication (wx.login) -- ❌ Backend progress sync -- ❌ Analytics -- ❌ Ads/Monetization -- ❌ Leaderboards - ---- - -## User Journey Example - -``` -New User Opens Game - ↓ -[Initialize: 10 lives, Level 1] - ↓ -Attempt Level 1 - ├─ Unlocks Hint 2 (-1 life) → 9 lives - ├─ Unlocks Hint 3 (-1 life) → 8 lives - ├─ Submits Answer - │ - ├─ CORRECT - │ ├─ Awards +1 life → 9 lives - │ ├─ Saves progress (level → 2) - │ └─ Shows PassModal - │ ├─ Can click "Next Level" → Level 2 - │ └─ Can click "Share" → wx.share - │ - └─ WRONG - ├─ No penalty → 9 lives unchanged - ├─ Shows error toast - └─ Can retry immediately -``` - ---- - -## Loading & Initialization - -### On App Start -1. **Detect New vs. Returning User** - - Check localStorage "game_lives" key - - New: Initialize to 10 - - Returning: Restore value - -2. **Load Level Metadata** - - API call to fetch all levels - - Retry up to 2 times on failure - - Cache all level data in memory - -3. **Preload First Level** - - Download image for level 1 - - Cache in memory for instant display - -4. **Show Home Screen** - - Display "Start Game" button - - Initialize WeChat sharing - -### On Subsequent Game Sessions -1. Restore lives from localStorage -2. Restore progress from localStorage -3. Resume at saved level -4. API already cached (no refetch unless app restarted) - ---- - -## Key Constants - -| Constant | Value | Usage | -|----------|-------|-------| -| DEFAULT_LIVES | 10 | New player starting amount | -| MIN_LIVES | 0 | Cannot go negative | -| LEVEL_TIME_LIMIT | 60 seconds | Countdown timer | -| API_TIMEOUT | 8000ms | HTTP request timeout | -| API_RETRY_COUNT | 2 | Attempts on failure | -| HINT_COST | 1 life each | Unlock clue 2 or 3 | -| WIN_REWARD | 1 life | Completion bonus | -| MODAL_Z_INDEX | 999 | Victory modal layer | - ---- - -## Business Logic Highlights - -### Win Condition -```typescript -userAnswer.trim() === correctAnswer -``` -- Case-sensitive -- Whitespace trimmed -- Must match exactly - -### No Lose Condition -- Wrong answers: No penalty -- Time up: No penalty -- Can retry infinitely on same level - -### Safety Checks -- Can't unlock hints if lives ≤ 0 -- Can't go below 0 lives -- Can't exceed total levels -- Invalid data resets to defaults - ---- - -## Performance Optimizations - -1. **Image Caching** - Downloaded images cached in memory -2. **Level Metadata Caching** - API data cached to avoid re-fetching -3. **Progress Caching** - localStorage data cached to reduce reads -4. **Async Preloading** - Next level preloaded silently -5. **Efficient UI** - Single EditBox (not per-character) -6. **Lazy Loading** - Pages cached after first load - ---- - -## Missing Features / Gaps - -| Feature | Status | Impact | -|---------|--------|--------| -| User Authentication | ❌ | No way to sync across devices | -| Backend Progress Save | ❌ | Progress lost if localStorage clears | -| Monetization | ❌ | No revenue stream | -| Ads | ❌ | No ad integration | -| Analytics | ❌ | Can't track player behavior | -| Leaderboards | ❌ | No competition mechanics | -| Sound Toggle | ❌ | Always plays sounds | -| Difficulty Levels | ❌ | All players see same levels | - ---- - -## Testing Scenarios - -### Test Case 1: Lives Economy -``` -1. New game → 10 lives -2. Beat level without hints → 11 lives -3. Beat level with 1 hint → 11 lives (net 0) -4. Beat level with 2 hints → 10 lives (net -1) -→ Verify localStorage updated after each -``` - -### Test Case 2: Progression -``` -1. Complete level 1 -2. Verify currentLevel → 2 -3. Verify maxUnlocked → 1 -4. Restart app -5. Verify levels still saved -6. Replay level 1 -7. Verify no duplicate progress -``` - -### Test Case 3: Hint Cost -``` -1. Start with 10 lives -2. Unlock hint 2 → 9 lives displayed -3. Unlock hint 3 → 8 lives displayed -4. Can't unlock again (button inactive) -5. Wrong answer → 8 lives still -6. Correct answer → 9 lives (net -1) -``` - ---- - -## Conclusion - -This is a **well-architected mini-game** with: -- ✅ Clear lives-based economy -- ✅ Persistent progress tracking -- ✅ Strategic hint system -- ✅ Reliable API integration -- ✅ Good error handling -- ✅ WeChat platform integration - -The point system is **intentionally forgiving** - players who solve puzzles smartly gain lives indefinitely, while those using hints maintain stability. This encourages skill development without hard progress walls. - ---- - -## Documentation Files Generated - -1. **GAME_ANALYSIS.md** - Comprehensive 17-section analysis (13KB) -2. **QUICK_REFERENCE.md** - Quick lookup guide (5.6KB) -3. **POINTS_FLOW_DIAGRAM.md** - Visual flow diagrams (15KB) -4. **SUMMARY.md** - This executive summary (8KB) - -**Total:** 42KB of detailed documentation covering every aspect of the points/score/lives system. - diff --git a/UI_COMPONENTS_INDEX.md b/UI_COMPONENTS_INDEX.md deleted file mode 100644 index 0a49fa2..0000000 --- a/UI_COMPONENTS_INDEX.md +++ /dev/null @@ -1,489 +0,0 @@ -# UI Components Complete Analysis - Index - -## 📚 Documentation Files Created - -This analysis consists of three comprehensive documents: - -### 1. **UI_COMPONENT_ANALYSIS.md** (Main Reference) -**1,424 lines | 45 KB** -- Detailed breakdown of all 10 files -- Complete method signatures and implementations -- Lifecycle method documentation -- Data manager interactions -- Game flow explanations -- Key concepts and patterns - -**Best for:** Deep dives into specific components, understanding implementation details - -### 2. **QUICK_REFERENCE.md** (Developer Cheat Sheet) -**~400 lines | 8.6 KB** -- Component overview table -- Lifecycle stages summary -- Key data flows (visual) -- Component properties summary -- Points & scoring system quick look -- Timer system overview -- Hint/clue system -- Navigation stack examples -- Toast system usage -- WeChat integration quick reference -- Common tasks & code snippets -- File complexity metrics - -**Best for:** Quick lookups, copy-paste code patterns, understanding overall architecture - -### 3. **ARCHITECTURE_DIAGRAM.md** (Visual Reference) -**~1,000 lines | 26 KB** -- System architecture diagram -- Page state machine -- Page stack visualization with session traces -- Component communication diagrams -- Data flow diagrams (level completion, hint unlocking) -- Timer sequence diagrams -- Input box creation flow -- Share challenge mode flow -- ViewManager caching mechanism -- Error handling paths - -**Best for:** Understanding flow, tracing execution, visual learners - ---- - -## 🎯 Component Summary - -### Core Infrastructure -- **main.ts** (92 lines) - App bootstrap, register pages and managers -- **BaseView.ts** (132 lines) - Abstract page lifecycle interface -- **ViewManager.ts** (320 lines) - Stack-based page navigation system - -### UI Pages (All extend BaseView) -- **PageLoading.ts** (141 lines) - Splash screen, load data, detect share code -- **PageHome.ts** (119 lines) - Landing page hub with game/PK buttons -- **PageLevel.ts** (823 lines) ⭐ **Most Complex** - Main game with timer, hints, scoring -- **PagePreviewLevels.ts** (247 lines) - Scrollable list of selected levels -- **PassModal.ts** (155 lines) - Completion modal with next/share buttons - -### Notification System -- **Toast.ts** (50 lines) - Individual toast notification with fade animation -- **ToastManager.ts** (59 lines) - Singleton manager for toast creation - -**Total: ~2,138 lines across 10 files** - ---- - -## 🔑 Key Topics Quick Index - -### Game Mechanics -| Topic | Location | Key Method | -|-------|----------|-----------| -| 60-second countdown | PageLevel | startCountdown() / onCountdownTick() | -| Hint/clue system (3 levels) | PageLevel | onUnlockClue(index) | -| Answer verification | PageLevel | onSubmitAnswer() / getAnswer() | -| Level progression | PageLevel | nextLevel() | -| Points earning | PageLevel | showSuccess() + UserAssetsManager.earnPoint() | -| Points spending | PageLevel | onUnlockClue() + UserAssetsManager.consumePoint() | - -### UI Management -| Topic | Location | Key Method | -|-------|----------|-----------| -| Page navigation | ViewManager | open() / back() / replace() | -| Page lifecycle | BaseView | onViewLoad/Show/Hide/Destroy | -| Page caching | ViewManager | _viewCache Map | -| Page stacking | ViewManager | _viewStack array (LIFO) | -| Toast notifications | ToastManager | show(content, duration) | - -### Special Features -| Topic | Location | Key Method | -|-------|----------|-----------| -| Share challenge mode | PageLevel + ShareManager | _isShareMode flag | -| WeChat integration | PageHome, PassModal, WxSDK | initShare() / shareAppMessage() | -| Server progress sync | PageLoading | _syncProgressFromServer() | -| Level preloading | PageLevel | preloadNextLevel() | - ---- - -## 📊 Complexity Breakdown - -``` -PageLevel.ts (823 lines - 38% of total code) -├─ Input management -├─ Timer system -├─ Hint unlocking -├─ Answer validation -├─ Point interactions -├─ Audio effects -├─ Modal handling -└─ Share mode support - -ViewManager.ts (320 lines - 15% of total code) -├─ Page registration -├─ Stack management -├─ Instance caching -├─ Lifecycle coordination -└─ Error handling - -All Other Components (995 lines - 47% of total code) -``` - ---- - -## 🎮 Game Flow Overview - -``` -User Launches App - ↓ -main.ts registers pages & managers - ↓ -PageLoading loads auth + levels (parallel) - ↓ - ┌─ Detect Share Code ──→ Enter Share Mode (PageLevel) - │ ↓ - │ Play Levels - │ ↓ - │ Return Home - │ - └─ Normal Path ──→ Open PageHome - ↓ - ┌──────┴──────┐ - ↓ ↓ - Play Game Create Challenge - (PageLevel) (PageWriteLevels) - ↓ ↓ - Win Level PagePreviewLevels - ↓ ↓ - PassModal PassModal/Share - ↓ ↓ - Next/Back Share to WeChat -``` - ---- - -## 💾 Data Manager Interactions - -### By Page - -**PageLoading** -- AuthManager.initialize() -- LevelDataManager.initialize() -- StorageManager.getMaxUnlockedLevelIndex() -- StorageManager.onLevelCompleted() -- ShareManager.joinShare() -- WxSDK.getShareCodeFromLaunch() - -**PageHome** -- WxSDK.initShare() -- checkPrivacySetting() -- requirePrivacyAuthorize() - -**PageLevel** (Most interactions) -- LevelDataManager (load, preload, get count) -- StorageManager (get/set progress, get points) -- UserAssetsManager (earn/consume points) -- ShareManager (share mode, report progress) -- WxSDK (vibration, sharing) -- ToastManager (notifications) -- ViewManager (navigation) - -**PagePreviewLevels** -- LevelDataManager.ensureLevelReady() - -**PassModal** -- WxSDK.shareAppMessage() - ---- - -## ⚠️ Important Flags & Preventing Issues - -### Double-Submission Prevention -```typescript -// In PageLevel.onSubmitAnswer() -if (this._isTransitioning) return; // Blocks rapid re-submission -``` - -### Double-Click Unlock Prevention -```typescript -// In PageLevel.onUnlockClue() -if (this._isUnlocking) return; // Blocks rapid hint unlocking -``` - -### Share Mode Detection -```typescript -// In PageLevel.onViewLoad() -this._isShareMode = params?.shareMode === true; -``` - -### Timer Running Flag -```typescript -// In PageLevel.onCountdownTick() -if (this._isTimeUp) return; // Prevents countdown after timeout -``` - ---- - -## 🔄 Page Lifecycle Details - -### First-Time Open -1. ViewManager.open() called -2. Prefab instantiated -3. onViewLoad() called (do heavy init here) -4. Page added to stack -5. onViewShow() called - -### Re-open (if cached) -1. ViewManager.open() called -2. Cache hit found -3. Previous page onViewHide() called -4. This page onViewShow() called (not onViewLoad!) -5. Page already in stack (or re-added) - -### Close/Back -1. ViewManager.back() called -2. Current page onViewHide() called -3. Current page destroyed (if not cached) -4. Previous page onViewShow() called - -### Destroy -1. onViewDestroy() called -2. All listeners removed -3. Resources cleaned up -4. Node destroyed - ---- - -## 📝 Common Code Patterns - -### Open Page with Parameters -```typescript -ViewManager.instance.open('PageLevel', { - params: { shareMode: true }, - onComplete: (view) => { - console.log('PageLevel opened'); - }, - onError: (err) => { - console.error('Failed to open:', err); - } -}); -``` - -### Get Page Parameters -```typescript -onViewLoad(): void { - const params = this.getParams(); - if (params?.shareMode) { - // Handle share mode - } -} -``` - -### Show Toast Notification -```typescript -ToastManager.show('提示信息', 2000); // 2 second display -``` - -### Check & Consume Points -```typescript -if (StorageManager.hasPoints()) { - const success = await UserAssetsManager.consumePoint(levelId, hintIndex); - if (success) { - this.updatePointsLabel(); - } -} -``` - -### Schedule & Unschedule -```typescript -startCountdown(): void { - this.schedule(this.onCountdownTick, 1); // Every 1 second -} - -stopCountdown(): void { - this.unschedule(this.onCountdownTick); // Stop callback -} -``` - ---- - -## 🐛 Known Patterns to Watch - -### Caching Implications -- Pages with cache:true keep their onViewLoad() state -- onViewShow() is called each time, even if cached -- If you modify state in onViewLoad(), it persists! -- Solution: Reset state in onViewShow() if needed - -### Share Mode Edge Cases -- Share mode doesn't save local progress -- Share mode uses ShareManager instead of LevelDataManager -- Share mode clears state on return to home -- Must check _isShareMode before saving - -### Modal Positioning -- PassModal instantiated with z-index 999 (topmost) -- Added to Canvas root node (not PageLevel child) -- This allows full-screen coverage with proper Widget alignment - -### Timer Edge Cases -- Timer continues even if page is hidden -- Must stop timer on destroy or completion -- onTimeUp() plays sound but doesn't end game automatically -- Wrong answer doesn't stop timer - ---- - -## 🎯 Tips for Modifications - -### Adding New Hint Types -1. Add hint4 config to RuntimeLevelConfig -2. Add unLockItem4 property to PageLevel -3. Add tipsItem4 property to PageLevel -4. Call showUnlockButton(4) in _applyLevelConfig() -5. Handle in onUnlockClue() switch/case - -### Adding Page Analytics -1. Add tracking in onViewLoad() (first visit) -2. Add tracking in onViewShow() (each visit) -3. Use page viewId for identification -4. Track user actions in event handlers - -### Changing Point System -1. Modify UserAssetsManager (not shown) -2. Update updatePointsLabel() display format -3. Adjust hasPoints() check if needed -4. Test unlock flow with new values - -### Adjusting Timer -1. Change `_countdown = 60` to desired seconds -2. Update display frequency if needed -3. Adjust point calculation if time-based -4. Test timeout behavior - ---- - -## 📖 How to Use This Documentation - -**For a new developer:** -1. Start with QUICK_REFERENCE.md for overview -2. Read UI_COMPONENT_ANALYSIS.md sections for specific pages -3. Reference ARCHITECTURE_DIAGRAM.md for flow understanding - -**For debugging:** -1. Find error in component name -2. Look up methods in UI_COMPONENT_ANALYSIS.md -3. Check flow in ARCHITECTURE_DIAGRAM.md -4. Use QUICK_REFERENCE.md for code patterns - -**For adding features:** -1. Understand game flow in QUICK_REFERENCE.md -2. Find where to hook in UI_COMPONENT_ANALYSIS.md -3. Check data flows in ARCHITECTURE_DIAGRAM.md -4. Copy patterns from QUICK_REFERENCE.md - -**For optimization:** -1. Note complexity metrics in QUICK_REFERENCE.md -2. Identify bottlenecks in PageLevel.ts -3. Check async operations in PageLoading.ts -4. Review caching in ViewManager.ts - ---- - -## 🔗 Cross-References - -### Frequently Referenced Files -- **PageLevel.ts** - Most complex, used from PageHome and PageLoading -- **ViewManager.ts** - Used by all pages for navigation -- **StorageManager** - Used by PageLoading and PageLevel -- **UserAssetsManager** - Used by PageLevel for points -- **LevelDataManager** - Used by PageLoading and PageLevel - -### Integration Points -``` -main.ts (startup) - ↓ -PageLoading (data loading) - ├─ AuthManager - ├─ LevelDataManager - └─ ShareManager - ↓ -PageHome (navigation hub) - ├─ PageLevel (game) - │ ├─ StorageManager - │ ├─ UserAssetsManager - │ ├─ WxSDK - │ └─ PassModal (modal) - │ └─ WxSDK - │ - └─ PageWriteLevels (not analyzed) - └─ PagePreviewLevels - └─ LevelDataManager -``` - ---- - -## 📊 File Statistics - -| Metric | Value | -|--------|-------| -| Total Lines | 2,138 | -| Total Files Analyzed | 10 | -| Most Complex File | PageLevel.ts (823 lines, 38%) | -| Most Reused Manager | ViewManager | -| Total Data Managers Used | 7+ | -| UI Components | 5 pages + 2 utilities | -| Lifecycle Methods | 4 per BaseView | -| Key Patterns | Stack navigation, caching, events | - ---- - -## 🚀 Next Steps for Development - -1. **Run the game** and trace through the flows documented here -2. **Add logging** to key methods to understand real execution order -3. **Modify a page** to get familiar with lifecycle -4. **Create new page** following BaseView pattern -5. **Add new feature** using documented patterns - ---- - -## 📞 Quick Troubleshooting - -**Page not showing?** -- Check ViewManager.open() called -- Verify prefab has BaseView component -- Check registration in main.ts - -**Points not updating?** -- Verify UserAssetsManager call succeeds -- Check updatePointsLabel() is called -- Test StorageManager.getPoints() returns value - -**Timer not stopping?** -- Check stopCountdown() called on completion -- Verify unschedule() works in Cocos -- Test onDestroy calls stopCountdown() - -**Modal not appearing?** -- Check z-index set to 999 -- Verify added to Canvas root -- Test instantiate() succeeds -- Check onViewLoad/Show called manually - -**Share mode broken?** -- Verify share code detected in PageLoading -- Check _isShareMode set properly -- Test ShareManager.joinShare() succeeds -- Verify clearShareMode() called on exit - ---- - -## ✅ Analysis Complete - -All 10 UI component files have been thoroughly analyzed and documented. - -**Total Documentation: 3 comprehensive guides** -- **UI_COMPONENT_ANALYSIS.md** - Deep technical reference (1,424 lines) -- **QUICK_REFERENCE.md** - Developer cheat sheet (~400 lines) -- **ARCHITECTURE_DIAGRAM.md** - Visual flow diagrams (~1,000 lines) -- **This file** - Navigation & index (this document) - -**Ready for:** Development, debugging, feature implementation, onboarding - diff --git a/UI_COMPONENT_ANALYSIS.md b/UI_COMPONENT_ANALYSIS.md deleted file mode 100644 index 4d4c756..0000000 --- a/UI_COMPONENT_ANALYSIS.md +++ /dev/null @@ -1,1424 +0,0 @@ -# Cocos Creator Project - UI Component Analysis - -This document provides a comprehensive analysis of the UI component files in the "写英语" (Write English) Cocos Creator game project. - ---- - -## 1. **main.ts** - Main Entry Script - -**Location:** `assets/main.ts` - -**Class:** `main extends Component` - -### Purpose -- Main initialization script that bootstraps the entire application -- Responsible for initializing the ViewManager singleton -- Registers all page prefabs with their configurations -- Initializes the ToastManager for notifications - -### Lifecycle Methods -- **onLoad()** - Executes before `start()` to ensure ViewManager is initialized early - - Calls `_initViewManager()` to set up the view system - -### Key Methods -- **_initViewManager()** - Core initialization logic: - - Initializes `ViewManager` with the canvas node as container - - Registers 5 pages with configurations (cache and z-index): - - **PageHome** (cache: true, zIndex: 0) - Landing page - - **PageLevel** (cache: true, zIndex: 1) - Game level page - - **PageWriteLevels** (cache: true, zIndex: 2) - Create challenge levels - - **PagePreviewLevels** (cache: true, zIndex: 3) - Preview selected levels - - **PagePKData** (cache: true, zIndex: 3) - Challenge data page - - Initializes `ToastManager` with toast prefab and canvas reference - -### UI Properties (Serialized in Editor) -``` -@property pageHomePrefab: Prefab -@property pageLevelPrefab: Prefab -@property pageWriteLevelsPrefab: Prefab -@property pagePreviewLevelsPrefab: Prefab -@property pagePKDataPrefab: Prefab -@property toastPrefab: Prefab -``` - -### Data Managers Used -- ViewManager (initialization) -- ToastManager (initialization) - -### Game Flow -Entry point → Initializes ViewManager → Registers all pages → PageLoading is likely instantiated separately to show splash/loading screen - ---- - -## 2. **PageLoading.ts** - Loading/Splash Screen - -**Location:** `assets/PageLoading.ts` - -**Class:** `PageLoading extends Component` - -### Purpose -- Handles application initialization and resource loading -- Manages user authentication and level data preloading -- Displays loading progress (0-100%) -- Handles WeChat share code detection for shared challenges -- Syncs user progress from server with local storage - -### Lifecycle Methods -- **start()** - Called when scene starts - - Immediately calls `_startPreload()` - -### Key Methods - -**_startPreload() - Main initialization pipeline:** -1. Resets progress bar to 0% -2. Runs login and level loading **in parallel** using `Promise.all()`: - - `AuthManager.instance.initialize()` - User authentication - - `LevelDataManager.instance.initialize()` - Level data loading (provides progress updates) - - Level loading occupies 0-80% of progress bar -3. Logs login success/failure -4. Checks for login success: - - If login successful → calls `_syncProgressFromServer()` - - Maps server `completedLevelIds` to local `currentLevelIndex` - - Takes max of (server progress, local progress) to prevent regression -5. Detects WeChat share code: - - Calls `WxSDK.getShareCodeFromLaunch()` to extract share code from launch parameters - - If share code exists AND login successful → calls `ShareManager.instance.joinShare(shareCode)` - - If join succeeds → directly opens **PageLevel** with `shareMode: true` - - Destroys loading page after PageLevel opens -6. Normal flow (no share code): - - Preloads **PageHome** (80-100% of progress) - - Opens PageHome and destroys itself - -**_syncProgressFromServer() - Server progress sync:** -- Gets `completedLevelIds` from server -- Finds max completed index: `LevelDataManager.getMaxCompletedIndex(completedIds)` -- Compares with local max: `StorageManager.getMaxUnlockedLevelIndex()` -- If server > local → calls `StorageManager.onLevelCompleted(maxCompletedIndex)` -- Ensures user never loses progress across devices - -**_updateProgress(progress: number)** - Updates progress bar (0-1 range) - -**_updateStatusLabel(message: string)** - Updates status text display - -### UI Components -``` -@property progressBar: ProgressBar -@property statusLabel: Label -``` - -### Data Managers Used -- **AuthManager** - User authentication (login, completedLevelIds) -- **LevelDataManager** - Level loading with progress callbacks -- **StorageManager** - Local progress storage -- **ShareManager** - Handle shared challenge joins -- **WxSDK** - WeChat integration (launch params, share code detection) -- **ViewManager** - Open pages - -### Flow Control -- Login + Level loading (parallel) → Server sync → Share code check → Page navigation → Self-destroy - -### Key States -- Progress tracking (0.0 to 1.0) -- Login success/failure handling -- Share code detection and join flow -- Normal vs. share challenge modes - ---- - -## 3. **PageHome.ts** - Home/Landing Page - -**Location:** `assets/prefabs/PageHome.ts` - -**Class:** `PageHome extends BaseView` - -### Purpose -- Main landing page displayed after loading complete -- Provides navigation to: - - Game levels (single-player) - - PK/Challenge creation (multiplayer) -- Handles WeChat SDK initialization and privacy authorization - -### Lifecycle Methods -- **onViewLoad()** - First-time initialization: - - Calls `_initButtons()` - - Calls `_initWxShare()` - - Calls `_checkPrivacyAuthorization()` - -- **onViewShow()** - Called every time page is shown (after being hidden) - - Logs view show event - -- **onViewHide()** - Called when page is covered or closed - - Logs view hide event - -- **onViewDestroy()** - Called when page is destroyed: - - Removes button event listeners - -### UI Event Handlers - -**_initButtons() - Button setup:** -```typescript -startGameBtn - "Start Game" button click → _onStartGameClick() -pkBtn - "PK/Challenge" button click → _onPkClick() -``` - -**_onStartGameClick() - Start game handler:** -- Logs button click -- Opens **PageLevel** (game page) -- Navigation: PageHome → PageLevel - -**_onPkClick() - Challenge/PK handler:** -- Logs button click -- Opens **PageWriteLevels** (challenge creation page) -- Navigation: PageHome → PageWriteLevels - -### UI Properties -``` -@property(Node) startGameBtn -@property(Node) pkBtn -``` - -### Data Managers Used -- ViewManager (page navigation) -- WxSDK (share initialization, privacy checks) - -### WeChat Integration - -**_initWxShare() - Share configuration:** -```typescript -WxSDK.initShare({ - title: '写英语' (title) - imageUrl: '' (empty - uses default) - query: '' (empty - filled per-share) -}) -``` - -**_checkPrivacyAuthorization() - Privacy handling:** -- Checks if running in WeChat environment: `WxSDK.isWechat()` -- Calls `checkPrivacySetting()` to check privacy authorization status -- If not authorized → calls `requirePrivacyAuthorize()` to prompt user -- Catches exceptions and logs warnings - -### Flow -PageLoading → PageHome → [startGameBtn] → PageLevel - → [pkBtn] → PageWriteLevels - ---- - -## 4. **PageLevel.ts** - Game Level/Challenge Page - -**Location:** `assets/prefabs/PageLevel.ts` - -**Class:** `PageLevel extends BaseView` - -### Purpose -- Main game level playing interface -- Handles gameplay mechanics: - - Display level image/clue - - Accept user answer input - - Verify answer correctness - - Manage hint unlocking with point consumption - - Level progression -- Supports both normal and shared challenge modes -- Manages 60-second countdown timer - -### Lifecycle Methods - -**onViewLoad() - Initialization:** -1. Reads parameters (checks for `shareMode: true`) -2. Sets `currentLevelIndex`: - - Share mode: start at index 0 - - Normal mode: restore from `StorageManager.getCurrentLevelIndex()` -3. Updates points display: `updatePointsLabel()` -4. Initializes icon setting button -5. Initializes unlock buttons (for hints 2 & 3) -6. Initializes submit button -7. **Async:** Loads level config → Starts 60-second countdown - -**onViewShow() - Shown callback:** -- Updates points display - -**onViewHide() - Hidden callback:** -- Logs event - -**onViewDestroy() - Cleanup:** -- Clears input nodes -- Stops countdown -- Closes pass modal -- Removes all event listeners - -### UI Properties -``` -// Layout -@property(Node) inputLayout - Input box container -@property(Node) submitButton - Answer submission -@property(Node) inputTemplate - Input box template -@property(Node) actionNode - Action buttons area -@property(Node) iconSetting - Settings/back button -@property(Node) tipsLayout - Clues layout -@property(Node) mainImage - Main clue image display - -// Clues (3 hints) -@property(Node) tipsItem1 - Clue 1 (default unlocked) -@property(Node) tipsItem2 - Clue 2 (unlockable) -@property(Node) tipsItem3 - Clue 3 (unlockable) - -// Unlock buttons -@property(Node) unLockItem2 - Unlock button for clue 2 -@property(Node) unLockItem3 - Unlock button for clue 3 - -// Display labels -@property(Label) clockLabel - Countdown timer display -@property(Label) liveLabel - Points display (named "liveLabel" for compatibility) - -// Audio clips -@property(AudioClip) clickAudio -@property(AudioClip) successAudio -@property(AudioClip) failAudio - -// Pass modal -@property(Prefab) passModalPrefab -``` - -### Configuration Properties -``` -@property currentLevelIndex - Current level (synced from storage) -``` - -### Internal State -``` -_inputNodes: Node[] - Created input box nodes -_countdown: number - Countdown remaining (60 seconds) -_isTimeUp: boolean - Timer expired flag -_currentConfig: RuntimeLevelConfig - Current level data -_isTransitioning: boolean - Prevents double submit during transitions -_isUnlocking: boolean - Prevents double-click on unlock buttons -_passModalNode: Node - Pass modal instance -_isShareMode: boolean - True if in shared challenge mode -``` - -### Level Loading - -**initLevel() - Async level loading:** -- Share mode: loads from `ShareManager.instance.ensureShareLevelReady(levelIndex)` -- Normal mode: - 1. Tries cache: `LevelDataManager.getLevelConfig(levelIndex)` - 2. If not cached → loads: `LevelDataManager.ensureLevelReady(levelIndex)` -- Calls `_applyLevelConfig(config)` after loading - -**_applyLevelConfig(config) - Apply level configuration:** -1. Stores config in `_currentConfig` -2. Resets transition/time-up flags -3. Sets main image from `config.spriteFrame` -4. Sets clue 1 (default visible) from `config.clue1` -5. Hides clues 2 & 3 -6. Shows unlock buttons 2 & 3 -7. Creates input field based on answer length: `createSingleInput(config.answer.length)` -8. Updates countdown display -9. Preloads next level: - - Share mode: `ShareManager.instance.ensureShareLevelReady(nextIndex)` - - Normal mode: `LevelDataManager.preloadNextLevel(levelIndex)` - -### Input Box Management - -**createSingleInput(answerLength) - Single input field:** -- Creates one input field sized for full answer -- Sets placeholder: `(X个字)` (X characters) -- Sets max length to answer length -- Dynamically sizes width: `Math.min(600, Math.max(200, answerLength * 60 + 40))` -- Adjusts underline width to match -- Listens to TEXT_CHANGED and EDITING_DID_ENDED events - -**getAnswer() - Retrieves user input:** -- Gets text from single input field -- Trims whitespace -- Returns as string - -### Clue/Hint System - -**Clue Display (3 levels):** -- Clue 1: Always visible, free -- Clue 2: Requires point consumption to unlock -- Clue 3: Requires point consumption to unlock - -**Methods:** -- `setClue(index, content)` - Set clue text -- `showClue(index)` - Show clue item -- `hideClue(index)` - Hide clue item -- `showUnlockButton(index)` - Show unlock button -- `hideUnlockButton(index)` - Hide unlock button - -**onUnlockClue(index) - Unlock hint handler:** -1. Check if currently unlocking (prevent double-click): `_isUnlocking` -2. Check points availability: `hasPoints()` -3. If no points → show toast "积分不足,无法解锁提示!" (Insufficient points) -4. Call `UserAssetsManager.consumePoint(levelId, index)` (async) -5. If successful: - - Update points display: `updatePointsLabel()` - - Play click sound - - Hide unlock button - - Show clue - - Set clue content from config (clue2 or clue3) - -### Points/Score System - -**updatePointsLabel() - Display current points:** -- Gets points: `StorageManager.getPoints()` -- Updates label: `x {points}` - -**hasPoints() - Check if user has points:** -- Returns: `StorageManager.hasPoints()` - -**Data Manager Interactions:** -- `UserAssetsManager.consumePoint(levelId, hintIndex)` - Consume points for hint (async) -- `UserAssetsManager.earnPoint(levelId, timeSpent)` - Earn points on level completion (async) -- `StorageManager.getPoints()` - Get current points -- `StorageManager.hasPoints()` - Check if any points available - -### Countdown Timer System - -**startCountdown() - Start 60-second timer:** -- Sets `_countdown = 60` -- Sets `_isTimeUp = false` -- Updates display -- Schedules `onCountdownTick()` every 1 second - -**onCountdownTick() - Per-second callback:** -- Decrements `_countdown` -- Updates display -- Calls `onTimeUp()` when countdown reaches 0 - -**updateClockLabel() - Display countdown:** -- Shows: `{_countdown}s` - -**stopCountdown() - Stop timer:** -- Unschedules `onCountdownTick()` - -**onTimeUp() - Timer expiration:** -- Plays fail sound -- Can add game over logic - -### Answer Submission - -**onSubmitAnswer() - Submit button handler:** -1. Checks if config loaded and not in transition -2. Gets user answer: `getAnswer()` -3. Compares with correct answer: `_currentConfig.answer` -4. If match → calls `showSuccess()` -5. If mismatch → calls `showError()` - -**showSuccess() - Correct answer handler:** -1. Sets `_isTransitioning = true` (prevent double submit) -2. Stops countdown -3. Plays success sound -4. Calculates time spent: `60 - _countdown` -5. Normal mode: - - Calls `UserAssetsManager.earnPoint(levelId, timeSpent)` to earn points - - Updates points display -6. Share mode: - - Calls `ShareManager.reportLevelProgress(levelId, true, timeSpent)` (fire-and-forget) -7. Shows pass modal: `_showPassModal()` - -**_showPassModal() - Display completion modal:** -1. Checks if prefab exists -2. Checks if modal already showing (prevent duplicates) -3. Instantiates pass modal -4. Positions at screen center -5. Sets z-index to 999 (highest layer) -6. Adds to Canvas root node (for proper full-screen layout) -7. Gets PassModal component and: - - Calls `setParams({levelIndex: currentLevelIndex + 1})` - - Sets callbacks for next level and share - - Manually calls `onViewLoad()` and `onViewShow()` - -**_closePassModal() - Close pass modal:** -- Checks if valid -- Destroys node -- Sets to null - -**showError() - Wrong answer handler:** -1. Plays fail sound -2. Triggers device vibration: `WxSDK.vibrateLong()` -3. Shows toast: "答案错误,再试试吧!" (Wrong answer, try again!) - -### Level Progression - -**nextLevel() - Advance to next level:** -1. Normal mode: saves progress → `StorageManager.onLevelCompleted(currentLevelIndex)` -2. Increments `currentLevelIndex` -3. Checks if more levels exist: - - Share mode: `ShareManager.getShareLevelCount()` - - Normal mode: `LevelDataManager.getLevelCount()` -4. If all levels complete: - - Logs completion message - - Stops countdown - - Share mode: clears share mode → opens PageHome - - Normal mode: goes back (ViewManager.back()) -5. If more levels: - - Loads next level: `initLevel()` - - Restarts countdown: `startCountdown()` - -### Audio Management - -**playSound(clip) - Generic sound player:** -- Gets AudioSource component -- Plays clip one-shot - -**playClickSound()** - Play button click sound - -**playSuccessSound()** - Play level completion sound - -**playFailSound()** - Play wrong answer sound - -### Icon Setting Button (Settings/Back) - -**initIconSetting() - Bind settings button:** -- Binds TOUCH_END event to `onIconSettingClick()` - -**onIconSettingClick() - Back button handler:** -1. Plays click sound -2. Share mode: - - Clears share mode: `ShareManager.clearShareMode()` - - Replaces with PageHome: `ViewManager.replace('PageHome')` -3. Normal mode: - - Goes back to previous page: `ViewManager.back()` - -### Data Managers Used -- **LevelDataManager** - Load level configs, preload next level, get level count -- **StorageManager** - Get/set current level, save progress, get points -- **UserAssetsManager** - Consume/earn points for hints and completions -- **ShareManager** - Handle shared challenge mode -- **WxSDK** - Vibration feedback, WeChat API -- **ToastManager** - Show notification toasts -- **ViewManager** - Navigate to other pages - -### Game Flow -1. Load level data (async) -2. Display main image, clue 1, unlock buttons -3. Create input field -4. Start 60-second countdown -5. User enters answer -6. User can unlock clues 2 & 3 (points cost) -7. User submits answer -8. If correct: show pass modal, earn points, progress to next level -9. If incorrect: show error toast, continue playing -10. If timeout: show fail sound, can add game over - -### Share Challenge Flow -- Join via WeChat link with share code -- Load custom level set from ShareManager -- Play through shared levels -- Report progress back to server -- Return to home when complete - ---- - -## 5. **PagePreviewLevels.ts** - Challenge Level Preview - -**Location:** `assets/prefabs/PagePreviewLevels.ts` - -**Class:** `PagePreviewLevels extends BaseView` - -### Purpose -- Displays preview of 6 selected challenge levels in a vertical scrollable list -- Used after PageWriteLevels (where user selects 6 levels) -- Shows for each level: - - Cover image - - Hint 1 - - Hint 2 - - Answer -- Allows user to verify selected levels before sharing - -### Lifecycle Methods - -**onViewLoad() - First-time initialization:** -1. Calls `_initButtons()` - Bind back buttons -2. Calls `_initScrollView()` - Configure scroll view anchor - -**onViewShow() - Page shown:** -1. Calls `_buildList()` - Build level preview list from params - -**onViewHide() - Page hidden:** -- Logs event - -**onViewDestroy() - Cleanup:** -1. Calls `_offButtons()` - Remove button listeners -2. Calls `_clearList()` - Destroy all list items - -### UI Properties -``` -@property(Node) backBtn - Top-left back button -@property(Node) scrollView - ScrollView component container -@property(Node) listContent - Content node of scroll view (vertical list) -@property(Node) listTemplate - Template node for single level preview item -@property(Node) backButton - Bottom back button -@property(Node) pkTitle - Title label (editable) -``` - -### Layout Configuration -```typescript -LAYOUT = { - ITEM_HEIGHT: 300, // Height of each level preview item - SPACING_Y: 30, // Vertical spacing between items - PADDING_TOP: 20 // Top padding -} -``` - -### Button Handling - -**_initButtons() - Bind button events:** -- Binds both `backBtn` and `backButton` (redundant back buttons) -- Listens for Button.EventType.CLICK -- Calls `_onBackClick()` when clicked - -**_offButtons() - Remove event listeners:** -- Unbinds both back buttons - -**_onBackClick() - Back navigation:** -- Calls `ViewManager.instance.back()` to return to PageWriteLevels - -### List Building - -**_buildList() - Build preview list:** -1. Clears existing list: `_clearList()` -2. Gets params from ViewManager: - - `selectedIndices` - Array of level indices selected by user - - `shareTitle` - User-entered challenge title -3. Updates title label if provided -4. Logs selected level indices -5. Updates content height based on item count -6. For each selected level index: - - Creates item node: `_createItem(displayIndex)` - - Adds to content node - - Stores in `_itemNodes` - - Async loads data: `_loadLevelData(itemNode, levelIndex, displayIndex)` -7. Scrolls to top: `scrollView.scrollToTop(0)` - -**_clearList() - Clear existing items:** -- Destroys all nodes in `_itemNodes` -- Empties array - -**_updateContentSize(count) - Set list height:** -- Calculates total height: - ``` - = PADDING_TOP - + count * ITEM_HEIGHT - + (count - 1) * SPACING_Y - + PADDING_TOP - ``` -- Sets UITransform contentSize - -**_createItem(displayIndex) - Create single preview item:** -1. Instantiates template node -2. Sets active = true -3. Calculates y position (vertical list, negative y downward): - ``` - y = -(PADDING_TOP + displayIndex * (ITEM_HEIGHT + SPACING_Y) + ITEM_HEIGHT / 2) - ``` -4. Sets position and returns node - -**_loadLevelData(item, levelIndex, displayIndex) - Load level data:** -1. Async loads config: `LevelDataManager.ensureLevelReady(levelIndex)` -2. Fills item components: - - **LevelCover** (Sprite) ← spriteFrame - - **Tips1** (Label) ← `线索一:{clue1}` - - **Tips2** (Label) ← `线索二:{clue2}` - - **Answer** (Label) ← `答案:{answer}` - -### Data Managers Used -- **LevelDataManager** - Load level configs for preview -- **ViewManager** - Navigate back - -### Parameters (from PageWriteLevels) -```typescript -{ - selectedIndices: number[], // [0, 5, 10, 15, 20, 25] - 6 level indices - shareTitle: string // User-entered title -} -``` - -### Flow -PageHome → PageWriteLevels [Select 6 levels] → PagePreviewLevels [Verify] → Share/Back - -### Notes -- Uses vertical scrolling list with dynamic layout -- Displays all level data in preview format -- Title is customizable by user -- Soft loading pattern (preview page shows cached/async-loaded data) - ---- - -## 6. **PassModal.ts** - Level Completion Modal - -**Location:** `assets/prefabs/PassModal.ts` - -**Class:** `PassModal extends BaseView` - -### Purpose -- Modal dialog shown after level is completed successfully -- Provides two action buttons: - - "Next Level" - Progress to next level - - "Share to Friends" - Share challenge via WeChat -- Shows level completion confirmation -- Plays success sound - -### Interface Definition -```typescript -export interface PassModalCallbacks { - onNextLevel?: () => void; // Callback when next level clicked - onShare?: () => void; // Callback when share clicked -} -``` - -### Lifecycle Methods - -**onViewLoad() - First-time initialization:** -- Calls `_bindButtonEvents()` to attach button listeners - -**onViewShow() - Page shown:** -1. Calls `_updateWidget()` to set modal to full screen size -2. Calls `_playSuccessSound()` to play completion audio - -**onViewDestroy() - Cleanup:** -- Calls `_unbindButtonEvents()` to remove listeners - -### UI Properties -``` -@property(Node) nextLevelButton - "Next Level" button -@property(Node) shareButton - "Share" button -@property(Label) tipLabel - Optional tip label (e.g., "+1 life") -@property(AudioClip) successAudio - Completion sound effect -``` - -### Static Constants -```typescript -static readonly MODAL_Z_INDEX = 999 // Layer index for topmost display -``` - -### Internal State -``` -_callbacks: PassModalCallbacks - Button callbacks -_screenSize: Size | null - Cached screen dimensions -``` - -### Methods - -**setCallbacks(callbacks: PassModalCallbacks) - Set button callbacks:** -- Stores callbacks for next level and share events - -**_updateWidget() - Full-screen sizing:** -1. Gets visible screen size: `view.getVisibleSize()` -2. Caches size in `_screenSize` (avoid repeated calculations) -3. Sets UITransform content size to match screen - -**_bindButtonEvents() - Attach listeners:** -``` -nextLevelButton → TOUCH_END → _onNextLevelClick() -shareButton → TOUCH_END → _onShareClick() -``` - -**_unbindButtonEvents() - Remove listeners:** -- Checks `isValid` before removing (node may be destroyed) -- Removes both button listeners - -**_playSuccessSound() - Play completion audio:** -- Gets AudioSource component -- Plays `successAudio` one-shot - -**_onNextLevelClick() - Next level button:** -- Calls callback: `_callbacks.onNextLevel?.()` -- Usually triggers level progression in PageLevel - -**_onShareClick() - Share button:** -1. Calls `WxSDK.shareAppMessage()` with parameters: - ``` - title: '快来一起玩这款游戏吧' (Come play this game!) - query: `level={levelIndex}` - ``` -2. Calls callback: `_callbacks.onShare?.()` -3. Modal remains open (user can continue or close) - -### Data Managers Used -- **WxSDK** - Share app message to WeChat - -### Parameters (from PageLevel) -```typescript -{ - levelIndex: number // Current level index (1-based for display) -} -``` - -### Flow -Level Completed → Show PassModal → [Next Level] → Continue game - → [Share] → Share to WeChat, stay in modal - → Close → Return to PageLevel - -### Notes -- Modal stays open after share (user can click next level without closing) -- Uses full screen size with z-index 999 for modal effect -- Callbacks allow decoupling from page logic -- Screen size cached to avoid repeated view queries - ---- - -## 7. **Toast.ts** - Toast Notification Component - -**Location:** `assets/prefabs/Toast.ts` - -**Class:** `Toast extends Component` - -### Purpose -- Individual toast notification display component -- Shows brief notification message with fade-out animation -- Auto-destroys after display duration -- Supports custom duration - -### Lifecycle Methods - -**onLoad() - Initialization:** -1. Gets or creates UIOpacity component for fade animation -2. Stores reference in `_uiOpacity` - -### UI Properties -``` -@property(Label) contentLabel - Text label for message content -``` - -### Internal State -``` -_uiOpacity: UIOpacity | null - Component for opacity animation -``` - -### Methods - -**show(content: string, duration: number = 2000) - Display toast:** -1. Sets label text: `contentLabel.string = content` -2. Resets opacity to full: `_uiOpacity.opacity = 255` -3. Schedules fade-out after duration (converted to seconds): - ``` - scheduleOnce(_fadeOut, duration / 1000) - ``` - -**_fadeOut() - Fade-out animation:** -1. Tweens opacity from 255 → 0 over 0.3 seconds -2. On completion: destroys node -3. Uses Cocos tween system for smooth animation - -### Data Flow -ToastManager.show("message") → Creates Toast instance → Toast.show() → Display + Schedule fade → Destroy - -### Features -- Default display time: 2000ms (2 seconds) -- Fade duration: 300ms -- Auto-destroy after fade complete -- UIOpacity component for animation support - ---- - -## 8. **ToastManager.ts** - Toast Management Utility - -**Location:** `assets/scripts/utils/ToastManager.ts` - -**Class:** `ToastManager` (Singleton) - -### Purpose -- Singleton manager for centralized toast notification handling -- Creates and displays Toast instances in a container -- Provides static convenience methods for showing messages -- Initializes toast system once, uses throughout app - -### Singleton Pattern -```typescript -static get instance(): ToastManager -// Lazy instantiation on first access -``` - -### Lifecycle - -**init(prefab: Prefab, container?: Node) - Initialize manager:** -- Stores toast prefab reference -- Sets container (defaults to Canvas if not provided) -- Should be called once in main.ts during app initialization - -**show(content: string, duration?: number) - Show toast:** -1. Validates prefab and container are initialized -2. Instantiates toast prefab -3. Adds to container as child -4. Gets Toast component from node -5. Calls `Toast.show(content, duration)` to display - -### Static Methods -```typescript -static show(content: string, duration?: number = 2000) -// Convenience method: ToastManager.show("message") -``` - -### Internal State -``` -_prefab: Prefab | null - Toast prefab (set during init) -_container: Node | null - Parent node for toast instances -``` - -### Data Managers Used -- None (utility class) - -### Usage Example -```typescript -// In ToastManager initialization (main.ts) -ToastManager.instance.init(toastPrefab, canvasNode); - -// Anywhere in app -ToastManager.show("积分不足!", 2000); -``` - -### Flow -ToastManager.init() [once] → ToastManager.show() [many times] -Each show() → instantiate → add to container → display → fade → destroy - ---- - -## 9. **BaseView.ts** - Base View Class - -**Location:** `assets/scripts/core/BaseView.ts` - -**Class:** `BaseView extends Component` - -### Purpose -- Abstract base class for all pages/views in the game -- Defines page lifecycle interface for consistent behavior -- Manages view state (showing, parameters) -- Handles Cocos lifecycle integration - -### Interface Definition -```typescript -export interface ViewConfig { - prefab: Prefab; // Prefab asset reference - cache?: boolean; // Cache instance (true = reuse, false = destroy) - zIndex?: number; // Layer depth (higher = on top) -} - -export interface ViewOptions { - params?: any; // Data to pass to page - onComplete?: (view: BaseView) => void; // Success callback - onError?: (err: Error) => void; // Error callback -} -``` - -### Properties - -**Public:** -``` -viewId: string - Unique identifier -config: ViewConfig - Configuration from ViewManager -isShowing: boolean - Currently visible flag -_params: any - View parameters -``` - -### Lifecycle Methods (To Override) - -**onViewLoad() - First-time initialization:** -- Called when page is first created -- Initialize UI, bind events, load data -- Default: empty implementation - -**onViewShow() - Page becomes visible:** -- Called when page is opened or shown -- Update UI based on current state -- Default: empty implementation - -**onViewHide() - Page becomes hidden:** -- Called when page is closed or covered -- Pause animations, release resources -- Default: empty implementation - -**onViewDestroy() - Page destruction:** -- Called when page is being destroyed -- Cleanup resources, remove listeners -- Default: empty implementation - -### Public Methods - -**setParams(params: any) - Store view parameters:** -- Stores data passed from caller -- Used before showing view - -**getParams(): any - Retrieve view parameters:** -- Returns stored params -- Used by view to read initialization data - -### Internal Methods (Called by ViewManager) - -**_doShow() - Execute show logic:** -1. Check if already showing (prevent double-show) -2. Set `isShowing = true` -3. Set `node.active = true` -4. Call `onViewShow()` - -**_doHide() - Execute hide logic:** -1. Check if already hidden -2. Set `isShowing = false` -3. Call `onViewHide()` -4. Set `node.active = false` - -**_doDestroy() - Execute destroy logic:** -1. Mark as destroyed (prevent double-call) -2. Call `node.destroy()` - -### Cocos Lifecycle Integration - -**onDestroy() - Cocos lifecycle (called by engine):** -1. Check if not already marked destroyed -2. If currently showing: call `onViewHide()` -3. Call `onViewDestroy()` - -### Internal State -``` -_destroyed: boolean - Destroyed flag (prevent double-call) -``` - -### Usage Pattern -```typescript -@ccclass('PageExample') -export class PageExample extends BaseView { - onViewLoad(): void { - // Initialize UI - } - - onViewShow(): void { - // Page shown - } - - onViewHide(): void { - // Page hidden - } - - onViewDestroy(): void { - // Cleanup - } -} -``` - -### Data Managers Used -- None (base class provides only framework) - -### Inheritance Hierarchy -- PageHome extends BaseView -- PageLevel extends BaseView -- PagePreviewLevels extends BaseView -- PassModal extends BaseView - ---- - -## 10. **ViewManager.ts** - View/Page Management System - -**Location:** `assets/scripts/core/ViewManager.ts` - -**Class:** `ViewManager` (Singleton) - -### Purpose -- Centralized page management system -- Handles: - - Page registration and configuration - - Page lifecycle (create, show, hide, destroy) - - Navigation (open, back, replace) - - Page stacking (history navigation) - - Instance caching for reusable pages - - Preloading support - -### Singleton Pattern -```typescript -static get instance(): ViewManager -// Lazy-instantiated on first access -``` - -### Data Structures - -**Registered Views:** -```typescript -_registeredViews: Map -// Maps viewId → configuration -``` - -**View Stack:** -```typescript -_viewStack: BaseView[] -// LIFO stack for navigation history -``` - -**View Cache:** -```typescript -_viewCache: Map -// Stores instances when cache: true in config -``` - -### Lifecycle Methods - -**init(container: Node) - Initialize manager:** -- Sets container node (usually Canvas) -- Called once in main.ts during startup -- Must be called before opening any pages - -### Registration Methods - -**register(viewId: string, config: ViewConfig) - Register single page:** -1. Validates not already registered (logs error if duplicate) -2. Applies defaults: - ``` - cache: true (default) - zIndex: 0 (default) - ``` -3. Stores in `_registeredViews` - -**registerAll(views: Record) - Batch register:** -- Calls `register()` for each view - -### Page Opening - -**open(viewId: string, options?: ViewOptions) - Open page:** -1. Validates container initialized -2. Validates page registered -3. Checks cache: - - If cached instance exists and valid → calls `_showView()` - - Otherwise → calls `_instantiateView()` - -**_instantiateView(viewId, prefab, options) - Create new instance:** -1. Instantiates prefab: `instantiate(prefab)` -2. Gets BaseView component from node -3. Validates component exists (logs error if missing) -4. Sets view properties: - - `viewId` - page identifier - - `config` - page configuration - - params from options -5. Sets z-index: `setSiblingIndex(config.zIndex)` -6. Adds to container -7. Calls `onViewLoad()` on page -8. Caches if `config.cache === true` -9. Calls `_showView()` to display - -**_showView(view, options) - Display page:** -1. Gets current view: `getCurrentView()` -2. If different view showing → calls `_doHide()` on it -3. Updates params if provided -4. Adds to stack if not already there: `_viewStack.push(view)` -5. Calls `view._doShow()` to activate -6. Calls completion callback: `options.onComplete?.(view)` - -### Navigation - -**back() - Return to previous page:** -- Alias for `close()` -- Pops stack, hides current, shows previous - -**close(options?: {destroy?: boolean}) - Close current page:** -1. Pops view from stack -2. Determines if should destroy: - ``` - shouldDestroy = options?.destroy ?? !currentView.config?.cache - ``` - (Default: destroy if not cached) -3. Calls `_hideAndDestroyView()` -4. Gets next view from stack -5. If exists → calls `_doShow()` on it - -**replace(viewId: string, options?: ViewOptions) - Replace current page:** -1. Gets current view from top of stack -2. Pops and removes from stack -3. Destroys based on cache setting -4. Opens new page: `open(viewId, options)` - -### Utility Methods - -**_hideAndDestroyView(view, shouldDestroy) - Hide and optionally destroy:** -1. Calls `view._doHide()` to hide -2. If `shouldDestroy`: - - Removes from cache: `_viewCache.delete()` - - Calls `view._doDestroy()` to destroy node - -**getCurrentView(): BaseView | null - Get active page:** -- Returns top of stack (or null if empty) - -**getViewStack(): BaseView[] - Get copy of stack:** -- Returns [..._viewStack] (copy prevents external modification) - -**clearAll() - Clear all pages:** -1. Pops and destroys all stack pages -2. Destroys all cached pages -3. Clears caches - -### Preloading - -**preload(viewId, onProgress?, onComplete?) - Preload page:** -- Note: Main bundle assets already loaded, so this is mostly for compatibility -- Immediately callbacks with 100% progress and complete - -**preloadAll(viewIds, onProgress?, onComplete?) - Batch preload:** -- Preloads all provided view IDs -- Accumulates progress (0-1) -- Calls complete when all done - -### Data Flow - -**Opening First Page (from PageLoading):** -``` -PageLoading.start() - → ViewManager.open('PageHome') - → _instantiateView('PageHome', prefab) - → instantiate prefab - → getComponent(BaseView) - → call onViewLoad() - → cache instance - → _showView() - → push to stack - → call _doShow() - → node.active = true - → onViewShow() -``` - -**Navigating Between Pages:** -``` -PageHome.startGameBtn.click() - → ViewManager.open('PageLevel') - → _instantiateView or _showView (if cached) - → _showView() - → getCurrentView() = PageHome - → PageHome._doHide() - → push PageLevel to stack - → PageLevel._doShow() -``` - -**Going Back:** -``` -PageLevel.onIconSettingClick() - → ViewManager.back() - → close() - → pop from stack = PageLevel - → PageLevel._doHide() - → destroy or cache PageLevel - → getCurrentView() = PageHome - → PageHome._doShow() -``` - -### Stack Example - -**Session Flow:** -``` -Initial: [] - -After loading PageHome: -[PageHome] - -Open PageLevel from PageHome: -[PageHome, PageLevel] - -Open PassModal (modal, not in flow typically): -[PageHome, PageLevel, PassModal?] - or modal might use different system - -Back from PageLevel: -[PageHome] - -Open PageWriteLevels: -[PageHome, PageWriteLevels] - -Open PagePreviewLevels: -[PageHome, PageWriteLevels, PagePreviewLevels] - -Back from PagePreviewLevels: -[PageHome, PageWriteLevels] - -Back from PageWriteLevels: -[PageHome] -``` - -### Caching Behavior - -**Cached Pages (cache: true):** -- Instance reused on re-open -- `onViewLoad()` only called once (first open) -- `onViewShow()` called each time opened -- Memory stays allocated - -**Non-Cached Pages (cache: false):** -- New instance created each time -- `onViewLoad()` called each time -- Full cleanup on close -- Memory released - -### Error Handling -- Validates container initialized before operations -- Validates page registered before opening -- Validates BaseView component exists on prefab -- Logs errors (using Cocos `error()` function) -- Returns via error callback if provided - -### Data Managers Used -- None (infrastructure only) - ---- - -## Summary Table - -| File | Class | Purpose | Extends | Key Features | -|------|-------|---------|---------|--------------| -| main.ts | main | App initialization | Component | Initialize ViewManager, register pages | -| PageLoading.ts | PageLoading | Splash/loading screen | Component | Load data, sync progress, handle share code | -| PageHome.ts | PageHome | Landing page | BaseView | Navigation to game/PK, WeChat setup | -| PageLevel.ts | PageLevel | Game level | BaseView | Game mechanics, timer, hints, scoring | -| PagePreviewLevels.ts | PagePreviewLevels | Level preview | BaseView | Scrollable list, level verification | -| PassModal.ts | PassModal | Completion modal | BaseView | Next level/share buttons, success sound | -| Toast.ts | Toast | Notification | Component | Message display, fade animation | -| ToastManager.ts | ToastManager | Toast system | Singleton | Centralized toast management | -| BaseView.ts | BaseView | Page base class | Component | Lifecycle interface, parameter passing | -| ViewManager.ts | ViewManager | Page management | Singleton | Navigation, stacking, caching | - ---- - -## Game Flow Map - -``` -┌─────────────────────────────────────────────────────────────┐ -│ main.ts (Initialize) │ -│ Initialize ViewManager + Register All Pages │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ PageLoading.ts (Splash) │ -│ • Load AuthManager + LevelDataManager (parallel) │ -│ • Sync server progress │ -│ • Check for share code (WeChat) │ -│ • Preload PageHome │ -└──────────────────────┬──────────────────────────────────────┘ - │ - ┌──────────────┼──────────────┐ - │ Normal Mode │ Share Mode │ - ▼ ▼ │ - ┌────────────────────────────┐ │ - │ PageHome.ts (Landing) │ │ - │ [Start Game] [PK/Create] │ │ - └──┬──────────────────────┬──┘ │ - │ │ │ - ▼ ▼ │ - ┌─────────────┐ ┌──────────────────┐ - │ PageLevel.ts│ │PageWriteLevels.ts│ (not shown) - │ (Game) │ │ (Create PK) │ - │ • Clues │ │ • Select 6 │ - │ • Timer │ │ • Input title │ - │ • Score │ └──────┬───────────┘ - │ • Submit │ │ - └──┬──────────┘ ▼ - │ ┌──────────────────┐ - │ │PagePreviewLevels │ - │ │ (Verify/Share) │ - │ └──────┬───────────┘ - │ │ - ├─────────────────────┤ - ▼ ▼ - ┌──────────────────────────────────┐ - │ PassModal.ts (Complete) │ - │ [Next Level] [Share to Friends]│ - │ • Earn points │ - │ • Play success sound │ - └──┬───────────────────────────────┘ - │ [Next Level] - ▼ or [Back] - (Loop to next level) -``` - ---- - -## Data Manager Integration - -``` -┌─────────────────────────────────────────────────────────┐ -│ Data Managers (Not in this analysis) │ -├─────────────────────────────────────────────────────────┤ -│ • AuthManager - User login, completedLevelIds │ -│ • LevelDataManager - Level configs, preloading │ -│ • StorageManager - Local progress, points │ -│ • UserAssetsManager - Point consumption/earning │ -│ • ShareManager - Share code, challenge data │ -│ • WxSDK - WeChat API, vibration, sharing │ -│ • ToastManager - Notifications │ -│ • ViewManager - Page navigation │ -└─────────────────────────────────────────────────────────┘ - -Used By Each Component: -┌────────────────────────────────────────────────┐ -│ PageLoading │ -│ • AuthManager.initialize() │ -│ • LevelDataManager.initialize() │ -│ • StorageManager.getMaxUnlockedLevelIndex() │ -│ • StorageManager.onLevelCompleted() │ -│ • ShareManager.joinShare() │ -│ • WxSDK.getShareCodeFromLaunch() │ -│ • ViewManager.preload/open() │ -└────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────┐ -│ PageLevel │ -│ • LevelDataManager (load, preload) │ -│ • StorageManager (progress, points) │ -│ • UserAssetsManager (consume, earn) │ -│ • ShareManager (share mode, report progress) │ -│ • WxSDK (vibration, sharing) │ -│ • ToastManager (notifications) │ -│ • ViewManager (navigation) │ -└────────────────────────────────────────────────┘ - -┌────────────────────────────────────────────────┐ -│ PagePreviewLevels │ -│ • LevelDataManager.ensureLevelReady() │ -│ • ViewManager.back() │ -└────────────────────────────────────────────────┘ -``` - ---- - -## Key Concepts - -### 1. **View Lifecycle Pattern** -- **onViewLoad**: First-time initialization -- **onViewShow**: Shown/activated -- **onViewHide**: Hidden/covered -- **onViewDestroy**: Destroyed/cleaned up - -### 2. **Caching Strategy** -- Pages can be cached (reused) or destroyed after close -- Cached pages call onViewLoad() once, onViewShow() multiple times -- Default: cache=true for most pages - -### 3. **Page Stacking** -- Stack-based navigation (LIFO) -- Each open() adds to stack -- back() pops from stack -- replace() swaps top of stack - -### 4. **Points/Scoring System** -- Earn points on level completion: based on time spent -- Consume points to unlock hints -- Points stored locally in StorageManager -- Synced from server on app start - -### 5. **Hint System** -- 3 hints per level -- Hint 1: always free -- Hints 2-3: point consumption -- Prevents double-unlock with flag - -### 6. **Timer Mechanic** -- 60-second countdown per level -- Displayed in clock label -- Affects points earned (faster = more points) -- Plays sound on timeout - -### 7. **Share Challenge Mode** -- Different game flow from normal mode -- Uses ShareManager instead of LevelDataManager -- Reports progress back to server -- Returns to home on completion - -### 8. **Toast Notification System** -- Singleton manager with prefab pool -- Supports custom duration -- Fade-out animation -- Auto-destroys after display -