GameManager.ts 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. import { _decorator, Component, Node, find, director, UITransform, Button, Label, ProgressBar, } from 'cc';
  2. import { LevelManager } from './LevelManager';
  3. import { LevelConfigManager } from './LevelConfigManager';
  4. import { SaveDataManager } from './SaveDataManager';
  5. import { ConfigManager } from '../Core/ConfigManager';
  6. // EnemyController已通过事件系统解耦,不再需要直接导入
  7. import EventBus, { GameEvents } from '../Core/EventBus';
  8. import { PhysicsManager } from '../Core/PhysicsManager';
  9. import { LevelSessionManager } from '../Core/LevelSessionManager';
  10. import { GameBlockSelection } from '../CombatSystem/BlockSelection/GameBlockSelection';
  11. // GamePause已通过事件系统解耦,不再需要直接导入
  12. import { Wall } from '../CombatSystem/Wall';
  13. import { GameStartMove } from '../Animations/GameStartMove';
  14. import { ReStartGame } from './ReStartGame';
  15. import { InGameManager, GameState } from './IN_game';
  16. const { ccclass, property } = _decorator;
  17. /**
  18. * 全局应用状态枚举
  19. * 区分游戏外状态和游戏内状态
  20. */
  21. export enum AppState {
  22. // 游戏外状态 - 主界面相关
  23. MAIN_MENU = 'main_menu', // 主界面
  24. SHOP = 'shop', // 商店
  25. SKILLS = 'skills', // 技能界面
  26. SETTINGS = 'settings', // 设置界面
  27. // 游戏内状态 - 战斗相关
  28. IN_GAME = 'in_game' // 游戏进行中(包含所有游戏内子状态)
  29. }
  30. // GameState 枚举已迁移到 IN_game.ts
  31. /**
  32. * 增强版游戏管理器
  33. * 整合了游戏启动、状态管理、UI控制等功能
  34. */
  35. @ccclass('GameManager')
  36. export class GameManager extends Component {
  37. // === 原GameManager属性 ===
  38. @property({
  39. type: Node,
  40. tooltip: '拖拽BallController节点到这里'
  41. })
  42. public ballController: Node = null;
  43. @property({
  44. type: Node,
  45. tooltip: '拖拽GameBlockSelection节点到这里'
  46. })
  47. public gameBlockSelection: Node = null;
  48. @property({
  49. type: Node,
  50. tooltip: '拖拽GameArea节点到这里'
  51. })
  52. public gameArea: Node = null;
  53. @property({
  54. type: Node,
  55. tooltip: '拖拽EnemyController节点到这里'
  56. })
  57. public enemyManager: Node = null;
  58. // === 游戏状态管理属性 ===
  59. @property({
  60. type: Node,
  61. tooltip: '游戏结束UI节点 (GameEnd)'
  62. })
  63. public gameEndUI: Node = null;
  64. // === 游戏内管理器 ===
  65. @property({
  66. type: Node,
  67. tooltip: '游戏内状态管理器节点'
  68. })
  69. public inGameManagerNode: Node = null;
  70. // === UI节点引用 ===
  71. @property({
  72. type: Node,
  73. tooltip: '主界面UI节点 (Canvas/MainUI)'
  74. })
  75. public mainUI: Node = null;
  76. // === 动画组件引用 ===
  77. @property({
  78. type: Node,
  79. tooltip: '摄像机节点,用于获取GameStartMove组件'
  80. })
  81. public cameraNode: Node = null;
  82. // === 游戏配置属性 ===
  83. @property({
  84. tooltip: '状态检查间隔(秒)'
  85. })
  86. public checkInterval: number = 1.0;
  87. // === 私有属性 ===
  88. private gameStarted: boolean = false;
  89. private currentAppState: AppState = AppState.MAIN_MENU; // 全局应用状态
  90. private levelManager: LevelManager = null;
  91. private levelConfigManager: LevelConfigManager = null;
  92. private saveDataManager: SaveDataManager = null;
  93. private configManager: ConfigManager = null;
  94. // enemyController已通过事件系统解耦,不再需要直接引用
  95. // 游戏内管理器引用
  96. private inGameManager: InGameManager = null;
  97. // 游戏区域的边界
  98. private gameBounds = {
  99. left: 0,
  100. right: 0,
  101. top: 0,
  102. bottom: 0
  103. };
  104. // === 波次相关属性(已迁移到 InGameManager,保留用于兼容性) ===
  105. private currentWave: number = 1;
  106. private currentWaveEnemyCount: number = 0;
  107. private currentWaveTotalEnemies: number = 0;
  108. private totalEnemiesSpawned: number = 0;
  109. // levelWaves 和 levelTotalEnemies 已迁移到 InGameManager
  110. // === 能量系统属性已迁移到 InGameManager ===
  111. // === UI状态属性 ===
  112. private pendingSkillSelection: boolean = false;
  113. private shouldShowNextWavePrompt: boolean = false;
  114. // === 游戏计时器 ===
  115. private gameStartTime: number = 0;
  116. private gameEndTime: number = 0;
  117. private checkTimer: number = 0;
  118. // === 组件引用 ===
  119. private blockSelectionComponent: GameBlockSelection = null;
  120. private wallComponent: Wall = null;
  121. private gameStartMoveComponent: GameStartMove = null;
  122. // 游戏内状态相关方法已迁移到 InGameManager
  123. // === 游戏状态检查方法 ===
  124. private isGameOver(): boolean {
  125. // 通过事件系统检查游戏是否结束
  126. let isGameOver = false;
  127. const eventBus = EventBus.getInstance();
  128. eventBus.emit(GameEvents.GAME_CHECK_OVER, (result: boolean) => {
  129. isGameOver = result;
  130. });
  131. return isGameOver;
  132. }
  133. start() {
  134. // 初始化物理系统
  135. this.initPhysicsSystem();
  136. // 初始化管理器
  137. this.initializeManagers();
  138. // 提前初始化本局数据,确保 BlockManager 在 start 时能拿到正确金币
  139. if (!LevelSessionManager.inst.runtime) {
  140. LevelSessionManager.inst.initialize(
  141. this.saveDataManager?.getCurrentLevel() || 1,
  142. this.getWallHealth()
  143. );
  144. }
  145. // 计算游戏区域边界
  146. this.calculateGameBounds();
  147. // 初始化游戏状态
  148. this.initializeGameState();
  149. // 初始化UI节点
  150. this.initUINodes();
  151. // 敌人控制器已通过事件系统解耦,不再需要直接查找和设置
  152. // 设置UI按钮
  153. this.setupUIButtons();
  154. // 初始化组件
  155. this.initGameBlockSelection();
  156. this.initGameStartMove();
  157. // 加载当前关卡配置
  158. this.loadCurrentLevelConfig();
  159. // 监听GamePause状态变化事件
  160. this.setupGamePauseEventListeners();
  161. // 初始化游戏计时器
  162. this.gameStartTime = Date.now();
  163. }
  164. /**
  165. * 设置GamePause事件监听器
  166. */
  167. private setupGamePauseEventListeners() {
  168. const eventBus = EventBus.getInstance();
  169. // 监听游戏成功事件
  170. eventBus.on(GameEvents.GAME_SUCCESS, this.onGameSuccessEvent, this);
  171. // 监听游戏失败事件
  172. eventBus.on(GameEvents.GAME_DEFEAT, this.onGameDefeatEvent, this);
  173. // 监听游戏恢复事件
  174. eventBus.on(GameEvents.GAME_RESUME, this.onGameResumeEvent, this);
  175. // 监听游戏重启事件
  176. eventBus.on(GameEvents.GAME_RESTART, this.onGameRestartEvent, this);
  177. // 监听重置游戏管理器事件
  178. eventBus.on(GameEvents.RESET_GAME_MANAGER, this.onResetGameManagerEvent, this);
  179. // 敌人击杀事件监听已迁移到 InGameManager
  180. }
  181. /**
  182. * 处理游戏成功事件
  183. */
  184. private onGameSuccessEvent() {
  185. console.log('[GameManager] 接收到游戏成功事件,执行成功处理');
  186. // 游戏状态管理已迁移到 InGameManager
  187. // 注意:不在这里切换到MAIN_MENU状态,保持IN_GAME状态
  188. // 只有用户点击成功界面的按钮时才切换到主界面
  189. // 显示游戏结束UI并设置成功文本
  190. if (this.gameEndUI) {
  191. this.gameEndUI.active = true;
  192. this.setEndLabelText('SUCCESS');
  193. }
  194. // 执行游戏成功逻辑
  195. this.onGameSuccess();
  196. }
  197. /**
  198. * 处理游戏失败事件
  199. */
  200. private onGameDefeatEvent() {
  201. console.log('[GameManager] 接收到游戏失败事件,执行失败处理');
  202. // 游戏状态管理已迁移到 InGameManager
  203. // 注意:不在这里切换到MAIN_MENU状态,保持IN_GAME状态
  204. // 只有用户点击失败界面的按钮时才切换到主界面
  205. // 显示游戏结束UI并设置失败文本
  206. if (this.gameEndUI) {
  207. this.gameEndUI.active = true;
  208. this.setEndLabelText('DEFEAT');
  209. }
  210. // 执行游戏失败逻辑
  211. this.onGameDefeat().catch(error => {
  212. console.error('[GameManager] 游戏失败处理出错:', error);
  213. });
  214. }
  215. /**
  216. * 处理游戏恢复事件
  217. */
  218. private onGameResumeEvent() {
  219. console.log('[GameManager] 接收到游戏恢复事件');
  220. // GameManager在这里可以处理恢复相关的逻辑
  221. // 但不直接调用EnemyController的方法,避免重复调用
  222. }
  223. /**
  224. * 处理游戏重启事件
  225. */
  226. private onGameRestartEvent() {
  227. console.log('[GameManager] 接收到游戏重启事件');
  228. this.restartGame();
  229. }
  230. /**
  231. * 处理重置游戏管理器事件
  232. */
  233. private onResetGameManagerEvent() {
  234. console.log('[GameManager] 接收到重置游戏管理器事件');
  235. // 重置游戏管理器状态
  236. this.currentAppState = AppState.IN_GAME;
  237. this.gameStarted = false;
  238. this.gameStartTime = 0;
  239. this.gameEndTime = 0;
  240. console.log('[GameManager] 游戏管理器状态已重置');
  241. }
  242. // 敌人击杀事件处理已迁移到 InGameManager
  243. // 游戏状态调试方法已迁移到 InGameManager
  244. // === 暂停游戏 ===
  245. private pauseGame() {
  246. // 通过事件系统触发游戏暂停
  247. const eventBus = EventBus.getInstance();
  248. eventBus.emit(GameEvents.GAME_PAUSE);
  249. }
  250. // === 恢复游戏 ===
  251. public resumeGame() {
  252. // 通过事件系统触发游戏恢复
  253. const eventBus = EventBus.getInstance();
  254. eventBus.emit(GameEvents.GAME_RESUME);
  255. if (this.gameEndUI) {
  256. this.gameEndUI.active = false;
  257. }
  258. // === 新增:恢复时弹出下一波提示Toast ===
  259. if (this.shouldShowNextWavePrompt) {
  260. this.shouldShowNextWavePrompt = false;
  261. // 通过事件系统显示下一波提示
  262. eventBus.emit(GameEvents.ENEMY_SHOW_START_WAVE_PROMPT);
  263. }
  264. }
  265. update(deltaTime: number) {
  266. // 只有在游戏中时才进行游戏逻辑更新
  267. if (this.currentAppState !== AppState.IN_GAME) {
  268. return;
  269. }
  270. // 检查自动保存
  271. if (this.saveDataManager) {
  272. this.saveDataManager.checkAutoSave();
  273. }
  274. }
  275. // === 物理系统初始化 ===
  276. private initPhysicsSystem() {
  277. // 确保 PhysicsManager 单例存在
  278. let pm = PhysicsManager.getInstance();
  279. if (!pm) {
  280. const physicsNode = new Node('PhysicsManager');
  281. director.getScene()?.addChild(physicsNode);
  282. pm = physicsNode.addComponent(PhysicsManager);
  283. }
  284. }
  285. // === 管理器初始化 ===
  286. private initializeManagers() {
  287. this.levelManager = LevelManager.getInstance();
  288. // this.shopManager = ShopManager.getInstance();
  289. this.configManager = ConfigManager.getInstance();
  290. this.levelConfigManager = LevelConfigManager.getInstance();
  291. // enemyController已通过事件系统解耦,不再需要直接初始化
  292. // 初始化存档管理器
  293. this.saveDataManager = SaveDataManager.getInstance();
  294. this.saveDataManager.initialize();
  295. }
  296. // === 游戏状态初始化 ===
  297. private initializeGameState() {
  298. // 默认初始化为主菜单状态,游戏开始时会切换到IN_GAME
  299. this.currentAppState = AppState.MAIN_MENU;
  300. // 游戏内状态管理已迁移到 InGameManager
  301. this.pendingSkillSelection = false;
  302. }
  303. // === 计算游戏区域边界 ===
  304. private calculateGameBounds() {
  305. const canvas = find('Canvas');
  306. if (!canvas) {
  307. return;
  308. }
  309. const canvasUI = canvas.getComponent(UITransform);
  310. if (!canvasUI) {
  311. return;
  312. }
  313. const screenWidth = canvasUI.width;
  314. const screenHeight = canvasUI.height;
  315. const worldPos = canvas.worldPosition;
  316. this.gameBounds.left = worldPos.x - screenWidth / 2;
  317. this.gameBounds.right = worldPos.x + screenWidth / 2;
  318. this.gameBounds.bottom = worldPos.y - screenHeight / 2;
  319. this.gameBounds.top = worldPos.y + screenHeight / 2;
  320. }
  321. // === 初始化UI节点 ===
  322. private initUINodes() {
  323. // 初始化游戏结束UI
  324. if (this.gameEndUI) {
  325. this.gameEndUI.active = false;
  326. }
  327. // 初始化游戏内管理器
  328. if (this.inGameManagerNode) {
  329. this.inGameManager = this.inGameManagerNode.getComponent(InGameManager);
  330. }
  331. }
  332. // === 敌人控制器相关方法已通过事件系统解耦,不再需要 ===
  333. // === 游戏失败回调 ===
  334. private async onGameDefeat() {
  335. this.gameEndTime = Date.now();
  336. // 记录游戏失败到存档
  337. if (this.saveDataManager) {
  338. const currentLevel = this.saveDataManager.getCurrentLevel();
  339. this.saveDataManager.failLevel(currentLevel);
  340. // 计算波数完成比例并给予失败奖励
  341. // 波数信息已迁移到 InGameManager,通过InGameManager获取
  342. const inGameManager = this.getInGameManager();
  343. const totalWaves = inGameManager?.levelWaves?.length || 1;
  344. const completedWaves = inGameManager ? Math.max(0, inGameManager.getCurrentWave() - 1) : 0;
  345. const waveCompletionRatio = completedWaves / totalWaves;
  346. console.log(`[GameManager] 游戏失败 - 完成波数: ${completedWaves}/${totalWaves}, 比例: ${(waveCompletionRatio * 100).toFixed(1)}%`);
  347. // 给予基于波数比例的失败奖励
  348. await this.saveDataManager.giveFailureRewards(currentLevel, waveCompletionRatio, this.getGameDuration());
  349. // 更新统计数据
  350. this.saveDataManager.updateStatistic('totalTimePlayed', this.getGameDuration());
  351. }
  352. }
  353. // === 游戏成功回调 ===
  354. private async onGameSuccess() {
  355. this.gameEndTime = Date.now();
  356. await this.giveReward();
  357. this.onLevelComplete();
  358. }
  359. // === 给予奖励 ===
  360. private async giveReward() {
  361. if (!this.saveDataManager) return;
  362. const currentLevel = this.saveDataManager.getCurrentLevel();
  363. // 给予JSON配置中的基础奖励
  364. await this.saveDataManager.giveCompletionRewards(currentLevel);
  365. }
  366. // === 处理关卡完成 ===
  367. private onLevelComplete() {
  368. if (!this.saveDataManager) return;
  369. const currentLevel = this.saveDataManager.getCurrentLevel();
  370. const gameTime = this.getGameDuration();
  371. // 记录关卡完成到存档
  372. this.saveDataManager.completeLevel(currentLevel, 0, gameTime);
  373. // 更新统计数据
  374. this.saveDataManager.updateStatistic('totalTimePlayed', gameTime);
  375. this.saveDataManager.updateStatistic('totalEnemiesDefeated', this.totalEnemiesSpawned);
  376. // 兼容原有的LevelManager
  377. if (this.levelManager) {
  378. this.levelManager.completeLevel(currentLevel, 0);
  379. }
  380. }
  381. // === 计算游戏时长 ===
  382. private getGameDuration(): number {
  383. if (this.gameStartTime === 0) return 0;
  384. const endTime = this.gameEndTime || Date.now();
  385. return Math.floor((endTime - this.gameStartTime) / 1000);
  386. }
  387. // === 设置UI按钮 ===
  388. private setupUIButtons() {
  389. this.setupEndUIButtons();
  390. }
  391. /**
  392. * 设置游戏结束UI的EndLabel文本
  393. * @param text 要显示的文本 ('SUCCESS' 或 'DEFEAT')
  394. */
  395. private setEndLabelText(text: string) {
  396. if (!this.gameEndUI) return;
  397. const endLabel = this.gameEndUI.getChildByPath('Sprite/EndLabel');
  398. if (endLabel) {
  399. const labelComponent = endLabel.getComponent(Label);
  400. if (labelComponent) {
  401. labelComponent.string = text;
  402. console.log(`[GameManager] 设置EndLabel文本为: ${text}`);
  403. } else {
  404. console.warn('[GameManager] 未找到EndLabel的Label组件');
  405. }
  406. } else {
  407. console.warn('[GameManager] 未找到EndLabel节点路径: Sprite/EndLabel');
  408. }
  409. }
  410. // === 设置游戏结束界面按钮 ===
  411. private setupEndUIButtons() {
  412. if (this.gameEndUI) {
  413. const restartBtn = this.gameEndUI.getChildByName('RestartBtn');
  414. const mainMenuBtn = this.gameEndUI.getChildByName('MainMenuBtn');
  415. const shopBtn = this.gameEndUI.getChildByName('ShopBtn');
  416. const reviveBtn = this.gameEndUI.getChildByName('ReviveBtn');
  417. if (restartBtn) {
  418. const button = restartBtn.getComponent(Button);
  419. if (button) {
  420. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  421. }
  422. }
  423. if (mainMenuBtn) {
  424. const button = mainMenuBtn.getComponent(Button);
  425. if (button) {
  426. button.node.on(Button.EventType.CLICK, this.onMainMenuClick, this);
  427. }
  428. }
  429. if (shopBtn) {
  430. const button = shopBtn.getComponent(Button);
  431. if (button) {
  432. button.node.on(Button.EventType.CLICK, this.onShopClick, this);
  433. }
  434. }
  435. if (reviveBtn) {
  436. const button = reviveBtn.getComponent(Button);
  437. if (button) {
  438. button.node.on(Button.EventType.CLICK, this.onReviveClick, this);
  439. }
  440. }
  441. }
  442. }
  443. // === 按钮点击事件处理 ===
  444. private onRestartClick() {
  445. console.log('[GameManager] 重新开始游戏');
  446. // 通过事件系统触发游戏重启
  447. const eventBus = EventBus.getInstance();
  448. eventBus.emit(GameEvents.GAME_RESTART);
  449. }
  450. private onMainMenuClick() {
  451. console.log('[GameManager] 返回主菜单');
  452. // 使用装饰器属性获取MainUI,避免使用find
  453. if (!this.mainUI) {
  454. console.error('[GameManager] MainUI节点未在编辑器中设置,请拖拽Canvas/MainUI到GameManager的mainUI属性');
  455. return;
  456. }
  457. const mainUIController = this.mainUI.getComponent('MainUIController' as any);
  458. if (mainUIController) {
  459. // 游戏状态检查已迁移到 InGameManager,这里使用默认的奖励动画返回
  460. if (typeof (mainUIController as any).onReturnToMainUIWithReward === 'function') {
  461. console.log('[GameManager] 调用带奖励动画的返回主界面方法');
  462. (mainUIController as any).onReturnToMainUIWithReward();
  463. } else if (typeof (mainUIController as any).onReturnToMainUI === 'function') {
  464. console.log('[GameManager] 调用普通返回主界面方法');
  465. (mainUIController as any).onReturnToMainUI();
  466. } else {
  467. console.warn('[GameManager] 未找到返回主界面方法,使用兜底逻辑');
  468. this.fallbackMainMenuLogic(mainUIController);
  469. }
  470. } else {
  471. console.error('[GameManager] 未找到MainUIController组件');
  472. this.fallbackMainMenuLogic(null);
  473. }
  474. }
  475. /**
  476. * 兜底逻辑:当找不到MainUIController或相关方法时使用
  477. */
  478. private fallbackMainMenuLogic(mainUIController: any) {
  479. console.warn('[GameManager] 使用兜底逻辑返回主界面');
  480. const gameLevelUI = find('Canvas/GameLevelUI');
  481. if (gameLevelUI) gameLevelUI.active = false;
  482. if (this.mainUI) this.mainUI.active = true;
  483. if (mainUIController && typeof (mainUIController as any).updateUI === 'function') {
  484. (mainUIController as any).updateUI();
  485. }
  486. }
  487. private onShopClick() {
  488. console.log('[GameManager] 商店按钮被点击');
  489. // TODO: 实现商店逻辑
  490. }
  491. private onReviveClick() {
  492. const reviveCost = 10; // 复活消耗的钻石数量
  493. if (this.saveDataManager && this.saveDataManager.spendDiamonds(reviveCost)) {
  494. this.revivePlayer();
  495. }
  496. }
  497. // === 复活玩家 ===
  498. private revivePlayer() {
  499. if (this.wallComponent) {
  500. this.wallComponent.setHealth(50);
  501. }
  502. // 通过事件系统进行完整重置
  503. const eventBus = EventBus.getInstance();
  504. eventBus.emit(GameEvents.GAME_RESTART);
  505. }
  506. // === 重新开始当前关卡 ===
  507. private restartCurrentLevel() {
  508. // 通过事件系统进行完整重置
  509. const eventBus = EventBus.getInstance();
  510. eventBus.emit(GameEvents.GAME_RESTART);
  511. }
  512. public startGame() {
  513. if (this.gameStarted) return;
  514. this.gameStarted = true;
  515. this.gameStartTime = Date.now();
  516. // 游戏状态管理已迁移到 InGameManager
  517. // 发送游戏开始事件,通知其他组件
  518. const eventBus = EventBus.getInstance();
  519. eventBus.emit(GameEvents.GAME_START);
  520. console.log('[GameManager] 发送游戏开始事件');
  521. // 开始生成球
  522. this.spawnBall();
  523. // 启动状态检查
  524. this.checkTimer = 0;
  525. // 设置UI按钮事件
  526. this.setupUIButtons();
  527. // 第一波提示UI后再开始生成敌人
  528. // 通过事件系统显示开始波次提示
  529. eventBus.emit(GameEvents.ENEMY_SHOW_START_WAVE_PROMPT);
  530. // 通过事件系统开始敌人生成
  531. eventBus.emit(GameEvents.ENEMY_START_GAME);
  532. LevelSessionManager.inst.initialize(
  533. SaveDataManager.getInstance().getCurrentLevel(),
  534. this.getWallHealth()
  535. );
  536. }
  537. private spawnBall() {
  538. // 通过事件系统启动球的移动
  539. const eventBus = EventBus.getInstance();
  540. eventBus.emit(GameEvents.BALL_START);
  541. console.log('[GameManager] 发送BALL_START事件,球已启动');
  542. }
  543. public gameOver() {
  544. this.triggerGameDefeat();
  545. }
  546. // === 公共方法 ===
  547. public setHealth(health: number) {
  548. this.wallComponent?.setHealth(health);
  549. }
  550. public takeDamage(damage: number) {
  551. this.wallComponent?.takeDamage(damage);
  552. if (this.wallComponent?.getCurrentHealth() <= 0) {
  553. this.triggerGameDefeat();
  554. }
  555. }
  556. /**
  557. * 获取当前全局应用状态
  558. */
  559. public getCurrentAppState(): AppState {
  560. return this.currentAppState;
  561. }
  562. /**
  563. * 设置全局应用状态
  564. */
  565. public setAppState(state: AppState): void {
  566. console.log(`[GameManager] 应用状态切换: ${this.currentAppState} -> ${state}`);
  567. this.currentAppState = state;
  568. // 根据状态控制UI栏显示
  569. this.updateUIBarsVisibility(state);
  570. // 游戏内状态管理已迁移到 InGameManager
  571. }
  572. /**
  573. * 根据应用状态更新UI栏显示
  574. */
  575. private updateUIBarsVisibility(state: AppState): void {
  576. const topBarNode = find('Canvas/TopBar');
  577. const navBarNode = find('Canvas/NavBar');
  578. if (state === AppState.IN_GAME) {
  579. // 游戏内状态:隐藏TopBar和NavBar
  580. if (topBarNode) topBarNode.active = false;
  581. if (navBarNode) navBarNode.active = false;
  582. console.log('[GameManager] 游戏内状态:隐藏TopBar和NavBar');
  583. } else {
  584. // 游戏外状态:显示TopBar和NavBar
  585. if (topBarNode) topBarNode.active = true;
  586. if (navBarNode) navBarNode.active = true;
  587. console.log('[GameManager] 游戏外状态:显示TopBar和NavBar');
  588. }
  589. }
  590. /**
  591. * 获取当前游戏内状态已迁移到 InGameManager
  592. * 请使用 InGameManager.getInstance().getCurrentState()
  593. */
  594. /**
  595. * 获取InGameManager实例
  596. * 用于访问游戏内状态和逻辑
  597. */
  598. public getInGameManager(): InGameManager | null {
  599. return this.inGameManager;
  600. }
  601. /**
  602. * 获取当前游戏内状态
  603. * 通过InGameManager获取
  604. */
  605. public getCurrentGameState(): GameState | null {
  606. return this.inGameManager ? this.inGameManager.getCurrentState() : null;
  607. }
  608. /**
  609. * 检查是否在游戏中
  610. */
  611. public isInGame(): boolean {
  612. return this.currentAppState === AppState.IN_GAME;
  613. }
  614. /**
  615. * 从方块选择状态切换到游戏进行状态
  616. * 当玩家完成方块选择后调用
  617. */
  618. public startGameFromBlockSelection(): void {
  619. if (this.currentAppState !== AppState.IN_GAME) {
  620. console.warn('[GameManager] 不在游戏中,无法开始游戏');
  621. return;
  622. }
  623. // 游戏状态检查已迁移到 InGameManager
  624. console.log('[GameManager] 从方块选择状态切换到游戏进行状态,播放退出动画');
  625. // 播放退出BLOCK_SELECTION状态的动画
  626. if (this.gameStartMoveComponent) {
  627. console.log('[GameManager] 执行退出BLOCK_SELECTION状态的动画');
  628. this.gameStartMoveComponent.exitBlockSelectionMode(300, 0.3);
  629. }
  630. // 游戏状态管理已迁移到 InGameManager
  631. // 发送游戏开始事件,通知GamePause等组件
  632. const eventBus = EventBus.getInstance();
  633. eventBus.emit(GameEvents.GAME_START);
  634. console.log('[GameManager] 从方块选择切换到游戏时发送游戏开始事件');
  635. }
  636. public restartGame() {
  637. console.log('[GameManager] 重新开始游戏');
  638. // 设置应用状态为游戏中
  639. this.currentAppState = AppState.IN_GAME;
  640. this.gameStarted = false;
  641. this.gameStartTime = 0;
  642. this.gameEndTime = 0;
  643. // 通过事件系统重置游戏状态
  644. const eventBus = EventBus.getInstance();
  645. eventBus.emit(GameEvents.RESET_GAME_MANAGER);
  646. console.log('[GameManager] 游戏状态重置完成');
  647. }
  648. public forceGameSuccess() {
  649. this.triggerGameSuccess();
  650. }
  651. public forceGameDefeat() {
  652. this.triggerGameDefeat();
  653. }
  654. // === 触发游戏成功 ===
  655. private triggerGameSuccess() {
  656. console.log('[GameManager] 触发游戏成功');
  657. const eventBus = EventBus.getInstance();
  658. eventBus.emit(GameEvents.GAME_SUCCESS);
  659. }
  660. // === 触发游戏失败 ===
  661. private triggerGameDefeat() {
  662. console.log('[GameManager] 触发游戏失败');
  663. const eventBus = EventBus.getInstance();
  664. eventBus.emit(GameEvents.GAME_DEFEAT);
  665. }
  666. // === EnemyController相关方法已通过事件系统解耦,不再需要直接访问 ===
  667. public setTotalEnemiesSpawned(count: number) {
  668. this.totalEnemiesSpawned = count;
  669. }
  670. onDestroy() {
  671. // 清理GamePause事件监听
  672. const eventBus = EventBus.getInstance();
  673. eventBus.off(GameEvents.GAME_SUCCESS, this.onGameSuccessEvent, this);
  674. eventBus.off(GameEvents.GAME_DEFEAT, this.onGameDefeatEvent, this);
  675. eventBus.off(GameEvents.GAME_RESTART, this.onGameRestartEvent, this);
  676. eventBus.off(GameEvents.RESET_GAME_MANAGER, this.onResetGameManagerEvent, this);
  677. // ENEMY_KILLED事件监听已迁移到 InGameManager
  678. // 清理按钮事件监听
  679. if (this.gameEndUI) {
  680. const buttons = this.gameEndUI.getComponentsInChildren(Button);
  681. buttons.forEach(button => {
  682. button.node.off(Button.EventType.CLICK);
  683. });
  684. }
  685. // 清理单例实例
  686. if (GameManager._instance === this) {
  687. GameManager._instance = null;
  688. }
  689. }
  690. // === 加载当前关卡配置 ===
  691. public async loadCurrentLevelConfig() {
  692. if (!this.saveDataManager || !this.levelConfigManager) return;
  693. const currentLevel = this.saveDataManager.getCurrentLevel();
  694. try {
  695. const levelConfig = await this.levelConfigManager.getLevelConfig(currentLevel);
  696. if (levelConfig) {
  697. this.applyLevelConfig(levelConfig);
  698. } else {
  699. console.warn(`关卡 ${currentLevel} 配置加载失败`);
  700. }
  701. } catch (error) {
  702. console.error(`关卡 ${currentLevel} 配置加载错误:`, error);
  703. }
  704. }
  705. private applyLevelConfig(levelConfig: any) {
  706. console.log('[GameManager] 委托关卡配置应用给InGameManager');
  707. // 委托给InGameManager处理关卡配置
  708. if (this.inGameManager) {
  709. this.inGameManager.applyLevelConfig(levelConfig);
  710. } else {
  711. console.warn('[GameManager] InGameManager未初始化,无法应用关卡配置');
  712. // 备用方案:基本的波次配置处理(已简化)
  713. if (levelConfig.waves && Array.isArray(levelConfig.waves)) {
  714. this.currentWave = 1;
  715. console.log('[GameManager] 使用备用方案处理波次配置(功能有限)');
  716. console.warn('[GameManager] 建议确保InGameManager正确初始化以获得完整功能');
  717. }
  718. }
  719. }
  720. // === 获取当前关卡信息 ===
  721. public async getCurrentLevelInfo() {
  722. const currentLevel = this.saveDataManager ?
  723. this.saveDataManager.getCurrentLevel() :
  724. (this.levelManager ? this.levelManager.getCurrentLevel() : 1);
  725. const levelProgress = this.saveDataManager ?
  726. this.saveDataManager.getLevelProgress(currentLevel) : null;
  727. const levelData = this.levelManager ?
  728. this.levelManager.getLevelData(currentLevel) : null;
  729. const levelConfig = await this.loadCurrentLevelConfig();
  730. return {
  731. level: currentLevel,
  732. maxUnlockedLevel: this.saveDataManager ?
  733. this.saveDataManager.getMaxUnlockedLevel() :
  734. (this.levelManager ? this.levelManager.getMaxUnlockedLevel() : 1),
  735. progress: levelProgress,
  736. data: levelData,
  737. config: levelConfig,
  738. playerData: this.saveDataManager ? {
  739. coins: this.saveDataManager.getCoins(),
  740. diamonds: this.saveDataManager.getDiamonds(),
  741. gems: this.saveDataManager.getGems(),
  742. wallLevel: this.saveDataManager.getWallLevel()
  743. } : null
  744. };
  745. }
  746. // === 波次管理和能量系统已迁移到 InGameManager ===
  747. // 这些方法现在委托给 InGameManager 处理
  748. // === 获取当前波次(委托给InGameManager)===
  749. public getCurrentWave(): number {
  750. if (this.inGameManager) {
  751. return this.inGameManager.getCurrentWave();
  752. }
  753. return this.currentWave;
  754. }
  755. // === 获取当前能量值(委托给InGameManager)===
  756. public getCurrentEnergy(): number {
  757. if (this.inGameManager) {
  758. return this.inGameManager.getCurrentEnergy();
  759. }
  760. return 0; // 默认值
  761. }
  762. // === 获取最大能量值(委托给InGameManager)===
  763. public getMaxEnergy(): number {
  764. if (this.inGameManager) {
  765. return this.inGameManager.getMaxEnergy();
  766. }
  767. return 5; // 默认值
  768. }
  769. /* ========= 墙体血量 / 等级相关 ========= */
  770. // === 获取墙体血量(委托给InGameManager)===
  771. private getWallHealth(): number {
  772. if (this.inGameManager) {
  773. return this.inGameManager.getWallHealth();
  774. }
  775. // 备用方案:直接访问墙体组件
  776. return this.wallComponent ? this.wallComponent.getCurrentHealth() : 100;
  777. }
  778. // === 墙体血量 / 等级相关方法 - 现在委托给InGameManager ===
  779. public getWallHealthByLevel(level: number): number {
  780. if (this.inGameManager) {
  781. return this.inGameManager.getWallHealthByLevel(level);
  782. }
  783. // 备用方案:直接访问墙体组件
  784. return this.wallComponent ? this.wallComponent.getWallHealthByLevel(level) : 100;
  785. }
  786. public getCurrentWallLevel(): number {
  787. if (this.inGameManager) {
  788. return this.inGameManager.getCurrentWallLevel();
  789. }
  790. // 备用方案:直接访问墙体组件
  791. return this.wallComponent ? this.wallComponent.getCurrentWallLevel() : 1;
  792. }
  793. public getCurrentWallHealth(): number {
  794. if (this.inGameManager) {
  795. return this.inGameManager.getCurrentWallHealth();
  796. }
  797. // 备用方案:直接访问墙体组件
  798. return this.wallComponent ? this.wallComponent.getCurrentHealth() : 100;
  799. }
  800. public upgradeWallLevel(): { currentLevel: number; currentHp: number; nextLevel: number; nextHp: number } | null {
  801. if (this.inGameManager) {
  802. return this.inGameManager.upgradeWallLevel();
  803. }
  804. // 备用方案:直接访问墙体组件
  805. return this.wallComponent ? this.wallComponent.upgradeWallLevel() : null;
  806. }
  807. // 初始化GameBlockSelection组件
  808. private initGameBlockSelection() {
  809. if (this.gameBlockSelection) {
  810. this.blockSelectionComponent = this.gameBlockSelection.getComponent(GameBlockSelection);
  811. if (this.blockSelectionComponent) {
  812. // 设置确认回调
  813. this.blockSelectionComponent.setConfirmCallback(() => {
  814. this.handleConfirmAction();
  815. });
  816. }
  817. }
  818. }
  819. // 初始化GameStartMove组件
  820. private initGameStartMove() {
  821. if (this.cameraNode) {
  822. this.gameStartMoveComponent = this.cameraNode.getComponent(GameStartMove);
  823. if (this.gameStartMoveComponent) {
  824. console.log('[GameManager] GameStartMove组件初始化成功');
  825. } else {
  826. console.warn('[GameManager] 未找到GameStartMove组件');
  827. }
  828. } else {
  829. console.warn('[GameManager] 摄像机节点未设置,无法初始化GameStartMove组件');
  830. }
  831. }
  832. // 处理确认操作(委托给InGameManager)
  833. private handleConfirmAction() {
  834. console.log('[GameManager] 方块选择确认,委托给InGameManager处理');
  835. // 委托给InGameManager处理确认操作
  836. if (this.inGameManager) {
  837. this.inGameManager.handleConfirmAction();
  838. } else {
  839. console.warn('[GameManager] InGameManager未初始化,无法处理确认操作');
  840. }
  841. }
  842. // === 单例模式支持 ===
  843. private static _instance: GameManager = null;
  844. /**
  845. * 获取GameManager单例实例
  846. */
  847. public static getInstance(): GameManager {
  848. return GameManager._instance;
  849. }
  850. /**
  851. * 设置GameManager单例实例
  852. */
  853. public static setInstance(instance: GameManager): void {
  854. GameManager._instance = instance;
  855. }
  856. onLoad() {
  857. // 设置单例实例
  858. GameManager.setInstance(this);
  859. }
  860. }