import { _decorator, Node, Component, find } from 'cc'; import EventBus, { GameEvents } from '../Core/EventBus'; const { ccclass, property } = _decorator; /** * 敌人攻击状态管理器 * 使用字典结构快速查询敌人是否可以被攻击 * 采用标准单例模式,不依赖BaseSingleton */ @ccclass('EnemyAttackStateManager') export class EnemyAttackStateManager extends Component { private static _instance: EnemyAttackStateManager | null = null; // 敌人攻击状态字典:key为敌人节点的uuid,value为是否可攻击 private enemyAttackableStates: Map = new Map(); // 敌人节点引用字典:key为敌人节点的uuid,value为敌人节点 private enemyNodeReferences: Map = new Map(); // Debug(属性检查器展示) @property({ tooltip: '是否更新调试信息(属性检查器)' }) public showDebug: boolean = true; @property({ tooltip: '调试信息刷新间隔(秒)' }) public debugUpdateInterval: number = 0.5; // 以下字段通过装饰器在属性检查器中显示(只读调试) @property({ tooltip: '敌人总数(只读)' }) public totalEnemies: number = 0; @property({ tooltip: '可攻击敌人数(只读)' }) public attackableCount: number = 0; @property({ type: [String], tooltip: '示例敌人名称(最多10个)' }) public enemyNameSamples: string[] = []; @property({ tooltip: '字典状态预览(JSON,只读)' }) public statesPreviewJson: string = ''; // 组件加载时设置单例并注册事件 onLoad() { EnemyAttackStateManager._instance = this; this.setupEventListeners(); if (this.showDebug) { this.schedule(this.refreshInspectorDebug.bind(this), Math.max(0.1, this.debugUpdateInterval)); } this.refreshInspectorDebug(); } public static getInstance(): EnemyAttackStateManager { // 若已有实例则直接返回 if (EnemyAttackStateManager._instance) { return EnemyAttackStateManager._instance; } // 尝试从场景中获取或挂载到 Canvas/GameLevelUI/enemyContainer const enemyContainer = find('Canvas/GameLevelUI/enemyContainer'); if (enemyContainer) { let comp = enemyContainer.getComponent(EnemyAttackStateManager) as EnemyAttackStateManager; if (!comp) { comp = enemyContainer.addComponent(EnemyAttackStateManager) as EnemyAttackStateManager; } EnemyAttackStateManager._instance = comp; return comp; } // 如果未找到节点,返回一个占位的组件实例(不建议,但保证调用不崩溃) // 注意:占位实例无法可视化显示,仅用于避免空指针 const placeholder = new EnemyAttackStateManager(); EnemyAttackStateManager._instance = placeholder as any; return EnemyAttackStateManager._instance; } private setupEventListeners() { const eventBus = EventBus.getInstance(); // 监听敌人隐身开始事件 eventBus.on(GameEvents.ENEMY_STEALTH_START, this.onEnemyStealthStart, this); // 监听敌人隐身结束事件 eventBus.on(GameEvents.ENEMY_STEALTH_END, this.onEnemyStealthEnd, this); // 监听敌人死亡事件 eventBus.on('ENEMY_KILLED', this.onEnemyKilled, this); // 监听清除所有敌人事件 eventBus.on(GameEvents.CLEAR_ALL_ENEMIES, this.onClearAllEnemies, this); } /** * 注册敌人到攻击状态管理器 * @param enemyNode 敌人节点 * @param isAttackable 初始攻击状态,默认为true */ public registerEnemy(enemyNode: Node, isAttackable: boolean = true) { if (!enemyNode || !enemyNode.isValid) return; const enemyId = enemyNode.uuid; this.enemyAttackableStates.set(enemyId, isAttackable); this.enemyNodeReferences.set(enemyId, enemyNode); console.log(`[EnemyAttackStateManager] 注册敌人: ${enemyNode.name} (${enemyId}), 可攻击: ${isAttackable}`); this.refreshInspectorDebug(); } /** * 从攻击状态管理器中移除敌人 * @param enemyNode 敌人节点 */ public unregisterEnemy(enemyNode: Node) { if (!enemyNode) return; const enemyId = enemyNode.uuid; this.enemyAttackableStates.delete(enemyId); this.enemyNodeReferences.delete(enemyId); console.log(`[EnemyAttackStateManager] 移除敌人: ${enemyNode.name} (${enemyId})`); this.refreshInspectorDebug(); } /** * 设置敌人的攻击状态 * @param enemyNode 敌人节点 * @param attackable 是否可攻击 */ public setEnemyAttackable(enemyNode: Node, attackable: boolean): void { if (!enemyNode || !enemyNode.isValid) return; const enemyId = enemyNode.uuid; const previousState = this.enemyAttackableStates.get(enemyId); this.enemyAttackableStates.set(enemyId, attackable); // 添加状态变化日志 if (previousState !== attackable) { const stateText = attackable ? '可攻击' : '不可攻击'; const reasonText = attackable ? '(退出隐身)' : '(进入隐身)'; console.log(`[EnemyAttackStateManager] 敌人状态变化: ${enemyNode.name} (${enemyId.substring(0, 8)}...) -> ${stateText} ${reasonText}`); } this.refreshInspectorDebug(); } /** * 检查敌人是否可以被攻击 * @param enemyNode 敌人节点 * @returns 是否可攻击 */ public isEnemyAttackable(enemyNode: Node): boolean { if (!enemyNode || !enemyNode.isValid) return false; const enemyId = enemyNode.uuid; return this.enemyAttackableStates.get(enemyId) ?? false; } /** * 获取所有可攻击的敌人节点 * @returns 可攻击的敌人节点数组 */ public getAttackableEnemies(): Node[] { const attackableEnemies: Node[] = []; for (const [enemyId, isAttackable] of this.enemyAttackableStates.entries()) { if (isAttackable) { const enemyNode = this.enemyNodeReferences.get(enemyId); if (enemyNode && enemyNode.isValid) { attackableEnemies.push(enemyNode); } else { // 清理无效的引用 this.enemyAttackableStates.delete(enemyId); this.enemyNodeReferences.delete(enemyId); } } } return attackableEnemies; } /** * 清理所有敌人状态 */ public clearAllEnemies() { this.enemyAttackableStates.clear(); this.enemyNodeReferences.clear(); console.log(`[EnemyAttackStateManager] 清理所有敌人状态`); this.refreshInspectorDebug(); } /** * 获取当前管理的敌人数量 */ public getEnemyCount(): number { return this.enemyAttackableStates.size; } /** * 获取可攻击敌人数量 */ public getAttackableEnemyCount(): number { let count = 0; for (const isAttackable of this.enemyAttackableStates.values()) { if (isAttackable) count++; } return count; } // 事件处理方法 private onEnemyStealthStart(data: { enemy: any, duration?: number }) { if (data.enemy && data.enemy.node) { this.setEnemyAttackable(data.enemy.node, false); console.log(`[EnemyAttackStateManager] 敌人进入隐身状态: ${data.enemy.node.name}`); } } private onEnemyStealthEnd(data: { enemy: any }) { if (data.enemy && data.enemy.node) { this.setEnemyAttackable(data.enemy.node, true); console.log(`[EnemyAttackStateManager] 敌人退出隐身状态: ${data.enemy.node.name}`); } } private onEnemyKilled() { // 注意:敌人节点的移除已经在EnemyInstance.onDestroy()中通过unregisterEnemy()处理 // 这个事件处理器主要用于其他需要响应敌人死亡的逻辑 console.log(`[EnemyAttackStateManager] 接收到敌人死亡事件`); } private onClearAllEnemies() { this.clearAllEnemies(); } onDestroy() { const eventBus = EventBus.getInstance(); // 移除所有事件监听器 eventBus.off(GameEvents.ENEMY_STEALTH_START, this.onEnemyStealthStart, this); eventBus.off(GameEvents.ENEMY_STEALTH_END, this.onEnemyStealthEnd, this); eventBus.off('ENEMY_KILLED', this.onEnemyKilled, this); eventBus.off(GameEvents.CLEAR_ALL_ENEMIES, this.onClearAllEnemies, this); // 清空所有数据 this.enemyAttackableStates.clear(); this.enemyNodeReferences.clear(); // 清空单例实例 EnemyAttackStateManager._instance = null; } // === Debug(属性检查器) === private refreshInspectorDebug() { if (!this.showDebug) return; // 概览统计 this.totalEnemies = this.getEnemyCount(); this.attackableCount = this.getAttackableEnemyCount(); // 示例名称(最多10个) const samples: string[] = []; let i = 0; for (const [, enemyNode] of this.enemyNodeReferences.entries()) { if (enemyNode && enemyNode.isValid) { samples.push(enemyNode.name); i++; if (i >= 10) break; } } this.enemyNameSamples = samples; // 预览字典(最多20条,JSON字符串) const previewItems: Array<{ name: string; id: string; attackable: boolean }> = []; i = 0; for (const [enemyId, isAttackable] of this.enemyAttackableStates.entries()) { const enemyNode = this.enemyNodeReferences.get(enemyId); const name = enemyNode && enemyNode.isValid ? enemyNode.name : '(invalid)'; previewItems.push({ name, id: enemyId.substring(0, 8), attackable: !!isAttackable }); i++; if (i >= 20) break; } this.statesPreviewJson = JSON.stringify(previewItems, null, 2); } }