- 进入关卡成功后显示 toast 提示消耗体力及剩余体力 - 将 StorageManager 中 UserInfo 接口移至模块顶层,修复嵌套接口语法问题 - WxSDK.getWx() 改为 static 公开方法,便于外部调用
1425 lines
45 KiB
Markdown
1425 lines
45 KiB
Markdown
# 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<string, RegisteredView>
|
|
// Maps viewId → configuration
|
|
```
|
|
|
|
**View Stack:**
|
|
```typescript
|
|
_viewStack: BaseView[]
|
|
// LIFO stack for navigation history
|
|
```
|
|
|
|
**View Cache:**
|
|
```typescript
|
|
_viewCache: Map<string, BaseView>
|
|
// 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<string, ViewConfig>) - 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
|
|
|