| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- import { _decorator, Component, Node, Button, find } from 'cc';
- import { PopUPAni } from '../../Animations/PopUPAni';
- import EventBus, { GameEvents } from '../../Core/EventBus';
- import { GameManager, AppState } from '../../LevelSystem/GameManager';
- import { SaveDataManager } from '../../LevelSystem/SaveDataManager';
- import { GameStartMove } from '../../Animations/GameStartMove';
- import { SoundController } from './SoundController';
- import { Audio } from '../../AudioManager/AudioManager';
- const { ccclass, property } = _decorator;
- /**
- * 菜单系统控制器
- * 负责管理菜单UI的显示和隐藏
- */
- @ccclass('MenuController')
- export class MenuController extends Component {
- // UI节点引用
- @property(Node) menuUI: Node = null; // Canvas/MenuUI
- @property(Button) menuButton: Button = null; // Canvas/MenuButton (主界面使用)
- @property(Button) menuButton1: Button = null; // Canvas/MenueButton-001 (游戏中使用)
- @property(Button) closeButton: Button = null; // Canvas/MenuUI中的关闭按钮
- @property(Button) backButton: Button = null; // Canvas/MenuUI/Buttons/BackButton 退出游戏按钮
- @property(Button) continueButton: Button = null; // Canvas/MenuUI/Buttons/ContinueButton 继续游戏按钮
-
- // 需要检测的UI节点引用
- @property(Node) skillUpUI: Node = null; // Canvas/SkillUpUI
- @property(Node) upgradeUI: Node = null; // Canvas/UpgradeUI
- @property(Node) shopUI: Node = null; // Canvas/ShopUI
-
- // 动画控制器
- @property(PopUPAni) popupAni: PopUPAni = null; // Canvas/MenuUI上的PopUPAni组件
-
- // 音频控制器
- @property(SoundController) soundController: SoundController = null; // Canvas/MenuUI上的SoundController组件
-
- // 游戏管理器引用
- @property(GameManager) gameManager: GameManager = null; // GameManager组件引用
-
- // 菜单状态
- private isMenuOpen: boolean = false;
-
- // GameStartMove组件引用,用于重置镜头位置
- private gameStartMoveComponent: GameStartMove = null;
-
- onLoad() {
- this.bindEvents();
- // 初始化时隐藏菜单 - 使用PopUPAni的场景外位置隐藏方法
- if (this.popupAni) {
- this.popupAni.hidePanelImmediate();
- }
-
- // 设置界面状态监听
- this.setupUIStateListeners();
-
- // 初始化菜单按钮显示状态
- this.updateMenuButtonVisibility();
- }
-
- start() {
- // 初始化菜单按钮状态
- this.updateMenuButtonVisibility();
- }
-
- /**
- * 绑定事件
- */
- private bindEvents() {
- // 绑定菜单按钮点击事件 (主界面使用)
- if (this.menuButton) {
- this.menuButton.node.on(Button.EventType.CLICK, this.onMenuButtonClick, this);
- }
-
- // 绑定菜单按钮1点击事件 (游戏中使用)
- if (this.menuButton1) {
- this.menuButton1.node.on(Button.EventType.CLICK, this.onMenuButtonClick, this);
- }
-
- // 绑定关闭按钮点击事件
- if (this.closeButton) {
- this.closeButton.node.on(Button.EventType.CLICK, this.onCloseButtonClick, this);
- }
-
- // 绑定退出游戏按钮点击事件
- if (this.backButton) {
- this.backButton.node.on(Button.EventType.CLICK, this.onBackButtonClick, this);
- }
-
- // 绑定继续游戏按钮点击事件
- if (this.continueButton) {
- this.continueButton.node.on(Button.EventType.CLICK, this.onContinueButtonClick, this);
- }
- }
-
- /**
- * 设置UI状态监听器
- */
- private setupUIStateListeners() {
- const eventBus = EventBus.getInstance();
-
- // 监听界面切换事件(如果有的话)
- // 这里可以根据实际的界面切换事件来更新菜单按钮显示状态
- eventBus.on(GameEvents.RETURN_TO_MAIN_MENU, this.updateMenuButtonVisibility, this);
- eventBus.on(GameEvents.UI_PANEL_SWITCHED, this.updateMenuButtonVisibility, this);
-
- // 监听应用状态切换事件
- eventBus.on(GameEvents.APP_STATE_CHANGED, this.onAppStateChanged, this);
- }
-
- /**
- * 检测当前是否在需要隐藏菜单按钮的界面
- */
- private isInHiddenMenuUI(): boolean {
- // 检查SkillUpUI、UpgradeUI、ShopUI是否激活
- const isSkillUpActive = this.skillUpUI && this.skillUpUI.active;
- const isUpgradeActive = this.upgradeUI && this.upgradeUI.active;
- const isShopActive = this.shopUI && this.shopUI.active;
-
- return isSkillUpActive || isUpgradeActive || isShopActive;
- }
-
- /**
- * 更新菜单按钮的显示状态
- */
- private updateMenuButtonVisibility() {
- // 检查当前应用状态
- const currentAppState = this.gameManager ? this.gameManager.getCurrentAppState() : null;
- // 检查是否为第一关
- const isFirstLevel = (() => {
- try {
- const save = SaveDataManager.getInstance();
- return save && typeof save.getCurrentLevel === 'function' && save.getCurrentLevel() === 1;
- } catch {
- return false;
- }
- })();
-
- // 在游戏外界面中,检查是否在需要隐藏菜单按钮的界面
- if (currentAppState !== AppState.IN_GAME && this.isInHiddenMenuUI()) {
- // 在SkillUpUI、UpgradeUI、ShopUI界面中隐藏所有菜单按钮
- if (this.menuButton) this.menuButton.node.active = false;
- if (this.menuButton1) this.menuButton1.node.active = false;
- console.log('[MenuController] 在游戏外界面中隐藏菜单按钮');
- } else if (currentAppState === AppState.IN_GAME) {
- // 第一关隐藏游戏中的菜单按钮
- if (isFirstLevel) {
- if (this.menuButton) this.menuButton.node.active = false;
- if (this.menuButton1) this.menuButton1.node.active = false;
- console.log('[MenuController] 第一关隐藏MenueButton-001');
- } else {
- // 非第一关:游戏中显示menuButton1,隐藏menuButton
- if (this.menuButton) this.menuButton.node.active = false;
- if (this.menuButton1) this.menuButton1.node.active = true;
- console.log('[MenuController] 游戏中显示menuButton1');
- }
- } else {
- // 主界面显示menuButton,隐藏menuButton1
- if (this.menuButton) this.menuButton.node.active = true;
- if (this.menuButton1) this.menuButton1.node.active = false;
- console.log('[MenuController] 主界面显示menuButton');
- }
- }
-
- /**
- * 菜单按钮点击事件
- */
- private async onMenuButtonClick() {
- // 播放UI点击音效
- Audio.playUISound('data/弹球音效/ui play');
- if (this.isMenuOpen) {
- await this.closeMenu();
- } else {
- await this.openMenu();
- }
- }
-
- /**
- * 关闭按钮点击事件
- */
- private async onCloseButtonClick() {
- // 播放UI点击音效
- Audio.playUISound('data/弹球音效/ui play');
- await this.closeMenu();
- }
-
- /**
- * 继续游戏按钮点击事件
- */
- private async onContinueButtonClick() {
- // 播放UI点击音效
- Audio.playUISound('data/弹球音效/ui play');
- console.log('[MenuController] 继续游戏按钮被点击');
-
- // 检查GameManager组件引用
- if (!this.gameManager) {
- console.error('[MenuController] GameManager组件未通过装饰器挂载,请在Inspector中拖拽GameManager组件');
- await this.closeMenu();
- return;
- }
-
- const currentAppState = this.gameManager.getCurrentAppState();
- console.log(`[MenuController] 当前应用状态: ${currentAppState}`);
-
- if (currentAppState === AppState.IN_GAME) {
- // 在游戏中点击继续,关闭菜单并恢复游戏
- console.log('[MenuController] 游戏中点击继续,关闭菜单并恢复游戏');
- await this.closeMenu();
- } else {
- // 游戏外点击继续,直接关闭菜单
- console.log('[MenuController] 游戏外点击继续,直接关闭菜单');
- await this.closeMenu();
- }
- }
-
- /**
- * 退出游戏按钮点击事件
- * 只在游戏中有效,游戏外界面不显示菜单按钮
- */
- private async onBackButtonClick() {
- // 播放UI点击音效
- Audio.playUISound('data/弹球音效/ui play');
- console.log('[MenuController] 退出游戏按钮被点击');
-
- // 检查GameManager组件引用
- if (!this.gameManager) {
- console.error('[MenuController] GameManager组件未通过装饰器挂载,请在Inspector中拖拽GameManager组件');
- return;
- }
-
- const currentAppState = this.gameManager.getCurrentAppState();
- console.log(`[MenuController] 当前应用状态: ${currentAppState}`);
-
- // 只处理游戏中的退出逻辑,游戏外界面不显示菜单按钮
- if (currentAppState === AppState.IN_GAME) {
- // 在游戏中退出,应该触发游戏失败事件显示GameEnd面板
- 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事件');
- } else {
- // 游戏外不应该显示菜单按钮,如果意外触发则只关闭菜单
- console.log('[MenuController] 游戏外意外触发退出按钮,仅关闭菜单');
- await this.closeMenu();
- }
- }
-
- /**
- * 重置镜头位置到原始位置
- * 确保从游戏中退出时镜头位置正常
- */
- private resetCameraPosition() {
- // 如果还没有获取GameStartMove组件,尝试获取
- if (!this.gameStartMoveComponent) {
- const cameraNode = find('Canvas/Main Camera');
- if (cameraNode) {
- this.gameStartMoveComponent = cameraNode.getComponent(GameStartMove);
- }
- }
-
- // 如果成功获取到组件,重置镜头位置
- if (this.gameStartMoveComponent) {
- console.log('[MenuController] 重置镜头位置到原始位置');
- this.gameStartMoveComponent.resetCameraToOriginalPosition(0.3);
- } else {
- console.warn('[MenuController] 未找到GameStartMove组件,无法重置镜头位置');
- }
- }
-
- /**
- * 打开菜单
- */
- public async openMenu(): Promise<void> {
- if (this.isMenuOpen) return;
-
- this.isMenuOpen = true;
-
- // 检查是否在游戏中,如果是则通过事件系统触发游戏暂停
- if (this.gameManager && this.gameManager.getCurrentAppState() === AppState.IN_GAME) {
- console.log('[MenuController] 游戏中打开菜单,通过事件系统触发游戏暂停');
- const eventBus = EventBus.getInstance();
- eventBus.emit(GameEvents.GAME_PAUSE);
- }
-
- // 使用动画显示菜单面板
- if (this.popupAni) {
- await this.popupAni.showPanel();
- }
- // 注意:不再有fallback逻辑设置active,菜单面板始终保持active=true
-
- console.log('菜单已打开');
- }
-
- /**
- * 关闭菜单
- */
- public async closeMenu(): Promise<void> {
- if (!this.isMenuOpen) return;
-
- this.isMenuOpen = false;
-
- // 使用动画隐藏菜单面板
- if (this.popupAni) {
- await this.popupAni.hidePanel();
- }
- // 注意:不再有fallback逻辑设置active,菜单面板始终保持active=true
-
- // 检查是否在游戏中,如果是则通过事件系统触发游戏恢复
- if (this.gameManager && this.gameManager.getCurrentAppState() === AppState.IN_GAME) {
- console.log('[MenuController] 游戏中关闭菜单,通过事件系统触发游戏恢复');
- const eventBus = EventBus.getInstance();
- eventBus.emit(GameEvents.GAME_RESUME);
- }
-
- console.log('菜单已关闭');
- }
-
- /**
- * 关闭菜单但不触发游戏恢复事件(用于游戏失败退出)
- */
- private async closeMenuWithoutResume(): Promise<void> {
- if (!this.isMenuOpen) return;
-
- this.isMenuOpen = false;
-
- // 使用动画隐藏菜单面板
- if (this.popupAni) {
- await this.popupAni.hidePanel();
- }
- // 注意:不再有fallback逻辑设置active,菜单面板始终保持active=true
-
- // 不触发GAME_RESUME事件,避免与GAME_DEFEAT事件冲突
- console.log('菜单已关闭(未触发游戏恢复)');
- }
-
- /**
- * 切换菜单状态
- */
- public async toggleMenu(): Promise<void> {
- if (this.isMenuOpen) {
- await this.closeMenu();
- } else {
- await this.openMenu();
- }
- }
-
- /**
- * 获取菜单状态
- */
- public getMenuState(): boolean {
- return this.isMenuOpen;
- }
-
- /**
- * 立即关闭菜单(无动画)
- */
- public closeMenuImmediate(): void {
- this.isMenuOpen = false;
-
- if (this.popupAni) {
- this.popupAni.hidePanelImmediate();
- }
- // 注意:不再有fallback逻辑设置active,菜单面板始终保持active=true
-
- console.log('菜单已立即关闭');
- }
-
- /**
- * 立即打开菜单(无动画)
- */
- public openMenuImmediate(): void {
- this.isMenuOpen = true;
-
- if (this.popupAni) {
- this.popupAni.showPanelImmediate();
- }
- // 注意:不再有fallback逻辑设置active,菜单面板始终保持active=true
-
- console.log('菜单已立即打开');
- }
-
- /**
- * 应用状态切换事件处理
- */
- private onAppStateChanged(): void {
- console.log('[MenuController] 应用状态切换,更新菜单按钮显示状态');
- this.updateMenuButtonVisibility();
- }
-
- /**
- * 公共方法:更新菜单按钮显示状态
- * 供外部组件调用,例如在界面切换时
- */
- public updateMenuButtonState(): void {
- this.updateMenuButtonVisibility();
- }
-
- onDestroy() {
- // 解绑按钮事件
- if (this.menuButton) {
- this.menuButton.node.off(Button.EventType.CLICK, this.onMenuButtonClick, this);
- }
-
- if (this.menuButton1) {
- this.menuButton1.node.off(Button.EventType.CLICK, this.onMenuButtonClick, this);
- }
-
- if (this.closeButton) {
- this.closeButton.node.off(Button.EventType.CLICK, this.onCloseButtonClick, this);
- }
-
- if (this.backButton) {
- this.backButton.node.off(Button.EventType.CLICK, this.onBackButtonClick, this);
- }
-
- if (this.continueButton) {
- this.continueButton.node.off(Button.EventType.CLICK, this.onContinueButtonClick, this);
- }
-
- // 解绑事件监听器
- const eventBus = EventBus.getInstance();
- eventBus.off(GameEvents.RETURN_TO_MAIN_MENU, this.updateMenuButtonVisibility, this);
- eventBus.off(GameEvents.UI_PANEL_SWITCHED, this.updateMenuButtonVisibility, this);
- eventBus.off(GameEvents.APP_STATE_CHANGED, this.onAppStateChanged, this);
- }
- }
|