// MainUIController.ts import { _decorator, Component, Node, Button, Label, JsonAsset, resources } from 'cc'; import { SaveDataManager } from '../../LevelSystem/SaveDataManager'; import { GameManager, AppState } from '../../LevelSystem/GameManager'; import { GameState } from '../../LevelSystem/IN_game'; import { GameStartMove } from '../../Animations/GameStartMove'; import { TopBarController } from '../TopBarController'; import { MoneyAni } from '../../Animations/MoneyAni'; import EventBus, { GameEvents } from '../../Core/EventBus'; import { Audio } from '../../AudioManager/AudioManager'; import { BundleLoader } from '../../Core/BundleLoader'; import { ResourcePreloader } from '../../Core/ResourcePreloader'; import { NewbieGuideManager } from '../../Core/NewbieGuideManager'; const { ccclass, property } = _decorator; @ccclass('MainUIController') export class MainUIController extends Component { /* 奖励节点 */ @property(Node) rewardMoneyNode: Node = null; // 左钞票奖励 @property(Node) rewardDiamondNode: Node = null; // 右钻石奖励 @property(Label) levelNumberLabel: Label = null; // 第 X 关文本 /* 升级 */ @property(Button) upgradeBtn: Button = null; // 升级按钮 @property(Node) upgradeCostLabel: Node = null; // 消耗钞票数字 @property(Node) upgradeHpLabel: Node = null; // 升级后血量数字 @property(Label) upgradeCurrentHpLabel: Label = null; // 当前血量Label @property(Label) upgradeNextHpLabel: Label = null; // 升级后血量Label @property(Label) upgradeInfoLabel: Label = null; // 升级信息Label(显示"升级墙壁"或"已满级") @property(Node) upgradeArrowNode: Node = null; // 箭头符号节点xxtb /* 墙体配置 */ // @property(JsonAsset) wallConfigAsset: JsonAsset = null; // 墙体配置JSON资源 - 已改为动态加载 /* 主功能按钮 */ @property(Node) battleBtn: Node = null; // 战斗 // 底栏按钮由 NavBarController 统一管理,这里不再需要引用 @property(Node) topArea: Node = null; // Canvas-001/TopArea /* UI节点引用 - 替换find查找 */ @property(Node) mainUI: Node = null; // Canvas/MainUI @property(Node) gameUI: Node = null; // Canvas/GameLevelUI @property(Node) gameManagerNode: Node = null; // Canvas/GameLevelUI/GameManager @property(Node) navBarNode: Node = null; // Canvas/NavBar @property(Node) topBarNode: Node = null; // Canvas/TopBar @property(Node) cameraNode: Node = null; // Canvas/Camera /* 奖励动画系统 */ @property(Node) moneyAniNode: Node = null; // MoneyAni节点 private sdm: SaveDataManager = null; private wallConfig: any = null; private newbieGuideManager: NewbieGuideManager = null; async onLoad () { console.log('[MainUIController] onLoad 开始执行'); this.sdm = SaveDataManager.getInstance(); this.newbieGuideManager = NewbieGuideManager.getInstance(); await this.loadWallConfig(); this.sdm.initialize(); console.log('[MainUIController] SaveDataManager 初始化完成'); this.bindButtons(); this.refreshAll(); // TopArea 默认隐藏,在点击战斗后再显示 if (this.topArea) this.topArea.active = false; // 预加载当前关卡和下一关的资源 this.preloadLevelResources(); // 检查并启动新手引导(如果是新用户) this.newbieGuideManager.checkAndStartNewbieGuideOnSceneLoad(); console.log('[MainUIController] onLoad 执行完成'); } onEnable() { // 移除旧的主界面bgm播放,改为统一入口 // this.playMainUIBGM(); Audio.updateBGMByTopUI(); // 下一帧再次复判,确保返回主界面时稳定播放 ui bgm this.scheduleOnce(() => { Audio.updateBGMByTopUI(); }, 0); } onDisable() { console.log('[MainUIController] MainUI节点隐藏,停止主界面音乐'); // 当MainUI节点隐藏时,停止主界面背景音乐 Audio.stopMusic(); } /* 绑定按钮事件 */ private bindButtons () { console.log('[MainUIController] bindButtons 开始执行'); console.log('[MainUIController] upgradeBtn 状态:', this.upgradeBtn ? '已设置' : '未设置'); console.log('[MainUIController] battleBtn 状态:', this.battleBtn ? '已设置' : '未设置'); // 升级按钮绑定 - upgradeBtn是Button类型 if (this.upgradeBtn) { console.log('[MainUIController] upgradeBtn.node:', this.upgradeBtn.node ? '存在' : '不存在'); this.upgradeBtn.node.on(Button.EventType.CLICK, this.upgradeWallHp, this); console.log('[MainUIController] 升级按钮事件已绑定'); } else { console.error('[MainUIController] upgradeBtn未设置,无法绑定升级事件'); } // 战斗按钮绑定 - battleBtn是Node类型 if (this.battleBtn) { this.battleBtn.on(Button.EventType.CLICK, this.onBattle, this); console.log('[MainUIController] 战斗按钮事件已绑定'); } else { console.error('[MainUIController] battleBtn未设置,无法绑定战斗事件'); } // 监听新手引导的自动战斗事件 EventBus.getInstance().on('NEWBIE_GUIDE_BATTLE_START', this.onNewbieGuideBattle, this); console.log('[MainUIController] 新手引导战斗事件已绑定'); console.log('[MainUIController] bindButtons 执行完成'); } /* ================= 配置加载 ================= */ /** * 加载墙体配置 */ private async loadWallConfig(): Promise { try { const bundleLoader = BundleLoader.getInstance(); const wallData = await bundleLoader.loadDataJson('wall'); if (!wallData) { console.error('[MainUIController] 墙体配置文件内容为空'); return; } this.wallConfig = wallData.json; console.log('[MainUIController] 墙体配置加载成功:', this.wallConfig); // 将配置传递给SaveDataManager this.sdm.setWallConfig(this.wallConfig); } catch (error) { console.error('[MainUIController] 加载墙体配置失败:', error); } } /** * 获取墙体升级费用 */ private getWallUpgradeCost(level: number): number { if (this.wallConfig && this.wallConfig.wallConfig && this.wallConfig.wallConfig.upgradeCosts) { const costs = this.wallConfig.wallConfig.upgradeCosts; return costs[level.toString()] || 0; } return 0; } /** * 获取墙体血量 */ private getWallHealthByLevel(level: number): number { if (this.wallConfig && this.wallConfig.wallConfig && this.wallConfig.wallConfig.healthByLevel) { const healthByLevel = this.wallConfig.wallConfig.healthByLevel; return healthByLevel[level.toString()] || 100; } return 100; } /** * 获取墙体最大等级 */ private getWallMaxLevel(): number { if (this.wallConfig && this.wallConfig.wallConfig && this.wallConfig.wallConfig.maxLevel) { return this.wallConfig.wallConfig.maxLevel; } return 5; } /* ================= 业务逻辑 ================= */ private upgradeWallHp () { // 播放UI点击音效 Audio.playUISound('data/弹球音效/level up 2'); console.log('[MainUIController] 升级墙体按钮被点击'); // 检查SaveDataManager是否初始化 if (!this.sdm) { console.error('[MainUIController] SaveDataManager未初始化'); return; } // 打印当前状态用于调试 const currentMoney = this.sdm.getMoney(); const currentWallLevel = this.sdm.getWallLevel(); const upgradeCost = this.getWallUpgradeCost(currentWallLevel); console.log(`[MainUIController] 当前状态: 钞票=${currentMoney}, 墙体等级=${currentWallLevel}, 升级费用=${upgradeCost}`); // 检查墙体等级是否已达到最大值 const maxLevel = this.getWallMaxLevel(); if (currentWallLevel >= maxLevel) { console.log('[MainUIController] 墙体已达到最大等级'); EventBus.getInstance().emit(GameEvents.SHOW_TOAST, { message: '墙体已达到最大等级', duration: 2.0 }); return; } // 检查钞票是否足够 if (currentMoney < upgradeCost) { EventBus.getInstance().emit(GameEvents.SHOW_RESOURCE_TOAST, { message: `钞票不足,需要${upgradeCost}钞票`, duration: 2.0 }); return; } // 执行升级 if (!this.sdm.spendMoney(upgradeCost)) { console.log('[MainUIController] 扣除钞票失败'); return; } if (!this.sdm.upgradeWallLevel()) { console.log('[MainUIController] 墙体升级失败'); return; } console.log('[MainUIController] 墙体升级成功'); // 刷新所有UI显示 this.refreshAll(); // 通过事件系统通知UI更新 EventBus.getInstance().emit(GameEvents.CURRENCY_CHANGED); // 通知墙体组件更新血量显示 const newLevel = this.sdm.getWallLevel(); const newHealth = this.getWallHealthByLevel(newLevel); EventBus.getInstance().emit(GameEvents.WALL_HEALTH_CHANGED, { previousHealth: 0, currentHealth: newHealth, maxHealth: newHealth }); } private onBattle () { console.log('[MainUIController] onBattle 被调用'); this.executeBattleFlow(); } /** * 新手引导自动触发战斗流程 */ private onNewbieGuideBattle() { console.log('[MainUIController] 新手引导自动触发战斗流程'); this.executeBattleFlow(); } /** * 执行战斗流程的核心逻辑 * 无论是玩家点击战斗按钮还是新手引导自动触发,都使用相同的流程 */ private executeBattleFlow() { console.log('[MainUIController] 执行战斗流程开始'); // 检查是否在新手引导中 const isNewbieGuide = this.newbieGuideManager?.isNewbieGuideInProgress() || false; if (isNewbieGuide) { console.log('[MainUIController] 当前处于新手引导模式'); } // 停止主界面背景音乐,避免与游戏内音效冲突 Audio.stopMusic(); // 战斗音乐现在由InGameManager控制,不在这里播放 console.log('[MainUIController] 战斗音乐将由InGameManager控制播放'); // 显示 TopArea(拖拽引用),避免使用 find() if (this.topArea) this.topArea.active = true; // 若上一关已完成则自动+1 const lvl = this.sdm.getCurrentLevel(); if (this.sdm.isLevelCompleted(lvl)) { const pd = this.sdm.getPlayerData(); pd.currentLevel = lvl + 1; this.sdm.savePlayerData(); } this.refreshLevelNumber(); // 切场景 UI - 使用装饰器引用 if (this.mainUI) this.mainUI.active = false; if (this.gameUI) this.gameUI.active = true; // 在下一帧根据当前顶层UI重新判定并更新BGM,避免同帧内两个onEnable竞争导致误判 this.scheduleOnce(() => { Audio.updateBGMByTopUI(); }, 0); const gm = this.gameManagerNode?.getComponent(GameManager); // 设置应用状态为游戏中(会自动隐藏TopBar和NavBar) gm?.setAppState(AppState.IN_GAME); // 统一使用StartGame启动游戏流程 const eventBus = EventBus.getInstance(); eventBus.emit(GameEvents.GAME_START); gm?.loadCurrentLevelConfig(); // 镜头下移动画现在已集成到StartGame流程中的slideUpFromBottom方法里 console.log('[MainUIController] 执行战斗流程完成'); } /* ================= 刷新 ================= */ private refreshLevelNumber(){ if (this.levelNumberLabel) this.levelNumberLabel.string = `第 ${this.sdm.getCurrentLevel()} 关`; } private refreshAll(){ this.refreshLevelNumber(); this.refreshUpgradeInfo(); this.refreshRewardDisplay(); } /** 刷新奖励显示 - 从JSON配置读取 */ private async refreshRewardDisplay() { const currentLevel = this.sdm.getCurrentLevel(); try { // 从SaveDataManager获取关卡奖励配置 const rewards = await this.sdm.getLevelRewardsFromConfig(currentLevel); if (rewards) { // 更新钞票奖励显示 if (this.rewardMoneyNode) { const coinLabel = this.rewardMoneyNode.getComponent(Label) || this.rewardMoneyNode.getComponentInChildren(Label); if (coinLabel) { coinLabel.string = rewards.money.toString(); } } // 更新钻石奖励显示 if (this.rewardDiamondNode) { const diamondLabel = this.rewardDiamondNode.getComponent(Label) || this.rewardDiamondNode.getComponentInChildren(Label); if (diamondLabel) { diamondLabel.string = rewards.diamonds.toString(); } } } else { console.warn(`无法获取关卡${currentLevel}的奖励配置,使用默认值`); // 使用默认奖励值 this.setDefaultRewards(); } } catch (error) { console.error('刷新奖励显示时出错:', error); this.setDefaultRewards(); } } /** 设置默认奖励值 */ private setDefaultRewards() { // 钞票奖励默认值 if (this.rewardMoneyNode) { const coinLabel = this.rewardMoneyNode.getComponent(Label) || this.rewardMoneyNode.getComponentInChildren(Label); if (coinLabel) { coinLabel.string = '50'; } } // 钻石奖励默认值 if (this.rewardDiamondNode) { const diamondLabel = this.rewardDiamondNode.getComponent(Label) || this.rewardDiamondNode.getComponentInChildren(Label); if (diamondLabel) { diamondLabel.string = '5'; } } } /** 刷新升级信息显示 */ private refreshUpgradeInfo () { console.log('[MainUIController] refreshUpgradeInfo 开始执行'); const costLbl = this.upgradeCostLabel?.getComponent(Label); const hpLbl = this.upgradeHpLabel?.getComponent(Label); const currentLevel = this.sdm.getWallLevel(); const maxLevel = this.getWallMaxLevel(); // 检查是否已达到最大等级 if (currentLevel >= maxLevel) { // 满级状态:显示"已满级",隐藏当前血量和箭头,只显示满级血量 if (this.upgradeInfoLabel) { this.upgradeInfoLabel.string = "已满级"; } // 隐藏当前血量Label和箭头符号 if (this.upgradeCurrentHpLabel) { this.upgradeCurrentHpLabel.node.active = false; } if (this.upgradeArrowNode) { this.upgradeArrowNode.active = false; } // 只显示满级血量 if (this.upgradeNextHpLabel) { const maxHp = this.getWallHealthByLevel(currentLevel); this.upgradeNextHpLabel.string = `${maxHp}`; this.upgradeNextHpLabel.node.active = true; } // 隐藏升级费用 if (costLbl) { costLbl.string = ""; } console.log(`[MainUIController] 满级状态显示: 等级=${currentLevel}, 血量=${this.getWallHealthByLevel(currentLevel)}`); } else { // 非满级状态:正常显示升级信息 if (this.upgradeInfoLabel) { this.upgradeInfoLabel.string = "升级墙壁"; } // 显示升级费用 const cost = this.getWallUpgradeCost(currentLevel); if (costLbl) { costLbl.string = cost.toString(); console.log(`[MainUIController] 升级费用: ${cost}`); } // 显示当前和下一级血量 const currentHp = this.getWallHealthByLevel(currentLevel); const nextHp = this.getWallHealthByLevel(currentLevel + 1); // 显示当前血量Label和箭头符号 if (this.upgradeCurrentHpLabel) { this.upgradeCurrentHpLabel.string = `${currentHp}>>`; this.upgradeCurrentHpLabel.node.active = true; } if (this.upgradeArrowNode) { this.upgradeArrowNode.active = true; } // 显示升级后血量 if (this.upgradeNextHpLabel) { this.upgradeNextHpLabel.string = `${nextHp}`; this.upgradeNextHpLabel.node.active = true; } console.log(`[MainUIController] 升级信息显示: 当前=${currentHp}, 升级后=${nextHp}, 当前等级: ${currentLevel}`); } // 升级按钮始终保持可点击状态,通过Toast显示各种提示 console.log(`[MainUIController] 升级按钮保持可交互状态`); } // 供外部(如 GameManager)调用的公共刷新接口 public updateUI (): void { this.refreshAll(); // 通过事件系统通知UI更新 EventBus.getInstance().emit(GameEvents.CURRENCY_CHANGED); } /** * 游戏失败或成功返回MainUI后的UI状态管理 * 隐藏Canvas-001并显示Canvas/TopBar和Canvas/NavBar */ public onReturnToMainUI(): void { console.log('MainUIController.onReturnToMainUI 开始执行'); // 注意:应用状态已在GameManager中设置,这里不需要重复设置 // 隐藏 TopArea (Canvas-001) if (this.topArea) { this.topArea.active = false; console.log('TopArea (Canvas-001) 已隐藏'); } // 显示主UI if (this.mainUI) { this.mainUI.active = true; console.log('MainUI 已显示'); } // 隐藏游戏UI if (this.gameUI) { this.gameUI.active = false; console.log('GameUI 已隐藏'); } // 显示顶部钱币栏 TopBar if (this.topBarNode) { this.topBarNode.active = true; console.log('TopBar 已显示'); } // 显示底部导航栏 NavBar if (this.navBarNode) { this.navBarNode.active = true; console.log('NavBar 已显示'); } // 不再在这里播放音乐,由onEnable自动处理 // 为防止同帧竞争导致音乐被stop覆盖,下一帧再执行一次复判 this.scheduleOnce(() => { Audio.updateBGMByTopUI(); }, 0); // 刷新UI显示 this.refreshAll(); // 通过事件系统通知UI更新 EventBus.getInstance().emit(GameEvents.CURRENCY_CHANGED); // 返回主界面时重新预加载资源,确保下次进入游戏时资源已准备就绪 this.preloadLevelResources(); console.log('MainUIController.onReturnToMainUI 执行完成'); } /** * 游戏成功返回MainUI并播放奖励动画 */ public onReturnToMainUIWithReward(): void { console.log('MainUIController.onReturnToMainUIWithReward 开始执行'); // 先执行基本的UI状态管理 this.onReturnToMainUI(); // 延迟播放奖励动画,确保UI已经完全显示 this.scheduleOnce(() => { this.playRewardAnimation(); }, 0.5); } /** * 播放奖励动画 */ private async playRewardAnimation(): Promise { console.log('MainUIController.playRewardAnimation 开始播放奖励动画'); const currentLevel = this.sdm.getCurrentLevel(); try { // 直接获取SaveDataManager中已经计算好的奖励数据 // 注意:不再依赖当前游戏状态判断,因为游戏数据清理可能已经重置了状态 // SaveDataManager.getLastRewards()已经包含了正确的奖励信息(成功或失败奖励) const rewards = this.sdm.getLastRewards(); console.log('获取已计算的奖励:', rewards); if (rewards && (rewards.money > 0 || rewards.diamonds > 0)) { // 使用MoneyAni播放奖励动画 if (this.moneyAniNode) { const moneyAni = this.moneyAniNode.getComponent(MoneyAni); if (moneyAni) { moneyAni.playRewardAnimation(rewards.money, rewards.diamonds, () => { console.log('奖励动画播放完成'); }); } else { console.error('MoneyAni组件未找到'); // 使用静态方法作为备选 MoneyAni.playReward(rewards.money, rewards.diamonds); } } else { console.warn('MoneyAni节点未设置,使用静态方法播放动画'); MoneyAni.playReward(rewards.money, rewards.diamonds); } } else { console.log('当前关卡没有奖励或奖励为0,跳过动画播放'); } } catch (error) { console.error('播放奖励动画时出错:', error); // 使用默认奖励 MoneyAni.playReward(25, 2); } } /** * 播放主界面背景音乐 */ private async playMainUIBGM(): Promise { // 旧逻辑废弃:直接走统一入口,根据顶层UI播放 // Audio.playMusic('data/弹球音效/ui bgm', true); Audio.updateBGMByTopUI(); } /** * 预加载当前关卡和下一关的资源 * 在MainUI场景加载时提前预加载,避免进入游戏时的资源加载延迟 */ private async preloadLevelResources(): Promise { console.log('[MainUIController] 开始预加载关卡资源'); try { const resourcePreloader = ResourcePreloader.getInstance(); const currentLevel = this.sdm.getCurrentLevel(); // 预加载当前关卡资源 console.log(`[MainUIController] 预加载当前关卡 ${currentLevel} 的资源`); try { await resourcePreloader.preloadLevelResources(currentLevel); console.log(`[MainUIController] 当前关卡 ${currentLevel} 资源预加载完成`); } catch (error) { console.warn(`[MainUIController] 当前关卡 ${currentLevel} 资源预加载失败:`, error); } // 预加载下一关资源(如果存在) const nextLevel = currentLevel + 1; console.log(`[MainUIController] 预加载下一关卡 ${nextLevel} 的资源`); try { await resourcePreloader.preloadLevelResources(nextLevel); console.log(`[MainUIController] 下一关卡 ${nextLevel} 资源预加载完成`); } catch (error) { console.warn(`[MainUIController] 下一关卡 ${nextLevel} 资源预加载失败(可能不存在该关卡):`, error); } console.log('[MainUIController] 关卡资源预加载流程完成'); } catch (error) { console.error('[MainUIController] 预加载关卡资源时发生错误:', error); } } /* =============== Util =============== */ private format(n:number){ return n>=1000000? (n/1e6).toFixed(1)+'M' : n>=1000? (n/1e3).toFixed(1)+'K' : n.toString(); } }