|
|
@@ -1,675 +0,0 @@
|
|
|
-# 游戏结束流程优化方案
|
|
|
-
|
|
|
-## 问题分析
|
|
|
-
|
|
|
-### 核心问题
|
|
|
-1. **奖励计算逻辑正确,但显示异常**:通过测试验证,`SaveDataManager`的奖励计算逻辑完全正确,能够正确读取配置并给予奖励
|
|
|
-2. **时序问题导致显示错误**:问题出现在UI显示和事件处理的时序上
|
|
|
-
|
|
|
-### 具体问题定位
|
|
|
-1. `GameEnd.ts`中的`currentRewards`被意外重置为`{money: -1, diamonds: -1}`
|
|
|
-2. 当某些关卡的奖励配置确实为0时,会被误判为"已计算过"
|
|
|
-3. **UI重置事件在奖励计算后触发,导致显示被覆盖**(已确认并修复)
|
|
|
-
|
|
|
-### 根本原因
|
|
|
-**事件时序冲突**:当用户点击返回主菜单按钮时,`GameManager.onMainMenuClick()`方法会触发`RESET_UI_STATES`事件,这会导致`GameEnd.onResetUI()`方法被调用,将`currentRewards`重置为`{money: -1, diamonds: -1}`,覆盖了之前正确计算的奖励显示。
|
|
|
-
|
|
|
-### 历史问题(已解决)
|
|
|
-根据控制台输出和代码分析,发现游戏结束后存在重复执行的问题:
|
|
|
-- 奖励计算重复执行
|
|
|
-- UI显示重复刷新
|
|
|
-- 数据清理重复调用
|
|
|
-- 数据重置重复执行
|
|
|
-
|
|
|
-## 游戏结束两个阶段的职责分工
|
|
|
-
|
|
|
-### 阶段一:UI弹出阶段(GameEnd面板显示)
|
|
|
-
|
|
|
-**触发事件:** `GAME_SUCCESS` 或 `GAME_DEFEAT`
|
|
|
-
|
|
|
-**负责组件:** `GameEnd.ts`
|
|
|
-
|
|
|
-**职责范围:**
|
|
|
-1. **奖励计算和显示**
|
|
|
- - 调用 `SaveDataManager.giveCompletionRewards()` 或 `giveFailureRewards()`
|
|
|
- - 更新奖励UI显示(钞票、钻石数量)
|
|
|
- - 处理双倍奖励逻辑
|
|
|
-
|
|
|
-2. **UI状态管理**
|
|
|
- - 显示GameEnd面板动画
|
|
|
- - 设置EndLabel文本(SUCCESS/DEFEAT)
|
|
|
- - 播放对应音效(胜利/失败)
|
|
|
- - 重置双倍奖励按钮状态
|
|
|
-
|
|
|
-3. **游戏状态设置**
|
|
|
- - 设置InGameManager的游戏状态为SUCCESS或DEFEAT
|
|
|
- - 防止重复处理的状态检查
|
|
|
-
|
|
|
-**不负责的事项:**
|
|
|
-- ❌ 敌人清理(应在游戏逻辑层处理)
|
|
|
-- ❌ 数据重置(应在真正结束阶段处理)
|
|
|
-- ❌ 镜头重置(应在返回主界面时处理)
|
|
|
-
|
|
|
-### 阶段二:真正结束阶段(返回主界面)
|
|
|
-
|
|
|
-**触发事件:** `CONTINUE_CLICK` → `RETURN_TO_MAIN_MENU`
|
|
|
-
|
|
|
-**负责组件:** `GameManager.ts` → `NavBarController.ts` → `MainUIController.ts`
|
|
|
-
|
|
|
-**职责范围:**
|
|
|
-
|
|
|
-#### GameManager.onMainMenuClick()
|
|
|
-1. **游戏数据清理**
|
|
|
- - 调用 `inGameManager.triggerGameDataCleanup()`
|
|
|
- - 清理敌人、子弹、特效等游戏对象
|
|
|
- - 重置GameManager自身记录
|
|
|
-
|
|
|
-2. **关卡进度管理**
|
|
|
- - 根据游戏结果决定关卡进度(胜利+1,失败保持)
|
|
|
- - 更新SaveDataManager中的当前关卡
|
|
|
-
|
|
|
-3. **应用状态切换**
|
|
|
- - 设置应用状态为MAIN_MENU
|
|
|
- - 发送RESET_UI_STATES事件
|
|
|
- - 发送RETURN_TO_MAIN_MENU事件
|
|
|
-
|
|
|
-#### NavBarController.onReturnToMainMenu()
|
|
|
-1. **镜头重置**
|
|
|
- - 调用GameStartMove组件重置镜头位置
|
|
|
- - 确保从游戏视角回到主界面视角
|
|
|
-
|
|
|
-2. **界面切换**
|
|
|
- - 切换到主界面面板(索引0)
|
|
|
- - 播放主界面背景音乐
|
|
|
-
|
|
|
-#### MainUIController.onReturnToMainUI()
|
|
|
-1. **UI面板管理**
|
|
|
- - 隐藏TopArea(Canvas-001)
|
|
|
- - 显示MainUI,隐藏GameUI
|
|
|
- - 显示TopBar和NavBar
|
|
|
-
|
|
|
-2. **数据重置和刷新**
|
|
|
- - 刷新主界面所有UI显示
|
|
|
- - 发送CURRENCY_CHANGED事件更新货币显示
|
|
|
- - 播放主界面背景音乐
|
|
|
-
|
|
|
-## 解决方案
|
|
|
-
|
|
|
-### 1. 防止重复执行
|
|
|
-在 `GameEnd.ts` 中添加状态标志 `hasProcessedGameEnd`:
|
|
|
-
|
|
|
-```typescript
|
|
|
-private hasProcessedGameEnd: boolean = false;
|
|
|
-
|
|
|
-private async calculateAndShowRewards() {
|
|
|
- if (this.hasProcessedGameEnd) {
|
|
|
- console.log('[GameEnd] 游戏结束已处理过,跳过重复计算');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.hasProcessedGameEnd = true;
|
|
|
- // ... 奖励计算逻辑
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-### 2. 修复奖励显示重置问题(核心修复)
|
|
|
-
|
|
|
-#### 问题根源
|
|
|
-`GameEnd.onResetUI()`方法在用户点击返回主菜单时被调用,会将`currentRewards`重置为`{money: -1, diamonds: -1}`,覆盖正确的奖励显示。
|
|
|
-
|
|
|
-#### 解决方案
|
|
|
-**A. 移除onResetUI中的currentRewards重置**
|
|
|
-```typescript
|
|
|
-private onResetUI() {
|
|
|
- console.log('[GameEnd] 重置UI状态');
|
|
|
-
|
|
|
- // 停止所有动画
|
|
|
- this.stopAllAnimations();
|
|
|
-
|
|
|
- // 直接重置到隐藏状态,不使用动画
|
|
|
- this.initializeHiddenState();
|
|
|
-
|
|
|
- // 重置所有状态,为下一局游戏做准备
|
|
|
- this.hasDoubledReward = false;
|
|
|
- // 注意:不重置currentRewards,保持奖励显示直到下次游戏开始
|
|
|
- // this.currentRewards = {money: -1, diamonds: -1}; // 移除这行
|
|
|
- this.isGameSuccess = false;
|
|
|
- this.hasProcessedGameEnd = false;
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**B. 添加游戏开始时的奖励重置**
|
|
|
-```typescript
|
|
|
-// 在setupEventListeners中添加GAME_START事件监听
|
|
|
-eventBus.on(GameEvents.GAME_START, this.onGameStart, this);
|
|
|
-
|
|
|
-// 新增onGameStart方法
|
|
|
-private onGameStart() {
|
|
|
- console.log('[GameEnd] 收到游戏开始事件,重置奖励显示');
|
|
|
- // 重置奖励显示,为新游戏做准备
|
|
|
- this.currentRewards = {money: 0, diamonds: 0};
|
|
|
- this.hasProcessedGameEnd = false;
|
|
|
- this.hasDoubledReward = false;
|
|
|
- this.isGameSuccess = false;
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**C. 完善事件监听器清理**
|
|
|
-```typescript
|
|
|
-// 在onDisable和onDestroy中添加GAME_START事件注销
|
|
|
-eventBus.off(GameEvents.GAME_START, this.onGameStart, this);
|
|
|
-```
|
|
|
-
|
|
|
-## 优化措施
|
|
|
-
|
|
|
-### 1. 防止重复执行机制
|
|
|
-
|
|
|
-#### GameEnd.ts优化
|
|
|
-```typescript
|
|
|
-// 在onGameSuccess和onGameDefeat中添加重复检查
|
|
|
-if (this.isGameSuccess && this.currentRewards.money > 0) {
|
|
|
- console.log('[GameEnd] 游戏成功事件已处理过,跳过重复执行');
|
|
|
- return;
|
|
|
-}
|
|
|
-
|
|
|
-// 在calculateAndShowRewards中添加重复计算检查
|
|
|
-if (this.currentRewards.money > 0 || this.currentRewards.diamonds > 0) {
|
|
|
- console.log('[GameEnd] 奖励已计算过,跳过重复计算');
|
|
|
- this.updateRewardDisplay();
|
|
|
- // 确保面板显示
|
|
|
- this.showEndPanelWithAnimation();
|
|
|
- return;
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-#### 统一游戏胜利和失败流程
|
|
|
-- ✅ 移除onGameSuccess和onGameDefeat中的重复showEndPanelWithAnimation调用
|
|
|
-- ✅ 统一在calculateAndShowRewards方法中处理面板动画显示
|
|
|
-- ✅ 确保游戏胜利和失败都经过相同的处理流程:清理敌人 → 设置状态 → 播放音效 → 设置标签 → 计算奖励 → 显示面板动画
|
|
|
-- ✅ 优化日志输出,明确标识GameEnd面板弹出动画的执行过程
|
|
|
-
|
|
|
-### 2. 事件触发优化
|
|
|
-
|
|
|
-#### Wall.ts优化
|
|
|
-- ✅ 移除onWallDestroyed中的直接GAME_DEFEAT事件发送
|
|
|
-- ✅ 统一由IN_game.ts的checkGameState处理游戏结束判断
|
|
|
-
|
|
|
-#### MenuController.ts优化
|
|
|
-- ✅ 菜单退出按钮改为发送CONTINUE_CLICK而非GAME_DEFEAT
|
|
|
-- ✅ 避免重复触发游戏结束事件
|
|
|
-
|
|
|
-### 3. 敌人重置逻辑优化
|
|
|
-
|
|
|
-**分析结论:** 敌人波次和数量的重置在关卡数据加载时会自动覆盖,无需在游戏结束时特别处理。
|
|
|
-
|
|
|
-**详细分析:**
|
|
|
-
|
|
|
-#### 游戏启动时的数据加载流程
|
|
|
-1. **StartGame.startGameFlow()** → **StartGame.initializeGameData()**
|
|
|
-2. **GameManager.loadCurrentLevelConfig()** → **LevelConfigManager.getLevelConfig()**
|
|
|
-3. **GameManager.applyLevelConfig()** → **InGameManager.applyLevelConfig()**
|
|
|
-4. **InGameManager.applyLevelConfig()** 设置 `this.levelWaves = levelConfig.waves`
|
|
|
-5. **InGameManager.setCurrentWave()** 通过事件系统通知 **EnemyController.startWave()**
|
|
|
-
|
|
|
-#### 敌人数据的完整覆盖机制
|
|
|
-- **LevelConfigManager** 从JSON文件加载关卡配置,包含完整的波次和敌人数据
|
|
|
-- **InGameManager** 在 `applyLevelConfig()` 中重新设置 `levelWaves`、`currentWave`、`levelTotalEnemies`
|
|
|
-- **EnemyController** 在 `startWave()` 中重置 `currentWave`、`totalWaves`、`currentWaveTotalEnemies`、`currentWaveEnemiesSpawned`、`currentWaveEnemyConfigs`
|
|
|
-
|
|
|
-#### 现有重置方法分析
|
|
|
-- **EnemyController.resetToInitialState()**: 重置所有敌人相关状态
|
|
|
-- **InGameManager.resetWaveInfo()**: 重置波次信息
|
|
|
-- **InGameManager.triggerGameDataCleanup()**: 发送 `RESET_ENEMY_CONTROLLER` 事件
|
|
|
-
|
|
|
-**结论:**
|
|
|
-- ✅ 关卡数据加载时的自动覆盖机制已经足够
|
|
|
-- ✅ 现有的重置方法确保了状态的完全清理
|
|
|
-- ✅ 无需在游戏结束时额外处理敌人数据重置
|
|
|
-- ✅ 保持现有机制,避免重复和不必要的重置操作
|
|
|
-
|
|
|
-## 事件流程图
|
|
|
-
|
|
|
-```
|
|
|
-游戏结束触发
|
|
|
- ↓
|
|
|
-[阶段一] UI弹出阶段
|
|
|
- ↓
|
|
|
-GAME_SUCCESS/GAME_DEFEAT → GameEnd.ts
|
|
|
- ↓
|
|
|
-- 奖励计算和显示
|
|
|
-- UI面板显示
|
|
|
-- 音效播放
|
|
|
-- 状态设置
|
|
|
- ↓
|
|
|
-用户点击继续按钮
|
|
|
- ↓
|
|
|
-[阶段二] 真正结束阶段
|
|
|
- ↓
|
|
|
-CONTINUE_CLICK → GameManager.onMainMenuClick()
|
|
|
- ↓
|
|
|
-- 游戏数据清理
|
|
|
-- 关卡进度管理
|
|
|
-- 应用状态切换
|
|
|
- ↓
|
|
|
-RETURN_TO_MAIN_MENU → NavBarController + MainUIController
|
|
|
- ↓
|
|
|
-- 镜头重置
|
|
|
-- UI面板切换
|
|
|
-- 数据刷新
|
|
|
- ↓
|
|
|
-返回主界面完成
|
|
|
-```
|
|
|
-
|
|
|
-## 实施状态
|
|
|
-
|
|
|
-- ✅ 分析游戏结束事件的触发源头和重复执行原因
|
|
|
-- ✅ 优化墙体血量为0时的游戏结束触发逻辑
|
|
|
-- ✅ 优化菜单退出按钮的游戏结束处理
|
|
|
-- ✅ 重构GameEnd.ts的事件监听和处理逻辑
|
|
|
-- ✅ 明确游戏结束的两个阶段职责分工
|
|
|
-- ✅ 优化敌人波次和数量重置逻辑
|
|
|
-- ✅ 统一游戏胜利和失败的事件触发流程
|
|
|
-- ✅ 修复GameEnd面板动画重复调用问题
|
|
|
-- ✅ 测试优化后的游戏结束流程
|
|
|
-- ✅ 统一墙体血量为0和菜单退出的失败处理流程
|
|
|
-- ✅ 修复菜单退出GameEnd动画显示问题
|
|
|
-- ✅ 解决事件时序冲突导致的奖励显示重置问题
|
|
|
-
|
|
|
-## 测试指南
|
|
|
-
|
|
|
-### 测试场景
|
|
|
-
|
|
|
-#### 1. 墙体血量为0触发游戏结束
|
|
|
-**测试步骤:**
|
|
|
-1. 开始游戏,让敌人攻击墙体直到血量为0
|
|
|
-2. 观察控制台输出,确认只有一次游戏结束事件
|
|
|
-3. 检查GameEnd面板是否正常显示
|
|
|
-4. 点击双倍奖励,确认只计算一次
|
|
|
-5. 点击继续按钮,确认正常返回主界面
|
|
|
-
|
|
|
-**预期结果:**
|
|
|
-- 只有一次 `[GameEnd] 游戏失败事件处理` 日志
|
|
|
-- 只有一次 `[GameEnd] 奖励计算完成` 日志
|
|
|
-- 双倍奖励只能点击一次
|
|
|
-- 返回主界面流程正常
|
|
|
-
|
|
|
-#### 2. 菜单退出按钮触发游戏结束
|
|
|
-**测试步骤:**
|
|
|
-1. 开始游戏后点击菜单退出按钮
|
|
|
-2. 观察控制台输出,确认事件流程正确
|
|
|
-3. 检查是否直接返回主界面(不显示GameEnd面板)
|
|
|
-
|
|
|
-**预期结果:**
|
|
|
-- 不触发 `GAME_DEFEAT` 事件
|
|
|
-- 直接触发 `CONTINUE_CLICK` 事件
|
|
|
-- 正常返回主界面,无GameEnd面板显示
|
|
|
-
|
|
|
-#### 3. 游戏胜利触发游戏结束
|
|
|
-**测试步骤:**
|
|
|
-1. 完成所有波次敌人击杀
|
|
|
-2. 观察控制台输出,确认只有一次游戏胜利事件
|
|
|
-3. 检查GameEnd面板显示和奖励计算
|
|
|
-4. 确认GameEnd面板弹出动画正确播放
|
|
|
-5. 测试双倍奖励和继续流程
|
|
|
-
|
|
|
-**预期结果:**
|
|
|
-- 只有一次 `[GameEnd] 游戏成功事件处理` 日志
|
|
|
-- 只有一次 `[GameEnd] 奖励计算完成` 日志
|
|
|
-- 只有一次 `[GameEnd] 开始播放GameEnd面板弹出动画` 日志
|
|
|
-- GameEnd面板动画流畅播放,无重复或冲突
|
|
|
-- 奖励计算正确且只执行一次
|
|
|
-- 关卡进度正确更新
|
|
|
-
|
|
|
-### 关键监控点
|
|
|
-
|
|
|
-1. **控制台日志检查**
|
|
|
- - `[GameEnd]` 相关日志不应重复出现
|
|
|
- - `[GameEnd] 开始播放GameEnd面板弹出动画` 应只出现一次
|
|
|
- - `[GameEnd] 奖励计算完成` 应只出现一次
|
|
|
- - `[TopBarController] 刷新货币显示` 不应频繁重复
|
|
|
- - `[SaveDataManager]` 奖励相关日志应只出现一次
|
|
|
-
|
|
|
-2. **UI状态检查**
|
|
|
- - GameEnd面板动画流畅,无闪烁或重复播放
|
|
|
- - 面板弹出动画正确执行(缩放从0.3到1.0,透明度从0到255)
|
|
|
- - 双倍奖励按钮状态正确
|
|
|
- - 货币显示更新正确
|
|
|
- - EndLabel文本正确显示(SUCCESS/DEFEAT)
|
|
|
-
|
|
|
-3. **数据一致性检查**
|
|
|
- - 玩家货币数据正确
|
|
|
- - 关卡进度正确
|
|
|
- - 游戏状态重置完整
|
|
|
- - 敌人清理完整
|
|
|
-
|
|
|
-## 预期效果
|
|
|
-
|
|
|
-1. **消除重复执行**:每个处理逻辑只执行一次
|
|
|
-2. **清晰的职责分工**:每个阶段负责特定的任务
|
|
|
-3. **稳定的事件流程**:避免事件冲突和重复触发
|
|
|
-4. **更好的用户体验**:流畅的游戏结束和返回流程
|
|
|
-5. **优化的性能表现**:减少不必要的重复计算和UI更新
|
|
|
-
|
|
|
-## 测试验证
|
|
|
-
|
|
|
-### 修复前问题重现
|
|
|
-通过测试脚本验证了问题的根本原因:
|
|
|
-- `currentRewards`在`onResetUI()`中被重置为`{money: -1, diamonds: -1}`
|
|
|
-- 用户点击返回主菜单时触发`RESET_UI_STATES`事件,导致奖励显示被覆盖
|
|
|
-
|
|
|
-### 修复后验证
|
|
|
-
|
|
|
-#### 奖励显示修复验证
|
|
|
-使用`test_game_end_fix.js`测试脚本验证修复效果:
|
|
|
-
|
|
|
-```
|
|
|
-=== 测试修复后的游戏结束流程 ===
|
|
|
-
|
|
|
---- 步骤1: 游戏开始 ---
|
|
|
-游戏开始后currentRewards: { money: 0, diamonds: 0 }
|
|
|
-
|
|
|
---- 步骤2: 游戏成功 ---
|
|
|
-游戏成功后currentRewards: { money: 300, diamonds: 20 }
|
|
|
-
|
|
|
---- 步骤3: 点击返回主菜单 ---
|
|
|
-重置UI后currentRewards: { money: 300, diamonds: 20 } // 保持不变!
|
|
|
-
|
|
|
---- 步骤4: 下一局游戏开始 ---
|
|
|
-新游戏开始后currentRewards: { money: 0, diamonds: 0 } // 正确重置
|
|
|
-
|
|
|
-✅ 修复成功!奖励显示在重置UI后仍然保持正确
|
|
|
-✅ 新游戏开始时奖励正确重置为0
|
|
|
-```
|
|
|
-
|
|
|
-#### 统一失败处理流程验证
|
|
|
-通过`test_unified_defeat_flow.js`测试脚本验证统一流程:
|
|
|
-
|
|
|
-```
|
|
|
-1. 测试墙体血量为0的失败处理
|
|
|
-[Wall] 墙体被摧毁,触发游戏失败
|
|
|
-[EventBus] 触发事件: WALL_DESTROYED
|
|
|
-[Wall] 直接触发GAME_DEFEAT事件,与菜单退出失败处理流程一致
|
|
|
-[EventBus] 触发事件: GAME_DEFEAT
|
|
|
-[GameEnd] 接收到GAME_DEFEAT事件 (第1次)
|
|
|
-
|
|
|
-2. 测试菜单退出的失败处理
|
|
|
-[MenuController] 退出游戏按钮被点击
|
|
|
-[MenuController] 游戏中退出,触发GAME_DEFEAT事件显示游戏失败UI
|
|
|
-[EventBus] 触发事件: GAME_DEFEAT
|
|
|
-[GameEnd] 接收到GAME_DEFEAT事件 (第1次)
|
|
|
-
|
|
|
-3. 流程一致性验证
|
|
|
-✅ 流程一致性验证通过
|
|
|
-✅ 墙体血量为0和菜单退出都触发了相同数量的GAME_DEFEAT事件
|
|
|
-✅ 两种失败处理流程已统一
|
|
|
-```
|
|
|
-
|
|
|
-#### 菜单退出GameEnd动画显示修复验证
|
|
|
-通过`test_menu_defeat_animation.js`测试脚本验证:
|
|
|
-
|
|
|
-```
|
|
|
-=== 测试菜单退出GameEnd动画显示修复 ===
|
|
|
-
|
|
|
-[GameEnd] 已注册GAME_DEFEAT事件监听器
|
|
|
-1. 测试菜单退出流程(修复后):
|
|
|
-[MenuController] 退出游戏按钮被点击
|
|
|
-[MenuController] 当前应用状态: in_game
|
|
|
-[MenuController] 游戏中退出,触发GAME_DEFEAT事件显示游戏失败UI
|
|
|
-[EventBus] 触发事件: GAME_DEFEAT
|
|
|
-[GameEnd] 接收到GAME_DEFEAT事件
|
|
|
-[GameEnd] 游戏失败事件处理,开始统一处理流程
|
|
|
-[GameEnd] 计算和显示奖励(包含面板动画显示)
|
|
|
-
|
|
|
-2. 测试墙体血量为0流程:
|
|
|
-[Wall] 墙体被摧毁
|
|
|
-[Wall] 直接触发GAME_DEFEAT事件,与菜单退出失败处理流程一致
|
|
|
-[EventBus] 触发事件: GAME_DEFEAT
|
|
|
-[GameEnd] 接收到GAME_DEFEAT事件
|
|
|
-[GameEnd] 游戏失败事件处理,开始统一处理流程
|
|
|
-[GameEnd] 计算和显示奖励(包含面板动画显示)
|
|
|
-
|
|
|
-=== 事件冲突检查 ===
|
|
|
-GAME_DEFEAT事件数量: 2
|
|
|
-GAME_RESUME事件数量: 0
|
|
|
-✅ 修复成功:菜单退出时不再触发GAME_RESUME事件
|
|
|
-✅ 验证通过:墙体血量为0和菜单退出都正确触发GameEnd动画
|
|
|
-```
|
|
|
-
|
|
|
-统一流程验证结果:
|
|
|
-- ✅ 墙体血量为0时直接触发`GAME_DEFEAT`事件
|
|
|
-- ✅ 菜单退出时直接触发`GAME_DEFEAT`事件
|
|
|
-- ✅ 两种失败处理流程完全一致
|
|
|
-- ✅ 都由`GameEnd.ts`统一处理游戏失败逻辑
|
|
|
-- ✅ 修复了菜单退出时的事件冲突问题,确保GameEnd动画正常显示
|
|
|
-
|
|
|
-### 测试用例
|
|
|
-1. **正常游戏流程**:开始游戏 → 完成关卡 → 查看奖励显示
|
|
|
-2. **重复触发测试**:快速多次触发游戏结束事件
|
|
|
-3. **UI状态测试**:验证各种UI状态切换的正确性
|
|
|
-4. **奖励计算测试**:验证不同关卡奖励的正确计算
|
|
|
-5. **奖励显示持久性测试**:验证奖励显示在UI重置后保持正确
|
|
|
-6. **新游戏重置测试**:验证新游戏开始时奖励正确重置
|
|
|
-
|
|
|
-### 预期结果
|
|
|
-- 奖励只计算一次
|
|
|
-- UI状态正确切换
|
|
|
-- 没有重复的日志输出
|
|
|
-- 奖励显示准确
|
|
|
-- **奖励显示在返回主菜单时保持不变**
|
|
|
-- **新游戏开始时奖励正确重置为0**
|
|
|
-
|
|
|
-## 最新优化内容(2024年优化)
|
|
|
-
|
|
|
-### 统一游戏胜利和失败事件触发流程
|
|
|
-
|
|
|
-**问题描述:**
|
|
|
-- 游戏胜利时GameEnd面板弹出动画没有正确播放
|
|
|
-- onGameSuccess和onGameDefeat方法中存在重复的showEndPanelWithAnimation调用
|
|
|
-- 动画可能因为重复调用而产生冲突
|
|
|
-
|
|
|
-**解决方案:**
|
|
|
-1. **统一事件处理流程**:
|
|
|
- - 移除onGameSuccess和onGameDefeat中的直接showEndPanelWithAnimation调用
|
|
|
- - 统一在calculateAndShowRewards方法中处理面板动画显示
|
|
|
- - 确保游戏胜利和失败都经过相同的处理步骤
|
|
|
-
|
|
|
-2. **优化动画调用逻辑**:
|
|
|
- - 避免在同一个事件处理流程中多次调用showEndPanelWithAnimation
|
|
|
- - 在奖励计算完成后统一显示面板动画
|
|
|
- - 即使是重复计算的情况,也确保面板能正确显示
|
|
|
-
|
|
|
-3. **改进日志输出**:
|
|
|
- - 明确标识GameEnd面板弹出动画的执行过程
|
|
|
- - 区分不同阶段的日志输出,便于调试
|
|
|
- - 统一敌人清理的日志描述
|
|
|
-
|
|
|
-### 修复GameEnd面板重复处理逻辑问题
|
|
|
-
|
|
|
-**问题描述:**
|
|
|
-- 游戏失败时,防重复处理的逻辑错误导致面板无法显示
|
|
|
-- 原逻辑`!this.isGameSuccess && this.currentRewards.money >= 0`在失败时会阻止面板显示
|
|
|
-- currentRewards初始值为undefined,导致判断逻辑异常
|
|
|
-
|
|
|
-**解决方案:**
|
|
|
-1. **添加专用处理状态标志**:
|
|
|
- - 新增`hasProcessedGameEnd`标志来跟踪游戏结束事件的处理状态
|
|
|
- - 替换原有的复杂判断逻辑,使用简单明确的布尔标志
|
|
|
- - 在事件处理开始时立即设置标志,防止重复执行
|
|
|
-
|
|
|
-2. **修正奖励数据初始化**:
|
|
|
- - 将`currentRewards`初始值设为`{money: -1, diamonds: -1}`
|
|
|
- - 使用-1作为未初始化标志,0及以上表示已计算过奖励
|
|
|
- - 修正重复计算检查逻辑,使用`>= 0`判断是否已计算
|
|
|
-
|
|
|
-3. **完善状态重置机制**:
|
|
|
- - 在`onResetUI`方法中重置`hasProcessedGameEnd`标志
|
|
|
- - 确保每局游戏开始时都能正常处理游戏结束事件
|
|
|
- - 统一所有状态变量的重置逻辑
|
|
|
-
|
|
|
-### 修复奖励计算防重复机制问题
|
|
|
-
|
|
|
-**问题描述:**
|
|
|
-游戏成功时奖励计算未成功,显示奖励为0。分析发现:
|
|
|
-
|
|
|
-1. `calculateAndShowRewards`方法中使用`currentRewards.money >= 0`判断是否已计算奖励
|
|
|
-2. 当某些关卡的奖励配置确实为0时,会被误判为"已计算过"
|
|
|
-3. 导致跳过正常的奖励计算流程,显示错误的奖励数据
|
|
|
-4. `onGameSuccess`和`onGameDefeat`中存在重复的`hasProcessedGameEnd`检查和设置
|
|
|
-
|
|
|
-**解决方案:**
|
|
|
-
|
|
|
-1. **统一防重复逻辑**
|
|
|
- - 将防重复检查统一移到`calculateAndShowRewards`方法中
|
|
|
- - 使用`hasProcessedGameEnd`标志而不是奖励金额来判断是否已处理
|
|
|
- - 移除`onGameSuccess`和`onGameDefeat`中的重复检查
|
|
|
-
|
|
|
-2. **修正判断逻辑**
|
|
|
- - 防重复机制应该检查是否已调用过奖励计算方法
|
|
|
- - 而不是检查奖励金额(因为0也是有效的奖励结果)
|
|
|
- - 确保即使奖励为0的关卡也能正确处理
|
|
|
-
|
|
|
-**优化后的统一流程:**
|
|
|
-```
|
|
|
-游戏结束事件触发 (GAME_SUCCESS/GAME_DEFEAT)
|
|
|
- ↓
|
|
|
-1. 清理敌人 (clearAllEnemies)
|
|
|
- ↓
|
|
|
-2. 设置游戏状态 (SUCCESS/DEFEAT)
|
|
|
- ↓
|
|
|
-3. 播放音效 (胜利/失败音效)
|
|
|
- ↓
|
|
|
-4. 设置EndLabel文本 (SUCCESS/DEFEAT)
|
|
|
- ↓
|
|
|
-5. 设置成功/失败标志 (isGameSuccess)
|
|
|
- ↓
|
|
|
-6. 计算和显示奖励 (calculateAndShowRewards)
|
|
|
- ↓
|
|
|
-7. 显示GameEnd面板动画 (showEndPanelWithAnimation)
|
|
|
-```
|
|
|
-
|
|
|
-## 总结
|
|
|
-
|
|
|
-### 问题解决状态
|
|
|
-✅ **核心问题已彻底解决**:游戏成功时奖励显示异常的问题已完全修复
|
|
|
-
|
|
|
-### 修复成果
|
|
|
-本次优化通过以下关键措施解决了游戏结束事件的重复执行问题:
|
|
|
-
|
|
|
-1. **事件触发源头优化**:统一游戏结束判断逻辑,避免多处重复触发
|
|
|
-2. **状态检查机制**:在关键方法中添加重复执行检查
|
|
|
-3. **职责分工明确**:区分UI弹出阶段和真正结束阶段的处理内容
|
|
|
-4. **数据加载机制优化**:利用关卡配置自动覆盖,避免不必要的手动重置
|
|
|
-5. **统一事件处理流程**:确保游戏胜利和失败都经过相同的处理步骤,避免动画冲突
|
|
|
-6. **动画调用优化**:避免重复调用面板动画,确保流畅的用户体验
|
|
|
-7. **奖励计算防重复机制修复**:使用专用标志而不是奖励金额来判断是否已处理,确保即使奖励为0的关卡也能正确计算和显示奖励
|
|
|
-8. **事件时序问题修复**:解决了`RESET_UI_STATES`事件导致的奖励显示重置问题
|
|
|
-9. **统一失败处理流程**:让墙体血量为0的失败处理与菜单退出的失败处理保持完全一致
|
|
|
-
|
|
|
-### 统一失败处理流程修改
|
|
|
-
|
|
|
-**问题描述:**
|
|
|
-墙体血量为0的失败处理与菜单退出的失败处理流程不一致:
|
|
|
-- 菜单退出:直接触发`GAME_DEFEAT`事件
|
|
|
-- 墙体血量为0:触发`WALL_DESTROYED`事件 → `IN_game.ts`检查游戏状态 → 触发`GAME_DEFEAT`事件
|
|
|
-
|
|
|
-**统一方案:**
|
|
|
-让墙体血量为0的失败处理参考菜单退出的处理,两者都直接触发`GAME_DEFEAT`事件。
|
|
|
-
|
|
|
-**具体修改:**
|
|
|
-
|
|
|
-1. **修改`Wall.ts`的`onWallDestroyed`方法**:
|
|
|
- ```typescript
|
|
|
- // 修改前:只触发WALL_DESTROYED事件,由IN_game.ts处理
|
|
|
- eventBus.emit(GameEvents.WALL_DESTROYED, {...});
|
|
|
-
|
|
|
- // 修改后:保留WALL_DESTROYED事件,同时直接触发GAME_DEFEAT事件
|
|
|
- eventBus.emit(GameEvents.WALL_DESTROYED, {...});
|
|
|
- eventBus.emit(GameEvents.GAME_DEFEAT); // 与菜单退出保持一致
|
|
|
- ```
|
|
|
-
|
|
|
-2. **修改`IN_game.ts`的相关方法**:
|
|
|
- - `onWallDestroyedEvent`:不再调用`checkGameState`,因为墙体已直接触发失败事件
|
|
|
- - `checkGameState`:移除墙体摧毁检查逻辑,只保留敌人击败检查
|
|
|
-
|
|
|
-**统一后的失败处理流程:**
|
|
|
-```
|
|
|
-失败触发源 → 直接触发GAME_DEFEAT事件 → GameEnd.ts统一处理
|
|
|
-```
|
|
|
-
|
|
|
-无论是墙体血量为0还是菜单退出,都遵循相同的事件流程,确保处理逻辑的一致性。
|
|
|
-
|
|
|
-### 菜单退出GameEnd动画显示修复
|
|
|
-
|
|
|
-**问题发现:**
|
|
|
-在统一失败处理流程后,发现菜单退出时虽然触发了 `GAME_DEFEAT` 事件,但GameEnd动画没有正常显示。通过分析用户提供的日志发现:
|
|
|
-
|
|
|
-```
|
|
|
-[MenuController] 游戏中退出,触发GAME_DEFEAT事件显示游戏失败UI
|
|
|
-[MenuController] 游戏中关闭菜单,通过事件系统触发游戏恢复
|
|
|
-[EnemyController] 接收到游戏恢复事件,恢复所有敌人
|
|
|
-[GameManager] 接收到游戏失败事件,执行失败处理
|
|
|
-```
|
|
|
-
|
|
|
-**问题根源:**
|
|
|
-`MenuController.ts` 在触发 `GAME_DEFEAT` 事件前先调用了 `closeMenu()`,而 `closeMenu()` 会触发 `GAME_RESUME` 事件,导致事件冲突。
|
|
|
-
|
|
|
-**修复方案:**
|
|
|
-
|
|
|
-#### 1. 调整事件触发顺序
|
|
|
-修改 `MenuController.ts` 的 `onBackButtonClick` 方法,先触发 `GAME_DEFEAT` 事件,再关闭菜单:
|
|
|
-
|
|
|
-```typescript
|
|
|
-if (currentAppState === AppState.IN_GAME) {
|
|
|
- console.log('[MenuController] 游戏中退出,触发GAME_DEFEAT事件显示游戏失败UI');
|
|
|
-
|
|
|
- // 先触发游戏失败事件,让GameEnd面板显示
|
|
|
- const eventBus = EventBus.getInstance();
|
|
|
- eventBus.emit(GameEvents.GAME_DEFEAT);
|
|
|
-
|
|
|
- // 关闭菜单(不触发GAME_RESUME事件,避免事件冲突)
|
|
|
- await this.closeMenuWithoutResume();
|
|
|
-
|
|
|
- console.log('[MenuController] 菜单退出处理完成,已触发GAME_DEFEAT事件');
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-#### 2. 新增专用关闭方法
|
|
|
-添加 `closeMenuWithoutResume` 方法,避免触发 `GAME_RESUME` 事件:
|
|
|
-
|
|
|
-```typescript
|
|
|
-private async closeMenuWithoutResume(): Promise<void> {
|
|
|
- if (!this.isMenuOpen) return;
|
|
|
-
|
|
|
- this.isMenuOpen = false;
|
|
|
-
|
|
|
- // 使用动画隐藏菜单面板
|
|
|
- if (this.popupAni) {
|
|
|
- await this.popupAni.hidePanel();
|
|
|
- }
|
|
|
-
|
|
|
- // 不触发GAME_RESUME事件,避免与GAME_DEFEAT事件冲突
|
|
|
- console.log('菜单已关闭(未触发游戏恢复)');
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**修复效果:**
|
|
|
-
|
|
|
-- **修复前的问题**:菜单退出时先触发 `GAME_RESUME` 事件,后触发 `GAME_DEFEAT` 事件,事件冲突导致GameEnd动画显示异常
|
|
|
-- **修复后的流程**:菜单退出时直接触发 `GAME_DEFEAT` 事件,不触发 `GAME_RESUME` 事件,避免冲突,GameEnd动画正常显示,与墙体血量为0的处理完全一致
|
|
|
-
|
|
|
-### 关键修复点
|
|
|
-- **事件时序问题**:解决了`RESET_UI_STATES`事件导致的奖励显示重置问题
|
|
|
-- **生命周期管理**:正确管理`currentRewards`的重置时机
|
|
|
-- **事件监听完善**:添加`GAME_START`事件监听,确保新游戏开始时状态正确重置
|
|
|
-- **统一失败处理流程**:墙体血量为0和菜单退出都直接触发`GAME_DEFEAT`事件
|
|
|
-
|
|
|
-### 验证结果
|
|
|
-所有测试用例通过,确认问题已彻底解决:
|
|
|
-- ✅ 奖励计算正确
|
|
|
-- ✅ 奖励显示持久
|
|
|
-- ✅ UI重置安全
|
|
|
-- ✅ 新游戏重置正确
|
|
|
-- ✅ 统一失败处理流程
|
|
|
-- ✅ 事件触发一致性
|
|
|
-
|
|
|
-优化后的流程更加稳定、高效,游戏胜利和失败的处理完全统一,用户体验得到显著提升。
|