GameManager.ts 40 KB

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