GameManager.ts 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. import { _decorator, Component, Node, Prefab, instantiate, Vec3, find, director, Canvas, UITransform, Button, Label, EPhysics2DDrawFlags, sys } from 'cc';
  2. import { LevelManager } from './LevelManager';
  3. import { LevelConfigManager } from './LevelConfigManager';
  4. import { SaveDataManager } from './SaveDataManager';
  5. import { ShopManager } from '../ShopSystem/ShopManager';
  6. import { ConfigManager } from '../Core/ConfigManager';
  7. import { EnemyController } from '../CombatSystem/EnemyController';
  8. import EventBus, { GameEvents } from '../Core/EventBus';
  9. import { PhysicsManager } from '../Core/PhysicsManager';
  10. import { MainUIController } from './MainUIController';
  11. import { BallController } from '../CombatSystem/BallController';
  12. import { BlockManager } from '../CombatSystem/BlockManager';
  13. import { LevelSessionManager } from '../Core/LevelSessionManager';
  14. const { ccclass, property } = _decorator;
  15. /**
  16. * 游戏状态枚举
  17. */
  18. enum GameState {
  19. PLAYING = 'playing',
  20. SUCCESS = 'success',
  21. DEFEAT = 'defeat',
  22. PAUSED = 'paused'
  23. }
  24. /**
  25. * 增强版游戏管理器
  26. * 整合了游戏启动、状态管理、UI控制等功能
  27. */
  28. @ccclass('GameManager')
  29. export class GameManager extends Component {
  30. // === 原GameManager属性 ===
  31. @property({
  32. type: Node,
  33. tooltip: '拖拽BallController节点到这里'
  34. })
  35. public ballController: Node = null;
  36. @property({
  37. type: Node,
  38. tooltip: '拖拽BlockSelectionUI节点到这里'
  39. })
  40. public blockSelectionUI: Node = null;
  41. @property({
  42. type: Node,
  43. tooltip: '拖拽GameArea节点到这里'
  44. })
  45. public gameArea: Node = null;
  46. @property({
  47. type: Node,
  48. tooltip: '拖拽EnemyController节点到这里'
  49. })
  50. public enemyManager: Node = null;
  51. // === 游戏状态管理属性 ===
  52. @property({
  53. type: Node,
  54. tooltip: '血量显示节点 (HeartLabeld)'
  55. })
  56. public heartLabelNode: Node = null;
  57. @property({
  58. type: Node,
  59. tooltip: '游戏成功UI节点 (GameSuccess)'
  60. })
  61. public gameSuccessUI: Node = null;
  62. @property({
  63. type: Node,
  64. tooltip: '游戏失败UI节点 (GameDefeat)'
  65. })
  66. public gameDefeatUI: Node = null;
  67. // === 游戏配置属性 ===
  68. // 墙体基础血量由存档决定,不再通过属性面板设置
  69. private wallHealth: number = 100;
  70. @property({
  71. tooltip: '初始血量'
  72. })
  73. public initialHealth: number = 100;
  74. @property({
  75. tooltip: '状态检查间隔(秒)'
  76. })
  77. public checkInterval: number = 1.0;
  78. // === 私有属性 ===
  79. private gameStarted: boolean = false;
  80. private currentHealth: number = 100;
  81. private currentState: GameState = GameState.PLAYING;
  82. private checkTimer: number = 0;
  83. private heartLabel: Label = null;
  84. private enemyController: EnemyController = null;
  85. private levelManager: LevelManager = null;
  86. private levelConfigManager: LevelConfigManager = null;
  87. private saveDataManager: SaveDataManager = null;
  88. private shopManager: ShopManager = null;
  89. private configManager: ConfigManager = null;
  90. private enemySpawningStarted: boolean = false;
  91. private totalEnemiesSpawned: number = 0;
  92. private currentWave: number = 1;
  93. private currentWaveEnemyCount: number = 0;
  94. private currentWaveTotalEnemies: number = 0; // 当前波次总敌人数
  95. private levelWaves: any[] = []; // 关卡波次配置
  96. private levelTotalEnemies: number = 0; // 本关卡总敌人数
  97. private enemiesKilled: number = 0; // 已击杀敌人数量
  98. // 游戏计时器
  99. private gameStartTime: number = 0;
  100. private gameEndTime: number = 0;
  101. // 游戏区域的边界
  102. private gameBounds = {
  103. left: 0,
  104. right: 0,
  105. top: 0,
  106. bottom: 0
  107. };
  108. start() {
  109. // 初始化物理系统
  110. this.initPhysicsSystem();
  111. // 初始化管理器
  112. this.initializeManagers();
  113. // 计算游戏区域边界
  114. this.calculateGameBounds();
  115. // 初始化游戏状态
  116. this.initializeGameState();
  117. // 查找UI节点
  118. this.findUINodes();
  119. // 查找敌人控制器
  120. this.findEnemyController();
  121. // 初始化墙体血量显示
  122. this.initWallHealthDisplay();
  123. // 设置敌人控制器
  124. this.setupEnemyController();
  125. // 设置UI按钮
  126. this.setupUIButtons();
  127. // 加载当前关卡配置
  128. this.loadCurrentLevelConfig();
  129. }
  130. update(deltaTime: number) {
  131. if (this.currentState !== GameState.PLAYING) {
  132. return;
  133. }
  134. // 更新检查计时器
  135. this.checkTimer += deltaTime;
  136. if (this.checkTimer >= this.checkInterval) {
  137. this.checkTimer = 0;
  138. this.checkGameState();
  139. }
  140. // 检查自动保存
  141. if (this.saveDataManager) {
  142. this.saveDataManager.checkAutoSave();
  143. }
  144. }
  145. // === 物理系统初始化 ===
  146. private initPhysicsSystem() {
  147. // 确保 PhysicsManager 单例存在
  148. let pm = PhysicsManager.getInstance();
  149. if (!pm) {
  150. const physicsNode = new Node('PhysicsManager');
  151. director.getScene()?.addChild(physicsNode);
  152. pm = physicsNode.addComponent(PhysicsManager);
  153. }
  154. }
  155. // === 管理器初始化 ===
  156. private initializeManagers() {
  157. this.levelManager = LevelManager.getInstance();
  158. this.shopManager = ShopManager.getInstance();
  159. this.configManager = ConfigManager.getInstance();
  160. this.levelConfigManager = LevelConfigManager.getInstance();
  161. this.enemyController = EnemyController.getInstance() || null;
  162. // 初始化存档管理器
  163. this.saveDataManager = SaveDataManager.getInstance();
  164. this.saveDataManager.initialize();
  165. // 从存档读取墙体基础血量
  166. const pd = this.saveDataManager.getPlayerData();
  167. if (pd && typeof pd.wallBaseHealth === 'number') {
  168. this.wallHealth = pd.wallBaseHealth;
  169. }
  170. }
  171. // === 游戏状态初始化 ===
  172. private initializeGameState() {
  173. this.currentHealth = this.initialHealth;
  174. this.currentState = GameState.PLAYING;
  175. this.checkTimer = 0;
  176. this.enemySpawningStarted = false;
  177. this.totalEnemiesSpawned = 0;
  178. this.currentWave = 1;
  179. this.currentWaveEnemyCount = 0;
  180. this.currentWaveTotalEnemies = 0; // 当前波次总敌人数
  181. // UI 初始化移交给 EnemyController
  182. }
  183. // === 计算游戏区域边界 ===
  184. private calculateGameBounds() {
  185. const canvas = find('Canvas');
  186. if (!canvas) {
  187. return;
  188. }
  189. const canvasUI = canvas.getComponent(UITransform);
  190. if (!canvasUI) {
  191. return;
  192. }
  193. const screenWidth = canvasUI.width;
  194. const screenHeight = canvasUI.height;
  195. const worldPos = canvas.worldPosition;
  196. this.gameBounds.left = worldPos.x - screenWidth / 2;
  197. this.gameBounds.right = worldPos.x + screenWidth / 2;
  198. this.gameBounds.bottom = worldPos.y - screenHeight / 2;
  199. this.gameBounds.top = worldPos.y + screenHeight / 2;
  200. }
  201. // === 查找UI节点 ===
  202. private findUINodes() {
  203. // 查找血量显示节点
  204. if (!this.heartLabelNode) {
  205. this.heartLabelNode = find('Canvas/GameLevelUI/HeartNode/HeartLabeld');
  206. }
  207. if (this.heartLabelNode) {
  208. this.heartLabel = this.heartLabelNode.getComponent(Label);
  209. }
  210. // 查找游戏成功UI
  211. if (!this.gameSuccessUI) {
  212. this.gameSuccessUI = find('Canvas/GameSuccess');
  213. }
  214. if (this.gameSuccessUI) {
  215. this.gameSuccessUI.active = false;
  216. }
  217. // 查找游戏失败UI
  218. if (!this.gameDefeatUI) {
  219. this.gameDefeatUI = find('Canvas/GameDefeat');
  220. }
  221. if (this.gameDefeatUI) {
  222. this.gameDefeatUI.active = false;
  223. }
  224. }
  225. // === 查找敌人控制器 ===
  226. private findEnemyController() {
  227. if (this.enemyManager) {
  228. this.enemyController = this.enemyManager.getComponent(EnemyController);
  229. }
  230. if (!this.enemyController) {
  231. const enemyNode = find('Canvas/GameLevelUI/EnemyController');
  232. if (enemyNode) {
  233. this.enemyController = enemyNode.getComponent(EnemyController);
  234. }
  235. }
  236. }
  237. // === 初始化墙体血量显示 ===
  238. private initWallHealthDisplay() {
  239. if (this.heartLabelNode && this.heartLabel) {
  240. this.heartLabel.string = this.wallHealth.toString();
  241. }
  242. // 让 EnemyController 自行查找/刷新血量 UI
  243. if (this.enemyController.initWallHealthDisplay) {
  244. this.enemyController.initWallHealthDisplay();
  245. }
  246. }
  247. // === 设置敌人控制器 ===
  248. private setupEnemyController() {
  249. if (!this.enemyManager) {
  250. const gameLevelUI = find('Canvas/GameLevelUI');
  251. if (!gameLevelUI) {
  252. console.error('找不到GameLevelUI节点,无法创建EnemyController');
  253. return;
  254. }
  255. this.enemyManager = new Node('EnemyController');
  256. gameLevelUI.addChild(this.enemyManager);
  257. }
  258. if (!this.enemyController) {
  259. this.enemyController = this.enemyManager.addComponent(EnemyController);
  260. this.enemyController.wallHealth = this.wallHealth;
  261. }
  262. }
  263. // === 游戏状态检查 ===
  264. private checkGameState() {
  265. // 更新血量
  266. this.updateHealthFromUI();
  267. if (this.currentHealth <= 0) {
  268. this.triggerGameDefeat();
  269. return;
  270. }
  271. // 检查是否全部击败
  272. if (this.checkAllEnemiesDefeated()) {
  273. this.triggerGameSuccess();
  274. return;
  275. }
  276. }
  277. // === 从UI更新血量 ===
  278. private updateHealthFromUI() {
  279. if (this.heartLabel) {
  280. const healthText = this.heartLabel.string;
  281. const healthMatch = healthText.match(/\d+/);
  282. if (healthMatch) {
  283. const newHealth = parseInt(healthMatch[0]);
  284. if (newHealth !== this.currentHealth) {
  285. this.currentHealth = newHealth;
  286. }
  287. }
  288. }
  289. }
  290. // === 检查所有敌人是否被击败 ===
  291. private checkAllEnemiesDefeated(): boolean {
  292. if (!this.enemyController) {
  293. return false;
  294. }
  295. // 检查敌人是否已开始生成(避免开局就胜利)
  296. if (!this.enemySpawningStarted) {
  297. if (this.enemyController.isGameStarted && this.enemyController.isGameStarted()) {
  298. this.enemySpawningStarted = true;
  299. } else {
  300. return false;
  301. }
  302. }
  303. // 获取当前敌人数量
  304. const currentEnemyCount = this.enemyController.getCurrentEnemyCount ?
  305. this.enemyController.getCurrentEnemyCount() : 0;
  306. // 如果关卡总敌人数已知,以击杀数为准判定胜利
  307. if (this.levelTotalEnemies > 0) {
  308. // 当击杀数达到或超过总敌数且场上没有存活敌人时胜利
  309. return this.enemiesKilled >= this.levelTotalEnemies && currentEnemyCount === 0;
  310. }
  311. // 否则退化到旧逻辑:依赖于是否曾经生成过敌人
  312. // 更新已生成敌人总数(记录曾经达到的最大值)
  313. if (currentEnemyCount > this.totalEnemiesSpawned) {
  314. this.totalEnemiesSpawned = currentEnemyCount;
  315. }
  316. const shouldCheckVictory = this.enemySpawningStarted &&
  317. currentEnemyCount === 0 &&
  318. this.totalEnemiesSpawned > 0;
  319. return shouldCheckVictory;
  320. }
  321. // === 触发游戏失败 ===
  322. private triggerGameDefeat() {
  323. if (this.currentState === GameState.DEFEAT) {
  324. return;
  325. }
  326. this.currentState = GameState.DEFEAT;
  327. this.pauseGame();
  328. if (this.gameDefeatUI) {
  329. this.gameDefeatUI.active = true;
  330. }
  331. this.onGameDefeat();
  332. // 派发游戏失败事件
  333. EventBus.getInstance().emit(GameEvents.GAME_DEFEAT);
  334. }
  335. // === 触发游戏成功 ===
  336. private triggerGameSuccess() {
  337. if (this.currentState === GameState.SUCCESS) {
  338. return;
  339. }
  340. this.currentState = GameState.SUCCESS;
  341. this.pauseGame();
  342. if (this.gameSuccessUI) {
  343. this.gameSuccessUI.active = true;
  344. }
  345. this.onGameSuccess();
  346. // 派发游戏成功事件
  347. EventBus.getInstance().emit(GameEvents.GAME_SUCCESS);
  348. }
  349. // === 暂停游戏 ===
  350. private pauseGame() {
  351. this.gameStarted = false;
  352. if (this.enemyController && this.enemyController.pauseSpawning) {
  353. this.enemyController.pauseSpawning();
  354. }
  355. }
  356. // === 恢复游戏 ===
  357. private resumeGame() {
  358. this.currentState = GameState.PLAYING;
  359. this.gameStarted = true;
  360. if (this.enemyController && this.enemyController.resumeSpawning) {
  361. this.enemyController.resumeSpawning();
  362. }
  363. if (this.gameSuccessUI) {
  364. this.gameSuccessUI.active = false;
  365. }
  366. if (this.gameDefeatUI) {
  367. this.gameDefeatUI.active = false;
  368. }
  369. }
  370. // === 游戏失败回调 ===
  371. private onGameDefeat() {
  372. this.gameEndTime = Date.now();
  373. // 记录游戏失败到存档
  374. if (this.saveDataManager) {
  375. const currentLevel = this.saveDataManager.getCurrentLevel();
  376. this.saveDataManager.failLevel(currentLevel);
  377. // 更新统计数据
  378. this.saveDataManager.updateStatistic('totalTimePlayed', this.getGameDuration());
  379. }
  380. }
  381. // === 游戏成功回调 ===
  382. private onGameSuccess() {
  383. this.gameEndTime = Date.now();
  384. this.giveReward();
  385. this.onLevelComplete();
  386. }
  387. // === 给予奖励 ===
  388. private giveReward() {
  389. if (!this.saveDataManager) return;
  390. const currentLevel = this.saveDataManager.getCurrentLevel();
  391. const baseReward = currentLevel * 50;
  392. const healthBonus = Math.floor(this.currentHealth * 0.1);
  393. const timeBonus = this.calculateTimeBonus();
  394. const totalCoins = baseReward + healthBonus + timeBonus;
  395. // 给予金币奖励
  396. this.saveDataManager.addCoins(totalCoins, `level_${currentLevel}_complete`);
  397. // 如果是首次完成,给予额外奖励
  398. if (!this.saveDataManager.isLevelCompleted(currentLevel)) {
  399. const firstClearBonus = currentLevel * 25;
  400. this.saveDataManager.addCoins(firstClearBonus, `level_${currentLevel}_first_clear`);
  401. }
  402. }
  403. // === 处理关卡完成 ===
  404. private onLevelComplete(score: number = 0, stars: number = 1) {
  405. if (!this.saveDataManager) return;
  406. const currentLevel = this.saveDataManager.getCurrentLevel();
  407. const gameTime = this.getGameDuration();
  408. // 计算得分(基于剩余血量、用时等)
  409. const calculatedScore = this.calculateScore();
  410. const finalScore = Math.max(score, calculatedScore);
  411. // 计算星级(基于表现)
  412. const calculatedStars = this.calculateStars();
  413. const finalStars = Math.max(stars, calculatedStars);
  414. // 记录关卡完成到存档
  415. this.saveDataManager.completeLevel(currentLevel, finalScore, gameTime, finalStars);
  416. // 更新统计数据
  417. this.saveDataManager.updateStatistic('totalTimePlayed', gameTime);
  418. this.saveDataManager.updateStatistic('totalEnemiesDefeated', this.totalEnemiesSpawned);
  419. // 兼容原有的LevelManager
  420. if (this.levelManager) {
  421. this.levelManager.completeLevel(currentLevel, finalScore, finalStars);
  422. }
  423. }
  424. // === 计算游戏时长 ===
  425. private getGameDuration(): number {
  426. if (this.gameStartTime === 0) return 0;
  427. const endTime = this.gameEndTime || Date.now();
  428. return Math.floor((endTime - this.gameStartTime) / 1000);
  429. }
  430. // === 计算时间奖励 ===
  431. private calculateTimeBonus(): number {
  432. const gameTime = this.getGameDuration();
  433. if (gameTime === 0) return 0;
  434. // 时间越短奖励越多,最多额外50%奖励
  435. const maxTime = 300; // 5分钟
  436. const timeRatio = Math.max(0, (maxTime - gameTime) / maxTime);
  437. const baseReward = this.saveDataManager.getCurrentLevel() * 50;
  438. return Math.floor(baseReward * timeRatio * 0.5);
  439. }
  440. // === 计算得分 ===
  441. private calculateScore(): number {
  442. const currentLevel = this.saveDataManager?.getCurrentLevel() || 1;
  443. const baseScore = currentLevel * 1000;
  444. const healthScore = this.currentHealth * 10;
  445. const enemyScore = this.totalEnemiesSpawned * 50;
  446. const timeScore = this.calculateTimeBonus();
  447. return baseScore + healthScore + enemyScore + timeScore;
  448. }
  449. // === 计算星级 ===
  450. private calculateStars(): number {
  451. const healthRatio = this.currentHealth / this.initialHealth;
  452. const gameTime = this.getGameDuration();
  453. // 基于血量剩余和用时计算星级
  454. if (healthRatio >= 0.8 && gameTime <= 120) {
  455. return 3; // 3星:血量80%以上,2分钟内完成
  456. } else if (healthRatio >= 0.5 && gameTime <= 300) {
  457. return 2; // 2星:血量50%以上,5分钟内完成
  458. } else if (healthRatio > 0) {
  459. return 1; // 1星:只要完成就有1星
  460. }
  461. return 1;
  462. }
  463. // === 设置UI按钮 ===
  464. private setupUIButtons() {
  465. this.setupSuccessUIButtons();
  466. this.setupDefeatUIButtons();
  467. }
  468. // === 设置成功界面按钮 ===
  469. private setupSuccessUIButtons() {
  470. if (this.gameSuccessUI) {
  471. const nextLevelBtn = this.gameSuccessUI.getChildByName('NextLevelBtn');
  472. const restartBtn = this.gameSuccessUI.getChildByName('RestartBtn');
  473. const mainMenuBtn = this.gameSuccessUI.getChildByName('MainMenuBtn');
  474. const shopBtn = this.gameSuccessUI.getChildByName('ShopBtn');
  475. if (nextLevelBtn) {
  476. const button = nextLevelBtn.getComponent(Button);
  477. if (button) {
  478. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  479. }
  480. }
  481. if (restartBtn) {
  482. const button = restartBtn.getComponent(Button);
  483. if (button) {
  484. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  485. }
  486. }
  487. if (mainMenuBtn) {
  488. const button = mainMenuBtn.getComponent(Button);
  489. if (button) {
  490. button.node.on(Button.EventType.CLICK, this.onMainMenuClick, this);
  491. }
  492. }
  493. if (shopBtn) {
  494. const button = shopBtn.getComponent(Button);
  495. if (button) {
  496. button.node.on(Button.EventType.CLICK, this.onShopClick, this);
  497. }
  498. }
  499. }
  500. }
  501. // === 设置失败界面按钮 ===
  502. private setupDefeatUIButtons() {
  503. if (this.gameDefeatUI) {
  504. const restartBtn = this.gameDefeatUI.getChildByName('RestartBtn');
  505. const mainMenuBtn = this.gameDefeatUI.getChildByName('MainMenuBtn');
  506. const shopBtn = this.gameDefeatUI.getChildByName('ShopBtn');
  507. const reviveBtn = this.gameDefeatUI.getChildByName('ReviveBtn');
  508. if (restartBtn) {
  509. const button = restartBtn.getComponent(Button);
  510. if (button) {
  511. button.node.on(Button.EventType.CLICK, this.onRestartClick, this);
  512. }
  513. }
  514. if (mainMenuBtn) {
  515. const button = mainMenuBtn.getComponent(Button);
  516. if (button) {
  517. button.node.on(Button.EventType.CLICK, this.onMainMenuClick, this);
  518. }
  519. }
  520. if (shopBtn) {
  521. const button = shopBtn.getComponent(Button);
  522. if (button) {
  523. button.node.on(Button.EventType.CLICK, this.onShopClick, this);
  524. }
  525. }
  526. if (reviveBtn) {
  527. const button = reviveBtn.getComponent(Button);
  528. if (button) {
  529. button.node.on(Button.EventType.CLICK, this.onReviveClick, this);
  530. }
  531. }
  532. }
  533. }
  534. // === 按钮点击事件处理 ===
  535. private onRestartClick() {
  536. this.restartGame();
  537. }
  538. private onMainMenuClick() {
  539. // 隐藏游戏界面,显示主界面
  540. const gameLevelUI = find('Canvas/GameLevelUI');
  541. const mainUI = find('Canvas/MainUI');
  542. if (gameLevelUI) gameLevelUI.active = false;
  543. if (mainUI) mainUI.active = true;
  544. // 更新主界面
  545. const mainUIController = mainUI?.getComponent(MainUIController);
  546. if (mainUIController) {
  547. mainUIController.updateUI();
  548. }
  549. }
  550. private onShopClick() {
  551. // 打开商店界面
  552. const gameLevelUI = find('Canvas/GameLevelUI');
  553. const shopUI = find('Canvas/ShopUI');
  554. if (gameLevelUI) gameLevelUI.active = false;
  555. if (shopUI) shopUI.active = true;
  556. }
  557. private onReviveClick() {
  558. const reviveCost = 10; // 复活消耗的钻石数量
  559. if (this.saveDataManager && this.saveDataManager.spendDiamonds(reviveCost)) {
  560. this.revivePlayer();
  561. }
  562. }
  563. // === 复活玩家 ===
  564. private revivePlayer() {
  565. this.setHealth(50);
  566. this.restartGame();
  567. }
  568. // === 重新开始当前关卡 ===
  569. private restartCurrentLevel() {
  570. this.restartGame();
  571. }
  572. // === 原GameManager方法 ===
  573. public onConfirmButtonClicked() {
  574. if (this.blockSelectionUI) {
  575. this.blockSelectionUI.active = false;
  576. const gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
  577. if (gridContainer) {
  578. gridContainer.active = true;
  579. }
  580. this.preservePlacedBlocks();
  581. }
  582. this.startGame();
  583. }
  584. private preservePlacedBlocks() {
  585. const blockController = find('Canvas/GameLevelUI/BlockController');
  586. if (blockController) {
  587. const blockManager = blockController.getComponent('BlockManager') as any;
  588. if (blockManager) {
  589. blockManager.onGameStart();
  590. }
  591. }
  592. }
  593. public startGame() {
  594. if (this.gameStarted) return;
  595. this.gameStarted = true;
  596. this.gameStartTime = Date.now();
  597. this.currentState = GameState.PLAYING;
  598. // 开始敌人生成
  599. this.spawnBall();
  600. // 启动状态检查
  601. this.checkTimer = 0;
  602. // 设置UI按钮事件
  603. this.setupUIButtons();
  604. // 开始生成敌人
  605. this.forceStartEnemySpawning();
  606. LevelSessionManager.inst.initialize(
  607. SaveDataManager.getInstance().getCurrentLevel(),
  608. this.wallHealth
  609. );
  610. }
  611. private spawnBall() {
  612. if (!this.ballController) return;
  613. const ballControllerComponent = this.ballController.getComponent(BallController);
  614. if (ballControllerComponent) {
  615. ballControllerComponent.startBall();
  616. }
  617. }
  618. public gameOver() {
  619. this.triggerGameDefeat();
  620. }
  621. // === 公共方法 ===
  622. public setHealth(health: number) {
  623. this.currentHealth = Math.max(0, health);
  624. }
  625. public takeDamage(damage: number) {
  626. this.currentHealth = Math.max(0, this.currentHealth - damage);
  627. if (this.currentHealth <= 0) {
  628. this.triggerGameDefeat();
  629. }
  630. }
  631. public getCurrentState(): GameState {
  632. return this.currentState;
  633. }
  634. public restartGame() {
  635. this.currentState = GameState.PLAYING;
  636. this.gameStarted = false;
  637. this.gameStartTime = 0;
  638. this.gameEndTime = 0;
  639. this.currentHealth = this.initialHealth;
  640. this.totalEnemiesSpawned = 0;
  641. this.enemiesKilled = 0;
  642. this.currentWave = 1;
  643. this.currentWaveEnemyCount = 0;
  644. // 关闭胜利/失败界面,确保重新进入时是正常状态
  645. if (this.gameSuccessUI) {
  646. this.gameSuccessUI.active = false;
  647. }
  648. if (this.gameDefeatUI) {
  649. this.gameDefeatUI.active = false;
  650. }
  651. // 通知BlockManager游戏重置
  652. const blockMgrNode = find('Canvas/GameLevelUI/BlockController');
  653. const blockManager = blockMgrNode?.getComponent(BlockManager);
  654. if (blockManager) {
  655. blockManager.onGameReset?.();
  656. }
  657. // 清空关卡剩余敌人
  658. if (this.enemyController && this.enemyController.clearAllEnemies) {
  659. this.enemyController.clearAllEnemies();
  660. }
  661. // 重置墙体血量显示
  662. this.initWallHealthDisplay();
  663. // 初始化本局数据(金币45等)
  664. LevelSessionManager.inst.clear();
  665. LevelSessionManager.inst.initialize(
  666. this.saveDataManager.getCurrentLevel(),
  667. this.wallHealth
  668. );
  669. // 刷新方块金币显示(如果 BlockManager 已存在)
  670. if (blockManager) {
  671. blockManager.updateCoinDisplay?.();
  672. }
  673. }
  674. public isGameOver(): boolean {
  675. return this.currentState === GameState.SUCCESS || this.currentState === GameState.DEFEAT;
  676. }
  677. public forceGameSuccess() {
  678. this.triggerGameSuccess();
  679. }
  680. public forceGameDefeat() {
  681. this.triggerGameDefeat();
  682. }
  683. // === 获取EnemyController组件 ===
  684. public getEnemyController() {
  685. return this.enemyController;
  686. }
  687. // === 调试方法 ===
  688. public getEnemyStatus() {
  689. if (!this.enemyController) return;
  690. const currentCount = this.enemyController.getCurrentEnemyCount();
  691. const gameStarted = this.enemyController.isGameStarted();
  692. const activeEnemies = this.enemyController.getActiveEnemies?.() || [];
  693. if (activeEnemies.length > 0) {
  694. for (let index = 0; index < activeEnemies.length; index++) {
  695. const enemy = activeEnemies[index];
  696. if (enemy?.isValid) {
  697. // 查看敌人状态
  698. } else {
  699. // 无效敌人节点
  700. }
  701. }
  702. }
  703. }
  704. public forceStartEnemySpawning() {
  705. if (this.enemyController) {
  706. this.enemyController.startGame();
  707. }
  708. }
  709. public setTotalEnemiesSpawned(count: number) {
  710. this.totalEnemiesSpawned = count;
  711. }
  712. public testEnemyDetection() {
  713. // 测试敌人检测功能
  714. this.getEnemyStatus();
  715. }
  716. public testComponentAccess() {
  717. // 测试组件访问
  718. if (this.enemyController) {
  719. // 组件访问正常
  720. }
  721. }
  722. public testEnemyAttackWall() {
  723. if (!this.enemyController) return;
  724. const currentHealth = this.enemyController.getCurrentWallHealth();
  725. const testDamage = 50;
  726. this.enemyController.damageWall(testDamage);
  727. const newHealth = this.enemyController.getCurrentWallHealth();
  728. }
  729. onDestroy() {
  730. // 清理按钮事件监听
  731. if (this.gameSuccessUI) {
  732. const buttons = this.gameSuccessUI.getComponentsInChildren(Button);
  733. buttons.forEach(button => {
  734. button.node.off(Button.EventType.CLICK);
  735. });
  736. }
  737. if (this.gameDefeatUI) {
  738. const buttons = this.gameDefeatUI.getComponentsInChildren(Button);
  739. buttons.forEach(button => {
  740. button.node.off(Button.EventType.CLICK);
  741. });
  742. }
  743. }
  744. // === 加载当前关卡配置 ===
  745. public async loadCurrentLevelConfig() {
  746. if (!this.saveDataManager || !this.levelConfigManager) return;
  747. const currentLevel = this.saveDataManager.getCurrentLevel();
  748. try {
  749. const levelConfig = await this.levelConfigManager.getLevelConfig(currentLevel);
  750. if (levelConfig) {
  751. this.applyLevelConfig(levelConfig);
  752. } else {
  753. console.warn(`关卡 ${currentLevel} 配置加载失败`);
  754. }
  755. } catch (error) {
  756. console.error(`关卡 ${currentLevel} 配置加载错误:`, error);
  757. }
  758. }
  759. private applyLevelConfig(levelConfig: any) {
  760. // 应用关卡配置
  761. // 如果有武器配置,应用武器
  762. if (levelConfig.weapons && Array.isArray(levelConfig.weapons)) {
  763. // 应用武器配置
  764. }
  765. // 如果有波次配置,设置敌人波次
  766. if (levelConfig.waves && Array.isArray(levelConfig.waves)) {
  767. this.levelWaves = levelConfig.waves;
  768. this.currentWave = 1;
  769. // 计算本关卡总敌人数
  770. this.levelTotalEnemies = 0;
  771. for (const wave of this.levelWaves) {
  772. for (const enemy of wave.enemies || []) {
  773. this.levelTotalEnemies += enemy.count || 0;
  774. }
  775. }
  776. // 通知 EnemyController 初始化第一波数据及 UI
  777. if (this.enemyController) {
  778. const totalWaves = this.levelWaves.length;
  779. const firstWaveEnemies = this.levelWaves.length > 0 && this.levelWaves[0].enemies ?
  780. this.levelWaves[0].enemies.reduce((t: number, g: any) => t + (g.count || 0), 0) : 0;
  781. this.enemyController.startWave(1, totalWaves, firstWaveEnemies);
  782. }
  783. }
  784. }
  785. // === 获取当前关卡信息 ===
  786. public async getCurrentLevelInfo() {
  787. const currentLevel = this.saveDataManager ?
  788. this.saveDataManager.getCurrentLevel() :
  789. (this.levelManager ? this.levelManager.getCurrentLevel() : 1);
  790. const levelProgress = this.saveDataManager ?
  791. this.saveDataManager.getLevelProgress(currentLevel) : null;
  792. const levelData = this.levelManager ?
  793. this.levelManager.getLevelData(currentLevel) : null;
  794. const levelConfig = await this.loadCurrentLevelConfig();
  795. return {
  796. level: currentLevel,
  797. maxUnlockedLevel: this.saveDataManager ?
  798. this.saveDataManager.getMaxUnlockedLevel() :
  799. (this.levelManager ? this.levelManager.getMaxUnlockedLevel() : 1),
  800. progress: levelProgress,
  801. data: levelData,
  802. config: levelConfig,
  803. playerData: this.saveDataManager ? {
  804. coins: this.saveDataManager.getCoins(),
  805. diamonds: this.saveDataManager.getDiamonds(),
  806. gems: this.saveDataManager.getGems(),
  807. playerLevel: this.saveDataManager.getPlayerLevel()
  808. } : null
  809. };
  810. }
  811. // === 更新波次显示 ===
  812. private updateWaveDisplay() {
  813. // UI 更新交由 EnemyController 处理
  814. }
  815. // === 更新敌人数量显示 ===
  816. private updateEnemyCountDisplay() {
  817. // UI 更新交由 EnemyController 处理
  818. }
  819. // === 设置当前波次 ===
  820. public setCurrentWave(wave: number, enemyCount: number = 0) {
  821. this.currentWave = wave;
  822. this.currentWaveEnemyCount = 0; // 重置当前击杀数
  823. this.currentWaveTotalEnemies = enemyCount; // 设置该波次总敌人数
  824. if (this.enemyController) {
  825. const totalWaves = this.levelWaves?.length || 1;
  826. this.enemyController.startWave(wave, totalWaves, enemyCount);
  827. }
  828. }
  829. // === 更新当前波次敌人数量 ===
  830. public updateCurrentWaveEnemyCount(count: number) {
  831. this.currentWaveEnemyCount = count;
  832. }
  833. // === 获取当前波次 ===
  834. public getCurrentWave(): number {
  835. return this.currentWave;
  836. }
  837. // === 获取当前波次敌人数量 ===
  838. public getCurrentWaveEnemyCount(): number {
  839. return this.currentWaveEnemyCount;
  840. }
  841. // === 获取当前波次总敌人数量 ===
  842. public getCurrentWaveTotalEnemies(): number {
  843. return this.currentWaveTotalEnemies;
  844. }
  845. // === 进入下一波 ===
  846. public nextWave() {
  847. this.currentWave++;
  848. // 根据关卡配置获取下一波敌人数
  849. let enemyTotal = 0;
  850. if (this.levelWaves && this.levelWaves.length >= this.currentWave) {
  851. const waveCfg = this.levelWaves[this.currentWave - 1];
  852. if (waveCfg && waveCfg.enemies) {
  853. enemyTotal = waveCfg.enemies.reduce((t: number, g: any) => t + (g.count || 0), 0);
  854. }
  855. }
  856. this.setCurrentWave(this.currentWave, enemyTotal);
  857. }
  858. /** 显示下一波提示并在短暂延迟后开始下一波 */
  859. private showNextWavePrompt() {
  860. if (this.enemyController) {
  861. this.enemyController.showNextWavePromptUI();
  862. }
  863. // 2 秒后开始下一波
  864. this.scheduleOnce(() => {
  865. this.nextWave();
  866. }, 2);
  867. }
  868. /** 敌人被消灭时由 EnemyController 调用 */
  869. public onEnemyKilled() {
  870. this.enemiesKilled++;
  871. // 当前波击杀 +1
  872. this.currentWaveEnemyCount++;
  873. const remaining = this.currentWaveTotalEnemies - this.currentWaveEnemyCount;
  874. if (remaining <= 0) {
  875. // 当前波结束
  876. if (this.currentWave < (this.levelWaves?.length || 1)) {
  877. // 还有下一波,显示提示
  878. this.showNextWavePrompt();
  879. } else {
  880. // 最后一波也结束
  881. this.triggerGameSuccess();
  882. }
  883. }
  884. }
  885. /* ========= 墙体血量 / 等级相关 ========= */
  886. private wallHpMap: Record<number, number> = {
  887. 1: 100,
  888. 2: 1000,
  889. 3: 1200,
  890. 4: 1500,
  891. 5: 2000
  892. };
  893. /** 根据等级获取墙体血量 */
  894. public getWallHealthByLevel(level: number): number {
  895. return this.wallHpMap[level] || (100 + (level - 1) * 200);
  896. }
  897. /** 获取当前墙壁等级 */
  898. public getCurrentWallLevel(): number {
  899. return this.saveDataManager?.getPlayerData().playerLevel || 1;
  900. }
  901. /** 获取当前墙体血量 */
  902. public getCurrentWallHealth(): number {
  903. return this.saveDataManager?.getPlayerData().wallBaseHealth || this.getWallHealthByLevel(1);
  904. }
  905. /** 升级墙体等级,返回升级后信息,失败返回null */
  906. public upgradeWallLevel(): { currentLevel: number; currentHp: number; nextLevel: number; nextHp: number } | null {
  907. if (!this.saveDataManager) return null;
  908. const pd = this.saveDataManager.getPlayerData();
  909. const curLvl = pd.playerLevel || 1;
  910. if (curLvl >= 5) return null; // 已达最高级
  911. const newLvl = curLvl + 1;
  912. const newHp = this.getWallHealthByLevel(newLvl);
  913. pd.playerLevel = newLvl;
  914. pd.wallBaseHealth = newHp;
  915. this.saveDataManager.savePlayerData(true);
  916. // 更新内存中的数值
  917. this.wallHealth = newHp;
  918. if (this.enemyController) {
  919. this.enemyController.wallHealth = newHp;
  920. this.enemyController.updateWallHealthDisplay?.();
  921. }
  922. return {
  923. currentLevel: newLvl,
  924. currentHp: newHp,
  925. nextLevel: newLvl + 1,
  926. nextHp: this.getWallHealthByLevel(newLvl + 1)
  927. };
  928. }
  929. }