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'; 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}; private hasDoubledReward: boolean = false; private isGameSuccess: boolean = false; onLoad() { console.log('[GameEnd] onLoad方法被调用'); this.setupEventListeners(); } start() { console.log('[GameEnd] start方法被调用'); // 保存原始缩放值 if (this.node) { this._originalScale.set(this.node.scale); } // 初始化为隐藏状态(通过动画实现) this.initializeHiddenState(); // 监听游戏事件 // start方法只在节点首次激活时调用一次 // 如果节点初始状态为false,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事件监听器'); // 监听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] 游戏成功,开始统一处理流程'); // 1. 设置游戏状态为成功(如果还未设置) if (this.inGameManager && this.inGameManager.getCurrentState() !== GameState.SUCCESS) { this.inGameManager.setCurrentState(GameState.SUCCESS); console.log('[GameEnd] 已将游戏状态切换为SUCCESS'); } // 2. 设置EndLabel文本 this.setEndLabelText('SUCCESS'); // 3. 显示UI面板 this.showEndPanelWithAnimation(); // 4. 计算和显示奖励 this.isGameSuccess = true; this.calculateAndShowRewards(); } /** * 处理游戏失败事件 * 统一处理游戏失败逻辑:状态切换 + UI显示 + 奖励计算 */ private onGameDefeat() { console.log('[GameEnd] 接收到GAME_DEFEAT事件'); console.log('[GameEnd] 游戏失败,开始统一处理流程'); console.log('[GameEnd] 当前节点激活状态:', this.node.active); console.log('[GameEnd] moneyLabel绑定状态:', !!this.moneyLabel); console.log('[GameEnd] diamondLabel绑定状态:', !!this.diamondLabel); // 1. 设置游戏状态为失败(如果还未设置) if (this.inGameManager && this.inGameManager.getCurrentState() !== GameState.DEFEAT) { this.inGameManager.setCurrentState(GameState.DEFEAT); console.log('[GameEnd] 已将游戏状态切换为DEFEAT'); } // 2. 播放游戏失败音效 Audio.playUISound('data/弹球音效/lose'); console.log('[GameEnd] 已播放游戏失败音效'); // 3. 设置EndLabel文本 this.setEndLabelText('DEFEAT'); // 4. 显示UI面板 this.showEndPanelWithAnimation(); // 5. 计算和显示奖励 this.isGameSuccess = false; this.calculateAndShowRewards(); } /** * 计算并显示奖励 */ private async calculateAndShowRewards() { console.log('[GameEnd] 开始计算并显示奖励'); if (!this.saveDataManager) { console.error('[GameEnd] SaveDataManager未初始化'); return; } const currentLevel = this.saveDataManager.getCurrentLevel(); console.log(`[GameEnd] 当前关卡: ${currentLevel}, 游戏成功: ${this.isGameSuccess}`); try { if (this.isGameSuccess) { // 游戏成功,给予完整奖励 console.log('[GameEnd] 准备给予成功奖励'); 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); 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() { // 重置双倍奖励状态 this.hasDoubledReward = false; if (this.doubleButton) { this.doubleButton.interactable = true; } // 播放显示动画(如果有的话) if (this.animationDuration > 0) { this.playShowAnimation(); } console.log('[GameEnd] 结算面板已通过动画显示'); } /** * 播放显示动画 */ private playShowAnimation() { if (!this.node) return; // 设置节点位置到屏幕中心 this.centerNodeOnScreen(); // 设置初始状态 this.node.setScale(0.5, 0.5, 1); const uiOpacity = this.node.getComponent(UIOpacity); if (uiOpacity) { uiOpacity.opacity = 0; } // 播放缩放和淡入动画 tween(this.node) .to(this.animationDuration, { scale: new Vec3(1, 1, 1) }, { easing: 'backOut' }) .start(); if (uiOpacity) { tween(uiOpacity) .to(this.animationDuration, { opacity: 255 }) .start(); } console.log('[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] 点击双倍奖励按钮'); // 这里可以添加观看广告的逻辑 // 暂时直接给予双倍奖励 this.giveDoubleReward(); } /** * 给予双倍奖励 */ 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] 点击继续按钮'); // 派发事件给MoneyAni播放奖励动画 const rewards = this.saveDataManager.getLastRewards(); console.log('[GameEnd] 派发奖励动画事件,奖励数据:', rewards); EventBus.getInstance().emit('PLAY_REWARD_ANIMATION', { money: rewards.money, diamonds: rewards.diamonds }); // 触发返回主菜单事件 EventBus.getInstance().emit('CONTINUE_CLICK'); // 隐藏结算面板(通过动画) this.hideEndPanelWithAnimation(); } /** * 隐藏结算面板(通过动画) */ private async hideEndPanelWithAnimation() { // 播放隐藏动画 await this.fadeOutWithScale(); console.log('[GameEnd] 结算面板已通过动画隐藏'); } /** * 重置UI状态 */ private onResetUI() { console.log('[GameEnd] 重置UI状态'); this.hideEndPanelWithAnimation(); this.hasDoubledReward = false; this.currentRewards = {money: 0, diamonds: 0}; } /** * 设置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); } uiOpacity.opacity = 0; } // 设置初始缩放为0 if (this.node) { this.node.setScale(0, 0, 1); } console.log('[GameEnd] 初始化为隐藏状态'); } /** * 淡入动画 */ 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) { const uiOpacity = this.node.getComponent(UIOpacity); if (uiOpacity) { Tween.stopAllByTarget(uiOpacity); } Tween.stopAllByTarget(this.node); } } /** * 重置所有目标节点到初始状态 */ 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.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.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); } } }