import { _decorator, Node, Label, Vec3, Prefab, instantiate, find, UITransform, resources, RigidBody2D } from 'cc'; import { sp } from 'cc'; import { ConfigManager, EnemyConfig } from '../Core/ConfigManager'; import { EnemyComponent } from '../CombatSystem/EnemyComponent'; import { EnemyInstance } from './EnemyInstance'; import { BaseSingleton } from '../Core/BaseSingleton'; import { SaveDataManager } from '../LevelSystem/SaveDataManager'; import { GamePause } from './GamePause'; import EventBus, { GameEvents } from '../Core/EventBus'; const { ccclass, property } = _decorator; // 前向声明EnemyInstance类型,避免循环引用 class EnemyInstanceType { public health: number; public maxHealth: number; public speed: number; public attackPower: number; public movingDirection: number; public targetY: number; public changeDirectionTime: number; public controller: any; public node: Node; public updateHealthDisplay: () => void; public takeDamage: (damage: number) => void; } @ccclass('EnemyController') export class EnemyController extends BaseSingleton { // 仅类型声明,实例由 BaseSingleton 维护 public static _instance: EnemyController; // 敌人预制体 @property({ type: Prefab, tooltip: '拖拽Enemy预制体到这里' }) public enemyPrefab: Prefab = null; // 敌人容器节点 @property({ type: Node, tooltip: '拖拽enemyContainer节点到这里(Canvas/GameLevelUI/enemyContainer)' }) public enemyContainer: Node = null; // 金币预制体 @property({ type: Prefab, tooltip: '金币预制体 CoinDrop' }) public coinPrefab: Prefab = null; // === 生成 & 属性参数(保留需要可在内部自行设定,Inspector 不再显示) === private spawnInterval: number = 3; // === 默认数值(当配置文件尚未加载时使用) === private defaultEnemySpeed: number = 50; private defaultAttackPower: number = 10; private defaultHealth: number = 30; // 墙体属性 // 最终数值将在 init() 中从存档或 GameManager 注入 public wallHealth: number = 0; // 墙体血量显示节点(Inspector 拖拽 HeartLabel 节点到此) @property({ type: Node, tooltip: '墙体血量 Label 节点 (HeartLabel)' }) public wallHealthNode: Node = null; // 游戏区域边界 - 改为public,让敌人实例可以访问 public gameBounds = { left: 0, right: 0, top: 0, bottom: 0 }; // 活跃的敌人列表 private activeEnemies: Node[] = []; // 游戏是否已开始 private gameStarted: boolean = false; // 墙体节点 private wallNodes: Node[] = []; // 配置管理器 private configManager: ConfigManager = null; @property({ type: Node, tooltip: '敌人数量显示节点 (EnemyNumber)' }) public enemyCountLabelNode: Node = null; @property({ type: Node, tooltip: '波数显示Label (WaveNumber)' }) public waveNumberLabelNode: Node = null; @property({ type: Node, tooltip: '每波开始提示UI节点 (StartWaveUI)' }) public startWaveUI: Node = null; private totalWaves: number = 1; private currentWave: number = 1; private currentWaveTotalEnemies: number = 0; private currentWaveEnemiesKilled: number = 0; // 暂停前保存的敌人状态 private pausedEnemyStates: Map = new Map(); /** * BaseSingleton 首次实例化回调 */ protected init() { // 获取配置管理器实例 this.configManager = ConfigManager.getInstance(); // 如果没有指定enemyContainer,尝试找到它 if (!this.enemyContainer) { this.enemyContainer = find('Canvas/GameLevelUI/enemyContainer'); if (!this.enemyContainer) { console.warn('找不到enemyContainer节点,将尝试创建'); } } // 获取游戏区域边界 this.calculateGameBounds(); // 查找墙体节点 this.findWallNodes(); // 从存档读取墙体基础血量,如果有的话 const sdm = SaveDataManager.getInstance(); const base = (sdm.getPlayerData() as any)?.wallBaseHealth; if (typeof base === 'number' && base > 0) this.wallHealth = base; // 初始化墙体血量显示 this.initWallHealthDisplay(); // 确保enemyContainer节点存在 this.ensureEnemyContainer(); // 如果没有指定enemyCountLabelNode,尝试找到它 if (!this.enemyCountLabelNode) { this.enemyCountLabelNode = find('Canvas/GameLevelUI/EnemyNode/EnemyNumber'); } if (!this.waveNumberLabelNode) { this.waveNumberLabelNode = find('Canvas/GameLevelUI/WaveInfo/WaveNumber'); } if (!this.startWaveUI) { this.startWaveUI = find('Canvas/GameLevelUI/StartWaveUI') || find('Canvas/GameLevelUI/NextWaveUI'); } // 初始化敌人数量显示 this.updateEnemyCountLabel(); // 监听游戏事件 this.setupEventListeners(); } /** * 设置事件监听器 */ private setupEventListeners() { const eventBus = EventBus.getInstance(); // 监听暂停事件 eventBus.on(GameEvents.GAME_PAUSE, this.onGamePauseEvent, this); // 监听恢复事件 eventBus.on(GameEvents.GAME_RESUME, this.onGameResumeEvent, this); // 监听游戏成功/失败事件 eventBus.on(GameEvents.GAME_SUCCESS, this.onGameEndEvent, this); eventBus.on(GameEvents.GAME_DEFEAT, this.onGameEndEvent, this); } /** * 处理游戏暂停事件 */ private onGamePauseEvent() { console.log('[EnemyController] 接收到游戏暂停事件,暂停所有敌人'); this.pauseAllEnemies(); this.pauseSpawning(); } /** * 处理游戏恢复事件 */ private onGameResumeEvent() { console.log('[EnemyController] 接收到游戏恢复事件,恢复所有敌人'); this.resumeAllEnemies(); // 检查游戏是否已经结束,如果结束则不恢复敌人生成 const gamePause = GamePause.getInstance(); if (!gamePause.isGameOver()) { this.resumeSpawning(); } } /** * 处理游戏结束事件 */ private onGameEndEvent() { console.log('[EnemyController] 接收到游戏结束事件,停止敌人生成'); this.stopGame(false); // 停止游戏但不清除敌人 } /** * 暂停所有敌人 */ private pauseAllEnemies(): void { const activeEnemies = this.getActiveEnemies(); for (const enemy of activeEnemies) { if (!enemy || !enemy.isValid) continue; // 保存敌人状态 const rigidBody = enemy.getComponent(RigidBody2D); if (rigidBody) { this.pausedEnemyStates.set(enemy, { velocity: rigidBody.linearVelocity.clone(), angularVelocity: rigidBody.angularVelocity, isMoving: true, attackTimer: 0 // 可以扩展保存攻击计时器 }); // 停止敌人运动 rigidBody.linearVelocity.set(0, 0); rigidBody.angularVelocity = 0; rigidBody.sleep(); } // 暂停敌人AI组件(如果有的话) const enemyAI = enemy.getComponent('EnemyAI'); if (enemyAI && typeof (enemyAI as any).pause === 'function') { (enemyAI as any).pause(); } // 暂停敌人动画(如果有的话) const animation = enemy.getComponent('Animation'); if (animation && typeof (animation as any).pause === 'function') { (animation as any).pause(); } } } /** * 恢复所有敌人 */ private resumeAllEnemies(): void { const activeEnemies = this.getActiveEnemies(); for (const enemy of activeEnemies) { if (!enemy || !enemy.isValid) continue; const savedState = this.pausedEnemyStates.get(enemy); if (savedState) { const rigidBody = enemy.getComponent(RigidBody2D); if (rigidBody) { // 恢复敌人运动 rigidBody.wakeUp(); rigidBody.linearVelocity = savedState.velocity; rigidBody.angularVelocity = savedState.angularVelocity; } } // 恢复敌人AI组件 const enemyAI = enemy.getComponent('EnemyAI'); if (enemyAI && typeof (enemyAI as any).resume === 'function') { (enemyAI as any).resume(); } // 恢复敌人动画 const animation = enemy.getComponent('Animation'); if (animation && typeof (animation as any).resume === 'function') { (animation as any).resume(); } } // 清空保存的状态 this.pausedEnemyStates.clear(); } // 计算游戏区域边界 private calculateGameBounds() { const gameArea = find('Canvas/GameLevelUI/GameArea'); if (!gameArea) { return; } const uiTransform = gameArea.getComponent(UITransform); if (!uiTransform) { return; } const worldPos = gameArea.worldPosition; const width = uiTransform.width; const height = uiTransform.height; this.gameBounds = { left: worldPos.x - width / 2, right: worldPos.x + width / 2, top: worldPos.y + height / 2, bottom: worldPos.y - height / 2 }; } // 查找墙体节点 private findWallNodes() { const gameArea = find('Canvas/GameLevelUI/GameArea'); if (gameArea) { this.wallNodes = []; for (let i = 0; i < gameArea.children.length; i++) { const child = gameArea.children[i]; if (child.name.includes('Wall') || child.name.includes('wall') || child.name.includes('墙')) { this.wallNodes.push(child); } } } } // 初始化墙体血量显示 initWallHealthDisplay() { if (this.wallHealthNode) { this.updateWallHealthDisplay(); } else { console.warn('EnemyController 未绑定 HeartLabel 节点,请在 Inspector 中拖拽 Canvas/GameLevelUI/HeartNode/HeartLabel'); } } // 更新墙体血量显示 public updateWallHealthDisplay() { if (!this.wallHealthNode) return; // 直接在当前节点或其子节点寻找 Label let heartLabel = this.wallHealthNode.getComponent(Label); if (!heartLabel) { heartLabel = this.wallHealthNode.getComponentInChildren(Label); } if (heartLabel) { heartLabel.string = this.wallHealth.toString(); } } // 确保enemyContainer节点存在 ensureEnemyContainer() { // 如果已经通过拖拽设置了节点,直接使用 if (this.enemyContainer && this.enemyContainer.isValid) { return; } // 尝试查找节点 this.enemyContainer = find('Canvas/GameLevelUI/enemyContainer'); if (this.enemyContainer) { console.log('找到已存在的enemyContainer节点'); return; } // 如果找不到,创建新节点 const gameLevelUI = find('Canvas/GameLevelUI'); if (!gameLevelUI) { console.error('找不到GameLevelUI节点,无法创建enemyContainer'); return; } this.enemyContainer = new Node('enemyContainer'); gameLevelUI.addChild(this.enemyContainer); if (!this.enemyContainer.getComponent(UITransform)) { this.enemyContainer.addComponent(UITransform); } console.log('已在GameLevelUI下创建enemyContainer节点'); } // 游戏开始 startGame() { // 检查游戏是否已经结束,如果结束则不开始生成敌人 const gamePause = GamePause.getInstance(); if (gamePause.isGameOver()) { console.log('[EnemyController] 游戏已经结束,不启动敌人生成'); return; } this.gameStarted = true; // 确保enemyContainer节点存在 this.ensureEnemyContainer(); // 开始生成敌人 this.schedule(this.spawnEnemy, this.spawnInterval); console.log('开始生成敌人'); } // 游戏结束 stopGame(clearEnemies: boolean = true) { this.gameStarted = false; // 停止生成敌人 this.unschedule(this.spawnEnemy); // 只有在指定时才清除所有敌人 if (clearEnemies) { // 当游戏结束时清除敌人,不触发敌人死亡事件 const gamePause = GamePause.getInstance(); const isGameOver = gamePause.isGameOver(); this.clearAllEnemies(!isGameOver); // 只有在游戏没有结束时才触发事件 } console.log('停止生成敌人'); } // 生成敌人 spawnEnemy() { if (!this.gameStarted || !this.enemyPrefab) return; // 检查游戏是否已经结束,如果结束则不生成敌人 const gamePause = GamePause.getInstance(); if (gamePause.isGameOver()) { console.log('[EnemyController] 游戏已经结束,不再生成敌人'); this.unschedule(this.spawnEnemy); // 取消定时生成 return; } // 随机决定从上方还是下方生成 const fromTop = Math.random() > 0.5; // 实例化敌人 const enemy = instantiate(this.enemyPrefab); enemy.name = 'Enemy'; // 确保敌人节点名称为Enemy // 添加到场景中 const enemyContainer = find('Canvas/GameLevelUI/enemyContainer'); if (!enemyContainer) { return; } enemyContainer.addChild(enemy); // 生成在对应线(Line1 / Line2)上的随机位置 const lineName = fromTop ? 'Line1' : 'Line2'; const lineNode = enemyContainer.getChildByName(lineName); if (!lineNode) { console.warn(`[EnemyController] 未找到 ${lineName} 节点,取消本次敌人生成`); enemy.destroy(); return; } // 在对应 line 上随机 X 坐标 const spawnWorldX = this.gameBounds.left + Math.random() * (this.gameBounds.right - this.gameBounds.left); const spawnWorldY = lineNode.worldPosition.y; const worldPos = new Vec3(spawnWorldX, spawnWorldY, 0); const localPos = enemyContainer.getComponent(UITransform).convertToNodeSpaceAR(worldPos); enemy.position = localPos; // === 根据配置设置敌人 === const enemyComp = enemy.addComponent(EnemyInstance); let enemyConfig: EnemyConfig = null; if (this.configManager && this.configManager.isConfigLoaded()) { enemyConfig = this.configManager.getRandomEnemy(); } if (enemyConfig) { // 添加EnemyComponent保存配置 const cfgComp = enemy.addComponent(EnemyComponent); cfgComp.enemyConfig = enemyConfig; cfgComp.spawner = this; // 应用数值 enemyComp.health = enemyConfig.stats.health; enemyComp.maxHealth = enemyConfig.stats.health; enemyComp.speed = enemyConfig.stats.speed; enemyComp.attackPower = enemyConfig.stats.damage; // 加载动画 this.loadEnemyAnimation(enemy, enemyConfig); } else { // 使用默认值 enemyComp.health = this.defaultHealth; enemyComp.maxHealth = this.defaultHealth; enemyComp.speed = this.defaultEnemySpeed; enemyComp.attackPower = this.defaultAttackPower; } // 额外的属性设置 enemyComp.spawnFromTop = fromTop; enemyComp.targetFence = find(fromTop ? 'Canvas/GameLevelUI/GameArea/TopFence' : 'Canvas/GameLevelUI/GameArea/BottomFence'); enemyComp.movingDirection = Math.random() > 0.5 ? 1 : -1; enemyComp.targetY = fromTop ? this.gameBounds.top - 50 : this.gameBounds.bottom + 50; enemyComp.changeDirectionTime = 0; enemyComp.controller = this; // 更新敌人血量显示 enemyComp.updateHealthDisplay(); // 添加到活跃敌人列表 this.activeEnemies.push(enemy); // 更新敌人数量显示 this.updateEnemyCountLabel(); console.log(`生成敌人,当前共有 ${this.activeEnemies.length} 个敌人`); } // 清除所有敌人 clearAllEnemies(triggerEvents: boolean = true) { // 如果不触发事件,先暂时禁用 notifyEnemyDead 方法 const originalNotifyMethod = this.notifyEnemyDead; if (!triggerEvents) { // 临时替换为空函数 this.notifyEnemyDead = () => {}; console.log('[EnemyController] 临时禁用敌人死亡通知'); } for (const enemy of this.activeEnemies) { if (enemy && enemy.isValid) { enemy.destroy(); } } // 恢复原来的方法 if (!triggerEvents) { this.notifyEnemyDead = originalNotifyMethod; console.log('[EnemyController] 恢复敌人死亡通知'); } this.activeEnemies = []; // 更新敌人数量显示 this.updateEnemyCountLabel(); } // 获取所有活跃的敌人 getActiveEnemies(): Node[] { // 过滤掉已经无效的敌人 this.activeEnemies = this.activeEnemies.filter(enemy => enemy && enemy.isValid); return this.activeEnemies; } // 获取当前敌人数量 getCurrentEnemyCount(): number { return this.getActiveEnemies().length; } // 获取最近的敌人节点 public getNearestEnemy(fromPosition: Vec3): Node | null { const enemies = this.getActiveEnemies(); if (enemies.length === 0) return null; let nearestEnemy: Node = null; let nearestDistance = Infinity; for (const enemy of enemies) { const distance = Vec3.distance(fromPosition, enemy.worldPosition); if (distance < nearestDistance) { nearestDistance = distance; nearestEnemy = enemy; } } return nearestEnemy; } // 检查是否有活跃敌人 public hasActiveEnemies(): boolean { return this.getActiveEnemies().length > 0; } // 获取游戏是否已开始状态 public isGameStarted(): boolean { return this.gameStarted; } // 暂停生成敌人 public pauseSpawning(): void { if (this.gameStarted) { console.log('暂停生成敌人'); this.unschedule(this.spawnEnemy); } } // 恢复生成敌人 public resumeSpawning(): void { // 检查游戏是否已经结束,如果结束则不恢复生成敌人 const gamePause = GamePause.getInstance(); if (gamePause.isGameOver()) { console.log('[EnemyController] 游戏已经结束,不恢复敌人生成'); return; } if (this.gameStarted) { this.schedule(this.spawnEnemy, this.spawnInterval); } } // 敌人受到伤害 damageEnemy(enemy: Node, damage: number) { if (!enemy || !enemy.isValid) return; // 检查游戏是否已经结束 const gamePause = GamePause.getInstance(); if (gamePause.isGameOver()) { console.warn('[EnemyController] 游戏已经结束,不再处理敌人伤害'); return; } // 获取敌人组件 const enemyComp = enemy.getComponent(EnemyInstance); if (!enemyComp) return; // 减少敌人血量 enemyComp.takeDamage(damage); // 检查敌人是否死亡 if (enemyComp.health <= 0) { // 从活跃敌人列表中移除 const index = this.activeEnemies.indexOf(enemy); if (index !== -1) { this.activeEnemies.splice(index, 1); } // 更新敌人数量显示 this.updateEnemyCountLabel(); // 销毁敌人 enemy.destroy(); } } // 墙体受到伤害 damageWall(damage: number) { // 减少墙体血量 this.wallHealth -= damage; // 更新墙体血量显示 this.updateWallHealthDisplay(); // 检查墙体是否被摧毁 if (this.wallHealth <= 0) { // 游戏结束 this.gameOver(); } } // 游戏结束 gameOver() { // 停止游戏,但不清除敌人 this.stopGame(false); // 通知GamePause触发游戏失败 const gamePause = GamePause.getInstance(); gamePause.triggerGameDefeat(); } update(dt: number) { if (!this.gameStarted) return; // 更新所有敌人 for (let i = this.activeEnemies.length - 1; i >= 0; i--) { const enemy = this.activeEnemies[i]; if (!enemy || !enemy.isValid) { this.activeEnemies.splice(i, 1); continue; } // 敌人更新由各自的组件处理 // 不再需要检查敌人是否到达墙体,因为敌人到达游戏区域后会自动攻击 // 敌人的攻击逻辑已经在EnemyInstance中处理 } } // === 调试方法 === public testEnemyAttack() { console.log('=== 测试敌人攻击墙体 ==='); // 手动触发墙体受到伤害 const testDamage = 50; console.log(`模拟敌人攻击,伤害: ${testDamage}`); this.damageWall(testDamage); return this.wallHealth; } public getCurrentWallHealth(): number { return this.wallHealth; } public forceEnemyAttack() { console.log('=== 强制所有敌人进入攻击状态 ==='); const activeEnemies = this.getActiveEnemies(); console.log(`当前活跃敌人数量: ${activeEnemies.length}`); for (const enemy of activeEnemies) { const enemyComp = enemy.getComponent(EnemyInstance); if (enemyComp) { console.log(`强制敌人 ${enemy.name} 进入攻击状态`); // 直接调用damageWall方法进行测试 this.damageWall(enemyComp.attackPower); } } } /** 供 EnemyInstance 在 onDestroy 中调用 */ public notifyEnemyDead(enemyNode?: Node) { if (enemyNode) { const idx = this.activeEnemies.indexOf(enemyNode); if (idx !== -1) this.activeEnemies.splice(idx, 1); this.currentWaveEnemiesKilled++; this.updateEnemyCountLabel(); } // 通知GamePause处理敌人被击杀 const gamePause = GamePause.getInstance(); gamePause.onEnemyKilled(); } /** * 加载敌人骨骼动画 */ private loadEnemyAnimation(enemyNode: Node, enemyConfig: EnemyConfig) { let spinePath: string | undefined = enemyConfig.visualConfig?.spritePrefab; if (!spinePath) return; if (spinePath.startsWith('@EnemyAni')) { spinePath = spinePath.replace('@EnemyAni', 'Animation/EnemyAni'); } if (spinePath.startsWith('@')) { spinePath = spinePath.substring(1); } resources.load(spinePath, sp.SkeletonData, (err, skeletonData) => { if (err) { console.warn(`加载敌人Spine动画失败: ${spinePath}`, err); return; } let skeleton = enemyNode.getComponent(sp.Skeleton); if (!skeleton) { skeleton = enemyNode.addComponent(sp.Skeleton); } skeleton.skeletonData = skeletonData; const anims = enemyConfig.visualConfig.animations; const walkName = anims?.walk ?? 'walk'; const idleName = anims?.idle ?? 'idle'; if (skeleton.findAnimation(walkName)) { skeleton.setAnimation(0, walkName, true); } else if (skeleton.findAnimation(idleName)) { skeleton.setAnimation(0, idleName, true); } }); } // 更新敌人数量显示 private updateEnemyCountLabel() { if (!this.enemyCountLabelNode) return; const label = this.enemyCountLabelNode.getComponent(Label); if (label) { const remaining = Math.max(0, this.currentWaveTotalEnemies - this.currentWaveEnemiesKilled); label.string = remaining.toString(); console.log(`EnemyController 剩余敌人数量: ${remaining}`); } } public startWave(waveNum: number, totalWaves: number, totalEnemies: number) { this.currentWave = waveNum; this.totalWaves = totalWaves; this.currentWaveTotalEnemies = totalEnemies; this.currentWaveEnemiesKilled = 0; this.updateWaveLabel(); this.updateEnemyCountLabel(); if (this.startWaveUI) this.startWaveUI.active = false; } private updateWaveLabel() { if (!this.waveNumberLabelNode) return; const label = this.waveNumberLabelNode.getComponent(Label); if (label) { label.string = `${this.currentWave}/${this.totalWaves}`; } } /** 显示每波开始提示,随后开启敌人生成 */ public showStartWavePromptUI(duration: number = 2) { // 检查游戏是否已经结束,如果结束则不显示波次提示 const gamePause = GamePause.getInstance(); if (gamePause.isGameOver()) { console.log('[EnemyController] 游戏已经结束,不显示波次提示'); return; } if (!this.startWaveUI) return; this.startWaveUI.active = true; // 暂停生成(确保未重复) this.pauseSpawning(); if (duration > 0) { this.scheduleOnce(() => { if (this.startWaveUI) this.startWaveUI.active = false; // 再次检查游戏是否已经结束,如果结束则不开始生成敌人 const gamePauseCheck = GamePause.getInstance(); if (gamePauseCheck.isGameOver()) { console.log('[EnemyController] 游戏已经结束,不启动敌人生成(延时检查)'); return; } // 真正开始/恢复生成敌人 this.startGame(); }, duration); } else { this.startGame(); } } onDestroy() { // 清理事件监听 const eventBus = EventBus.getInstance(); eventBus.off(GameEvents.GAME_PAUSE, this.onGamePauseEvent, this); eventBus.off(GameEvents.GAME_RESUME, this.onGameResumeEvent, this); eventBus.off(GameEvents.GAME_SUCCESS, this.onGameEndEvent, this); eventBus.off(GameEvents.GAME_DEFEAT, this.onGameEndEvent, this); // 清空状态 this.pausedEnemyStates.clear(); } }