GameManager.ts 40 KB

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