import { _decorator, Component, Node, Label, Button, tween, Tween, Vec3, UIOpacity, find } from 'cc'; import { SaveDataManager } from '../LevelSystem/SaveDataManager'; import { InGameManager, GameState } from '../LevelSystem/IN_game'; import EventBus, { GameEvents } from '../Core/EventBus'; import { Audio } from '../AudioManager/AudioManager'; import { AdManager } from '../Ads/AdManager'; const { ccclass, property } = _decorator; /** * 游戏结算界面管理器 * 负责处理游戏结束后的奖励显示和双倍奖励功能 */ @ccclass('GameEnd') export class GameEnd extends Component { // === UI节点引用 === @property({ type: Button, tooltip: '双倍奖励按钮 (Canvas/GameEnd/double)' }) public doubleButton: Button = null; @property({ type: Label, tooltip: '钞票数量显示 ' }) public moneyLabel: Label = null; @property({ type: Label, tooltip: '钻石数量显示 ' }) public diamondLabel: Label = null; @property({ type: Button, tooltip: '继续按钮 (Canvas/GameEnd/Continue)' }) public continueButton: Button = null; @property({ type: InGameManager, tooltip: '游戏管理器组件' }) public inGameManager: InGameManager = null; @property({ type: Label, tooltip: 'EndLabel文本显示 (Canvas/GameEnd/Sprite/EndLabel)' }) public endLabel: Label = null; // === 动画相关属性 === @property({ tooltip: '动画持续时间(秒)' }) public animationDuration: number = 0.3; // 淡入淡出和缩放动画都直接作用于当前节点(Canvas/GameEnd) // 不需要额外的目标节点属性 private _originalScale: Vec3 = new Vec3(); // === 私有属性 === private saveDataManager: SaveDataManager = null; public currentRewards: {money: number, diamonds: number} = {money: -1, diamonds: -1}; private hasDoubledReward: boolean = false; private isGameSuccess: boolean = false; private hasProcessedGameEnd: boolean = false; // 添加处理状态标志 private isAdRequestPending: boolean = false; // 防止重复点击广告期间的状态 // === 弹窗冷却控制 === private static lastPopupTimestamp: number = 0; // 最近一次弹出时间戳 private static readonly popupCooldownMs: number = 1; // 弹窗冷却时长(毫秒) private isInCooldown(): boolean { const now = Date.now(); return (now - GameEnd.lastPopupTimestamp) < GameEnd.popupCooldownMs; } onLoad() { console.log('[GameEnd] onLoad方法被调用'); this.setupEventListeners(); } start() { console.log('[GameEnd] start方法被调用'); // 保存原始缩放值 if (this.node) { this._originalScale.set(this.node.scale); } // 设置事件监听器 this.setupEventListeners(); // 初始化为隐藏状态(但保持节点激活以便接收事件) this.initializeHiddenStateKeepActive(); console.log('[GameEnd] start方法完成,面板已初始化为隐藏状态'); } onEnable() { console.log('[GameEnd] onEnable方法被调用,节点已激活'); // 初始化管理器 this.initializeManagers(); // UI节点已通过装饰器挂载,无需自动查找 // 绑定按钮事件 this.bindButtonEvents(); // 初始化UI状态 this.initializeUI(); // 检查当前游戏状态并处理奖励(解决时序问题) this.checkAndHandleGameState(); } /** * 初始化管理器 */ private initializeManagers() { this.saveDataManager = SaveDataManager.getInstance(); // InGameManager已通过装饰器挂载,无需查找 // 如果UI组件未在编辑器中绑定,尝试自动查找 this.autoFindUIComponents(); } /** * 自动查找UI组件(如果编辑器中未绑定) */ private autoFindUIComponents() { // 自动查找moneyLabel if (!this.moneyLabel) { // 尝试多个可能的路径 const moneyLabelPaths = [ 'ResourceNode/MoneyResourceNode/MoneyLabel', 'Sprite/ResourceNode/MoneyResourceNode/MoneyLabel', 'ResourceNode/MoneyLabel', 'MoneyResourceNode/MoneyLabel' ]; for (const path of moneyLabelPaths) { const moneyLabelNode = this.node.getChildByPath(path); if (moneyLabelNode) { this.moneyLabel = moneyLabelNode.getComponent(Label); if (this.moneyLabel) { console.log(`[GameEnd] 自动找到moneyLabel,路径: ${path}`); break; } } } // 如果还是没找到,尝试递归查找 if (!this.moneyLabel) { this.moneyLabel = this.findLabelByName(this.node, 'MoneyLabel'); if (this.moneyLabel) { console.log('[GameEnd] 通过递归查找找到moneyLabel'); } } } // 自动查找diamondLabel if (!this.diamondLabel) { // 尝试多个可能的路径 const diamondLabelPaths = [ 'ResourceNode/DiamondResourceNode/DiamondLabel', 'Sprite/ResourceNode/DiamondResourceNode/DiamondLabel', 'ResourceNode/DiamondLabel', 'DiamondResourceNode/DiamondLabel' ]; for (const path of diamondLabelPaths) { const diamondLabelNode = this.node.getChildByPath(path); if (diamondLabelNode) { this.diamondLabel = diamondLabelNode.getComponent(Label); if (this.diamondLabel) { console.log(`[GameEnd] 自动找到diamondLabel,路径: ${path}`); break; } } } // 如果还是没找到,尝试递归查找 if (!this.diamondLabel) { this.diamondLabel = this.findLabelByName(this.node, 'DiamondLabel'); if (this.diamondLabel) { console.log('[GameEnd] 通过递归查找找到diamondLabel'); } } } console.log('[GameEnd] UI组件自动查找完成 - moneyLabel:', !!this.moneyLabel, 'diamondLabel:', !!this.diamondLabel); // 如果仍然没有找到组件,打印节点结构用于调试 if (!this.moneyLabel || !this.diamondLabel) { console.warn('[GameEnd] 部分UI组件未找到,打印节点结构用于调试:'); this.printNodeStructure(this.node, 0, 3); // 最多打印3层 } } /** * 递归查找指定名称的Label组件 */ private findLabelByName(node: Node, targetName: string): Label | null { // 检查当前节点 if (node.name === targetName) { const label = node.getComponent(Label); if (label) return label; } // 递归检查子节点 for (const child of node.children) { const result = this.findLabelByName(child, targetName); if (result) return result; } return null; } /** * 打印节点结构用于调试 */ private printNodeStructure(node: Node, depth: number, maxDepth: number) { if (depth > maxDepth) return; const indent = ' '.repeat(depth); const components = node.getComponents(Component).map(c => c.constructor.name).join(', '); console.log(`${indent}${node.name} [${components || 'No Components'}]`); for (const child of node.children) { this.printNodeStructure(child, depth + 1, maxDepth); } } /** * 绑定按钮事件 */ private bindButtonEvents() { if (this.doubleButton) { this.doubleButton.node.on(Button.EventType.CLICK, this.onDoubleButtonClick, this); } if (this.continueButton) { this.continueButton.node.on(Button.EventType.CLICK, this.onContinueButtonClick, this); } } /** * 设置事件监听器 */ private setupEventListeners() { console.log('[GameEnd] 开始设置事件监听器'); const eventBus = EventBus.getInstance(); // 监听游戏成功事件 eventBus.on(GameEvents.GAME_SUCCESS, this.onGameSuccess, this); console.log('[GameEnd] 已注册GAME_SUCCESS事件监听器'); // 监听游戏失败事件 eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeat, this); console.log('[GameEnd] 已注册GAME_DEFEAT事件监听器'); // 监听游戏开始事件 eventBus.on(GameEvents.GAME_START, this.onGameStart, this); console.log('[GameEnd] 已注册GAME_START事件监听器'); // 监听UI重置事件 eventBus.on(GameEvents.RESET_UI_STATES, this.onResetUI, this); console.log('[GameEnd] 事件监听器设置完成'); } /** * 初始化UI状态 */ private initializeUI() { // 重置状态 this.hasDoubledReward = false; this.currentRewards = {money: 0, diamonds: 0}; console.log('[GameEnd] UI状态初始化完成'); } /** * 检查当前游戏状态并处理奖励(解决时序问题) */ private checkAndHandleGameState() { console.log('[GameEnd] 检查当前游戏状态'); if (!this.inGameManager) { console.log('[GameEnd] InGameManager未初始化,无法检查游戏状态'); return; } const currentState = this.inGameManager.getCurrentState(); console.log('[GameEnd] 当前游戏状态:', currentState); // 如果游戏已经结束,主动处理奖励 if (currentState === GameState.SUCCESS) { console.log('[GameEnd] 检测到游戏成功状态,主动处理奖励'); this.isGameSuccess = true; this.calculateAndShowRewards(); } else if (currentState === GameState.DEFEAT) { console.log('[GameEnd] 检测到游戏失败状态,主动处理奖励'); this.isGameSuccess = false; this.calculateAndShowRewards(); } } /** * 处理游戏成功事件 * 统一处理游戏成功逻辑:状态切换 + UI显示 + 奖励计算 */ private onGameSuccess() { console.log('[GameEnd] 接收到GAME_SUCCESS事件'); console.log('[GameEnd] 游戏成功事件处理,开始统一处理流程'); // 冷却期内拦截重复的胜利判定,避免二次弹窗/奖励 if (this.isInCooldown()) { console.log('[GameEnd] 冷却期内,忽略重复的GAME_SUCCESS事件'); return; } // 1. 清理敌人(阶段一:UI弹出阶段的职责) this.clearAllEnemies(); // 2. 设置游戏状态为成功(如果还未设置) if (this.inGameManager && this.inGameManager.getCurrentState() !== GameState.SUCCESS) { this.inGameManager.setCurrentState(GameState.SUCCESS); console.log('[GameEnd] 已将游戏状态切换为SUCCESS'); } // 3. 播放游戏成功音效 Audio.playUISound('data/弹球音效/win'); console.log('[GameEnd] 已播放游戏成功音效'); // 4. 设置EndLabel文本 this.setEndLabelText('SUCCESS'); // 5. 设置成功标志 this.isGameSuccess = true; // 6. 计算和显示奖励(包含面板动画显示) this.calculateAndShowRewards(); } /** * 处理游戏失败事件 * 统一处理游戏失败逻辑:状态切换 + UI显示 + 奖励计算 */ private onGameDefeat() { console.log('[GameEnd] 接收到GAME_DEFEAT事件'); console.log('[GameEnd] 游戏失败事件处理,开始统一处理流程'); // 冷却期内拦截重复的失败判定,避免二次弹窗/奖励 if (this.isInCooldown()) { console.log('[GameEnd] 冷却期内,忽略重复的GAME_DEFEAT事件'); return; } // 1. 清理敌人(阶段一:UI弹出阶段的职责) this.clearAllEnemies(); // 2. 设置游戏状态为失败(如果还未设置) if (this.inGameManager && this.inGameManager.getCurrentState() !== GameState.DEFEAT) { this.inGameManager.setCurrentState(GameState.DEFEAT); console.log('[GameEnd] 已将游戏状态切换为DEFEAT'); } // 3. 播放游戏失败音效 Audio.playUISound('data/弹球音效/lose'); console.log('[GameEnd] 已播放游戏失败音效'); // 4. 设置EndLabel文本 this.setEndLabelText('DEFEAT'); // 5. 设置失败标志 this.isGameSuccess = false; // 6. 计算和显示奖励(包含面板动画显示) this.calculateAndShowRewards(); } /** * 游戏开始处理 */ private onGameStart() { console.log('[GameEnd] 收到游戏开始事件,重置奖励显示'); // 重置奖励显示,为新游戏做准备 this.currentRewards = {money: 0, diamonds: 0}; this.hasProcessedGameEnd = false; this.hasDoubledReward = false; this.isGameSuccess = false; } /** * 计算并显示奖励 */ private async calculateAndShowRewards() { console.log('[GameEnd] 开始计算并显示奖励'); // 防止重复计算奖励(使用专用标志判断) if (this.hasProcessedGameEnd) { console.log('[GameEnd] 游戏结束已处理过,跳过重复计算,直接显示面板'); this.updateRewardDisplay(); // 确保面板显示 this.showEndPanelWithAnimation(); return; } // 标记为已处理,防止重复执行 this.hasProcessedGameEnd = true; if (!this.saveDataManager) { console.error('[GameEnd] SaveDataManager未初始化'); return; } const currentLevel = this.saveDataManager.getCurrentLevel(); console.log(`[GameEnd] 当前关卡: ${currentLevel}, 游戏成功: ${this.isGameSuccess}`); try { if (this.isGameSuccess) { // 游戏成功,先完成关卡(更新maxUnlockedLevel),再给予奖励 console.log('[GameEnd] 准备完成关卡并给予成功奖励'); // 获取游戏时长 const gameTime = this.inGameManager ? Math.floor(this.inGameManager.getGameDuration() / 1000) : 0; console.log(`[GameEnd] 游戏时长: ${gameTime}秒`); // 先调用completeLevel更新maxUnlockedLevel,这样武器解锁机制才能正常工作 this.saveDataManager.completeLevel(currentLevel, 0, gameTime); console.log('[GameEnd] 已完成关卡,maxUnlockedLevel已更新'); // 然后给予奖励 await this.saveDataManager.giveCompletionRewards(currentLevel); console.log('[GameEnd] 已给予成功奖励'); } else { // 游戏失败,给予按比例奖励 const totalWaves = this.inGameManager?.levelWaves?.length || 1; const completedWaves = this.inGameManager ? Math.max(0, this.inGameManager.getCurrentWave() - 1) : 0; console.log(`[GameEnd] 准备给予失败奖励,完成波数: ${completedWaves}/${totalWaves}`); await this.saveDataManager.giveFailureRewards(currentLevel, completedWaves, totalWaves); console.log(`[GameEnd] 已给予失败奖励,完成波数: ${completedWaves}/${totalWaves}`); } // 获取奖励数据并显示 this.currentRewards = this.saveDataManager.getLastRewards(); console.log('[GameEnd] 获取到的奖励数据:', this.currentRewards); console.log('[GameEnd] 奖励计算完成'); this.updateRewardDisplay(); // 显示结算面板(通过动画) this.showEndPanelWithAnimation(); } catch (error) { console.error('[GameEnd] 计算奖励时出错:', error); } } /** * 更新奖励显示 */ private updateRewardDisplay() { console.log(`[GameEnd] 开始更新奖励显示 - 钞票: ${this.currentRewards.money}, 钻石: ${this.currentRewards.diamonds}`); // 检查moneyLabel绑定状态 if (this.moneyLabel) { this.moneyLabel.string = this.currentRewards.money.toString(); console.log(`[GameEnd] 钞票标签已更新: ${this.currentRewards.money}`); } else { console.error('[GameEnd] moneyLabel未绑定!请在编辑器中将Canvas/GameEnd/ResourceNode/MoneyResourceNode/MoneyLabel拖拽到GameEnd组件的moneyLabel属性'); } // 检查diamondLabel绑定状态 if (this.diamondLabel) { this.diamondLabel.string = this.currentRewards.diamonds.toString(); console.log(`[GameEnd] 钻石标签已更新: ${this.currentRewards.diamonds}`); } else { console.error('[GameEnd] diamondLabel未绑定!请在编辑器中将Canvas/GameEnd/ResourceNode/DiamondResourceNode/DiamondLabel拖拽到GameEnd组件的diamondLabel属性'); } console.log(`[GameEnd] 奖励显示更新完成`); } /** * 显示结算面板(通过动画) */ private showEndPanelWithAnimation() { console.log('[GameEnd] 开始显示结算面板动画'); // 若处于冷却期,直接跳过显示,避免重复弹窗 if (this.isInCooldown()) { console.log('[GameEnd] 弹窗处于冷却期,跳过本次显示'); return; } // 确保节点处于激活状态 if (this.node && !this.node.active) { this.node.active = true; console.log('[GameEnd] 激活GameEnd节点'); } // 重置双倍奖励状态 this.hasDoubledReward = false; if (this.doubleButton) { this.doubleButton.interactable = true; } // 确保有UIOpacity组件 if (this.node) { let uiOpacity = this.node.getComponent(UIOpacity); if (!uiOpacity) { uiOpacity = this.node.addComponent(UIOpacity); console.log('[GameEnd] 在showEndPanelWithAnimation中添加UIOpacity组件'); } } // 记录弹窗时间戳(无论是否播放动画,都计入冷却) GameEnd.lastPopupTimestamp = Date.now(); // 播放显示动画(如果有的话) if (this.animationDuration > 0) { console.log(`[GameEnd] 动画持续时间: ${this.animationDuration}秒,开始播放GameEnd面板弹出动画`); this.playShowAnimation(); } else { // 如果没有动画,直接显示 if (this.node) { this.node.setScale(1, 1, 1); const uiOpacity = this.node.getComponent(UIOpacity); if (uiOpacity) { uiOpacity.opacity = 255; } } console.log('[GameEnd] 无动画配置,直接显示面板'); } console.log('[GameEnd] GameEnd面板显示流程完成'); } /** * 播放显示动画 */ private playShowAnimation() { if (!this.node) { console.error('[GameEnd] playShowAnimation: 节点不存在'); return; } console.log('[GameEnd] 开始播放GameEnd面板弹出动画'); // 停止所有正在进行的动画 this.stopAllAnimations(); // 设置节点位置到屏幕中心 this.centerNodeOnScreen(); // 确保有UIOpacity组件 let uiOpacity = this.node.getComponent(UIOpacity); if (!uiOpacity) { uiOpacity = this.node.addComponent(UIOpacity); console.log('[GameEnd] 添加UIOpacity组件'); } // 设置初始状态 this.node.setScale(0, 0, 1); uiOpacity.opacity = 0; console.log('[GameEnd] 设置动画初始状态 - 缩放: 0, 透明度: 0'); // 播放缩放和淡入动画 tween(this.node) .to(this.animationDuration, { scale: new Vec3(1, 1, 1) }, { easing: 'backOut' }) .call(() => { console.log('[GameEnd] GameEnd面板缩放动画完成'); }) .start(); tween(uiOpacity) .to(this.animationDuration, { opacity: 255 }) .call(() => { console.log('[GameEnd] GameEnd面板淡入动画完成'); }) .start(); console.log('[GameEnd] GameEnd面板弹出动画开始执行'); } /** * 将节点居中到屏幕中央 */ private centerNodeOnScreen() { if (!this.node) return; // 获取Canvas节点 const canvas = find('Canvas'); if (!canvas) { console.warn('[GameEnd] 未找到Canvas节点'); return; } // 设置位置为(0, 0),这在Canvas坐标系中是屏幕中心 this.node.setPosition(0, 0, 0); console.log('[GameEnd] 已将面板居中到屏幕中央'); } /** * 双倍按钮点击事件 */ private onDoubleButtonClick() { // 播放UI点击音效 Audio.playUISound('data/弹球音效/ui play'); if (this.hasDoubledReward) { console.log('[GameEnd] 已经获得过双倍奖励'); return; } console.log('[GameEnd] 点击双倍奖励按钮'); // 若广告请求正在进行,拦截重复点击(按钮保持不置灰) if (this.isAdRequestPending) { console.log('[GameEnd] 广告请求进行中,忽略重复点击'); return; } // 标记广告请求进行中 this.isAdRequestPending = true; // 显示激励视频广告 AdManager.getInstance().showRewardedVideoAd( () => { // 广告观看完成,给予双倍奖励 this.giveDoubleReward(); // 广告流程完成,解除请求中状态 this.isAdRequestPending = false; GameEnd.lastPopupTimestamp = Date.now(); }, (error) => { console.error('[GameEnd] 广告显示失败:', error); // 广告失败:不返回主界面,保留当前结算面板供用户选择继续 // 解除请求中状态,按钮不置灰,允许用户再次尝试 this.isAdRequestPending = false; GameEnd.lastPopupTimestamp = Date.now(); } ); } /** * 给予双倍奖励 */ private giveDoubleReward() { if (!this.saveDataManager || this.hasDoubledReward) { return; } // 计算双倍奖励 const doubleMoney = this.currentRewards.money; const doubleDiamonds = this.currentRewards.diamonds; // 添加额外奖励到玩家账户 if (doubleMoney > 0) { this.saveDataManager.addMoney(doubleMoney, 'double_reward'); } if (doubleDiamonds > 0) { this.saveDataManager.addDiamonds(doubleDiamonds, 'double_reward'); } // 更新当前奖励显示(显示双倍后的数值) this.currentRewards.money += doubleMoney; this.currentRewards.diamonds += doubleDiamonds; this.updateRewardDisplay(); // 标记已获得双倍奖励 this.hasDoubledReward = true; // 禁用双倍按钮 if (this.doubleButton) { this.doubleButton.interactable = false; } console.log(`[GameEnd] 双倍奖励已给予 - 额外钞票: ${doubleMoney}, 额外钻石: ${doubleDiamonds}`); // 触发货币变化事件 EventBus.getInstance().emit(GameEvents.CURRENCY_CHANGED); // 不自动返回主页面,不隐藏结算面板;由用户点击“继续”按钮决定返回 } /** * 继续按钮点击事件 */ private onContinueButtonClick() { // 播放UI点击音效 Audio.playUISound('data/弹球音效/ui play'); console.log('[GameEnd] 点击继续按钮'); // 触发返回主菜单事件(奖励动画由主界面统一播放) EventBus.getInstance().emit('CONTINUE_CLICK'); // 隐藏结算面板(通过动画) this.hideEndPanelWithAnimation(); } /** * 隐藏结算面板(通过动画) */ private async hideEndPanelWithAnimation() { // 播放隐藏动画 await this.fadeOutWithScale(); console.log('[GameEnd] 结算面板已通过动画隐藏'); } /** * 重置UI状态 */ 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; // 重置处理状态标志 // 重置按钮状态 if (this.doubleButton) { this.doubleButton.interactable = true; } // 清理广告请求状态,避免跨局残留 this.isAdRequestPending = false; console.log('[GameEnd] UI状态重置完成'); } /** * 设置EndLabel文本 * @param text 要显示的文本内容 */ private setEndLabelText(text: string) { if (this.endLabel) { this.endLabel.string = text; console.log(`[GameEnd] 已设置EndLabel文本为: ${text}`); } else { // 如果endLabel未绑定,尝试通过路径查找 const endLabelNode = find('Canvas/GameEnd/Sprite/EndLabel'); if (endLabelNode) { const labelComponent = endLabelNode.getComponent(Label); if (labelComponent) { labelComponent.string = text; console.log(`[GameEnd] 通过路径查找设置EndLabel文本为: ${text}`); } else { console.warn('[GameEnd] 找到EndLabel节点但无Label组件'); } } else { console.warn('[GameEnd] 未找到EndLabel节点,请检查路径: Canvas/GameEnd/Sprite/EndLabel'); } } } /** * 获取当前奖励信息(用于外部查询) */ public getCurrentRewards(): {money: number, diamonds: number} { return {...this.currentRewards}; } /** * 检查是否已获得双倍奖励 */ public hasGotDoubleReward(): boolean { return this.hasDoubledReward; } // === 动画方法 === /** * 初始化为隐藏状态 */ private initializeHiddenState() { // 设置初始透明度为0 if (this.node) { let uiOpacity = this.node.getComponent(UIOpacity); if (!uiOpacity) { uiOpacity = this.node.addComponent(UIOpacity); console.log('[GameEnd] 添加UIOpacity组件到节点'); } uiOpacity.opacity = 0; } // 设置初始缩放为0(完全隐藏,恢复到历史版本实现) if (this.node) { this.node.setScale(0, 0, 1); } console.log('[GameEnd] 初始化为隐藏状态 - 缩放: 0, 透明度: 0'); } /** * 初始化为隐藏状态但保持节点激活(用于游戏开始时) */ private initializeHiddenStateKeepActive() { // 设置初始透明度为0 if (this.node) { let uiOpacity = this.node.getComponent(UIOpacity); if (!uiOpacity) { uiOpacity = this.node.addComponent(UIOpacity); console.log('[GameEnd] 添加UIOpacity组件到节点'); } uiOpacity.opacity = 0; } // 设置初始缩放为0(完全隐藏,恢复到历史版本实现) if (this.node) { this.node.setScale(0, 0, 1); } console.log('[GameEnd] 初始化为隐藏状态(保持激活) - 缩放: 0, 透明度: 0'); } /** * 淡入动画 */ public fadeIn(target?: Node, duration?: number): Promise { const animTarget = target || this.node; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[GameEnd] fadeIn: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { let uiOpacity = animTarget.getComponent(UIOpacity); if (!uiOpacity) { uiOpacity = animTarget.addComponent(UIOpacity); } Tween.stopAllByTarget(uiOpacity); uiOpacity.opacity = 0; tween(uiOpacity) .to(animDuration, { opacity: 255 }, { easing: 'quadOut' }) .call(() => { resolve(); }) .start(); }); } /** * 淡出动画 */ public fadeOut(target?: Node, duration?: number): Promise { const animTarget = target || this.node; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[GameEnd] fadeOut: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { let uiOpacity = animTarget.getComponent(UIOpacity); if (!uiOpacity) { uiOpacity = animTarget.addComponent(UIOpacity); } Tween.stopAllByTarget(uiOpacity); tween(uiOpacity) .to(animDuration, { opacity: 0 }, { easing: 'quadIn' }) .call(() => { resolve(); }) .start(); }); } /** * 缩放弹出动画 */ public scalePopIn(target?: Node, duration?: number): Promise { const animTarget = target || this.node; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[GameEnd] scalePopIn: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { Tween.stopAllByTarget(animTarget); animTarget.setScale(0, 0, 1); tween(animTarget) .to(animDuration, { scale: this._originalScale }, { easing: 'backOut' }) .call(() => { resolve(); }) .start(); }); } /** * 缩放收缩动画 */ public scalePopOut(target?: Node, duration?: number): Promise { const animTarget = target || this.node; const animDuration = duration !== undefined ? duration : this.animationDuration; if (!animTarget) { console.warn('[GameEnd] scalePopOut: 未指定目标节点'); return Promise.resolve(); } return new Promise((resolve) => { Tween.stopAllByTarget(animTarget); tween(animTarget) .to(animDuration, { scale: new Vec3(0, 0, 1) }, { easing: 'backIn' }) .call(() => { resolve(); }) .start(); }); } /** * 组合动画:淡入 + 缩放弹出 */ public async fadeInWithScale(fadeTarget?: Node, scaleTarget?: Node, duration?: number): Promise { const promises: Promise[] = []; if (fadeTarget || this.node) { promises.push(this.fadeIn(fadeTarget, duration)); } if (scaleTarget || this.node) { promises.push(this.scalePopIn(scaleTarget, duration)); } await Promise.all(promises); } /** * 组合动画:淡出 + 缩放收缩 */ public async fadeOutWithScale(fadeTarget?: Node, scaleTarget?: Node, duration?: number): Promise { const promises: Promise[] = []; if (fadeTarget || this.node) { promises.push(this.fadeOut(fadeTarget, duration)); } if (scaleTarget || this.node) { promises.push(this.scalePopOut(scaleTarget, duration)); } await Promise.all(promises); } /** * 停止所有动画 */ public stopAllAnimations() { if (this.node) { // 停止所有Tween动画 Tween.stopAllByTarget(this.node); // 停止UIOpacity组件的动画 const uiOpacity = this.node.getComponent(UIOpacity); if (uiOpacity) { Tween.stopAllByTarget(uiOpacity); } console.log('[GameEnd] 已停止所有动画'); } else { console.warn('[GameEnd] stopAllAnimations: 节点不存在'); } } /** * 清理所有敌人 * 在游戏结束时调用,符合阶段一UI弹出阶段的职责 */ /** * 通过事件系统清理所有敌人 * 使用CLEAR_ALL_ENEMIES事件,避免直接引用EnemyController */ private clearAllEnemies() { console.log('[GameEnd] 游戏结束,派发CLEAR_ALL_ENEMIES事件清理所有敌人'); try { // 通过事件总线派发清理敌人事件 EventBus.getInstance().emit(GameEvents.CLEAR_ALL_ENEMIES); console.log('[GameEnd] 已派发CLEAR_ALL_ENEMIES事件,统一清理敌人'); } catch (error) { console.error('[GameEnd] 派发清理敌人事件时发生错误:', error); } } /** * 重置所有目标节点到初始状态 */ public resetToInitialState() { this.stopAllAnimations(); if (this.node) { let uiOpacity = this.node.getComponent(UIOpacity); if (uiOpacity) { uiOpacity.opacity = 255; } this.node.setScale(this._originalScale); } } onDisable() { console.log('[GameEnd] onDisable方法被调用,节点已禁用'); // 停止所有动画 this.stopAllAnimations(); // 清理事件监听 const eventBus = EventBus.getInstance(); eventBus.off(GameEvents.GAME_SUCCESS, this.onGameSuccess, this); eventBus.off(GameEvents.GAME_DEFEAT, this.onGameDefeat, this); eventBus.off(GameEvents.GAME_START, this.onGameStart, this); eventBus.off(GameEvents.RESET_UI_STATES, this.onResetUI, this); console.log('[GameEnd] 事件监听器已清理'); } protected onDestroy() { // 停止所有动画 this.stopAllAnimations(); // 清理事件监听 const eventBus = EventBus.getInstance(); eventBus.off(GameEvents.GAME_SUCCESS, this.onGameSuccess, this); eventBus.off(GameEvents.GAME_DEFEAT, this.onGameDefeat, this); eventBus.off(GameEvents.GAME_START, this.onGameStart, this); eventBus.off(GameEvents.RESET_UI_STATES, this.onResetUI, this); // 清理按钮事件 if (this.doubleButton) { this.doubleButton.node.off(Button.EventType.CLICK, this.onDoubleButtonClick, this); } if (this.continueButton) { this.continueButton.node.off(Button.EventType.CLICK, this.onContinueButtonClick, this); } } }