import { _decorator, Component, Node, Vec3, find, sp, Color } from 'cc'; import { BundleLoader } from '../../Core/BundleLoader'; import { Audio } from '../../AudioManager/AudioManager'; const { ccclass, property } = _decorator; /** * 武器特效管理器 * 负责处理敌人武器的击中特效、轨迹特效、音效等 * 挂载到Canvas/GameLevelUI/EnemyController上使用 */ @ccclass('WeaponEffectManager') export class WeaponEffectManager extends Component { // 当前播放中的特效节点列表 private activeEffects: Node[] = []; // 特效缓存 private effectCache: Map = new Map(); onLoad() { console.log('[WeaponEffectManager] 武器特效管理器已加载'); } onDestroy() { this.clearAllEffects(); } /** * 播放武器特效 * @param weaponEffects 武器特效配置 * @param position 特效播放位置 * @param attackType 攻击类型 */ public async playWeaponEffects(weaponEffects: any, position: Vec3, attackType: string) { if (!weaponEffects) { return; } // 播放击中特效 if (weaponEffects.hitEffect) { await this.playHitEffect(weaponEffects.hitEffect, position, weaponEffects.visualScale || 1.0); } // 播放轨迹特效(主要用于远程攻击) if (weaponEffects.trailEffect && (attackType === 'magic_projectile' || attackType === 'arrow_projectile')) { await this.playTrailEffect(weaponEffects.trailEffect, position, weaponEffects.visualScale || 1.0); } // 播放音效 if (weaponEffects.impactSound) { this.playImpactSound(weaponEffects.impactSound); } } /** * 播放击中特效 * @param effectName 特效名称 * @param position 播放位置 * @param scale 缩放比例 */ private async playHitEffect(effectName: string, position: Vec3, scale: number = 1.0) { const effectPath = this.getEffectPath(effectName); if (!effectPath) { console.warn(`[WeaponEffectManager] 未知的击中特效: ${effectName}`); return; } await this.createAndPlayEffect(effectPath, position, scale, false); } /** * 播放轨迹特效 * @param effectName 特效名称 * @param position 播放位置 * @param scale 缩放比例 */ private async playTrailEffect(effectName: string, position: Vec3, scale: number = 1.0) { const effectPath = this.getEffectPath(effectName); if (!effectPath) { console.warn(`[WeaponEffectManager] 未知的轨迹特效: ${effectName}`); return; } await this.createAndPlayEffect(effectPath, position, scale, false); } /** * 播放撞击音效 * @param soundName 音效名称 */ private playImpactSound(soundName: string) { const soundPath = this.getSoundPath(soundName); if (soundPath) { Audio.playWeaponSound(soundPath); } else { console.warn(`[WeaponEffectManager] 未知的撞击音效: ${soundName}`); } } /** * 获取特效路径 * @param effectName 特效名称 */ private getEffectPath(effectName: string): string | null { const effectPaths: { [key: string]: string } = { 'blood_splash': 'WeaponTx/tx0001/tx0001', 'magic_burst': 'WeaponTx/tx0002/tx0002', 'arrow_impact': 'WeaponTx/tx0003/tx0003', 'weapon_clash': 'WeaponTx/tx0004/tx0004', 'fire_explosion': 'WeaponTx/tx0005/tx0005', 'ice_shatter': 'WeaponTx/tx0006/tx0006', 'lightning_strike': 'WeaponTx/tx0007/tx0007', 'dark_energy': 'WeaponTx/tx0008/tx0008' }; return effectPaths[effectName] || null; } /** * 获取音效路径 * @param soundName 音效名称 */ private getSoundPath(soundName: string): string | null { const soundPaths: { [key: string]: string } = { 'melee_hit': 'data/弹球音效/melee_hit', 'magic_impact': 'data/弹球音效/magic_impact', 'arrow_hit': 'data/弹球音效/arrow_hit', 'weapon_clash': 'data/弹球音效/weapon_clash', 'explosion': 'data/弹球音效/explosion', 'ice_break': 'data/弹球音效/ice_break', 'lightning': 'data/弹球音效/lightning', 'dark_magic': 'data/弹球音效/dark_magic' }; return soundPaths[soundName] || null; } /** * 创建并播放特效 * @param effectPath 特效路径 * @param position 播放位置 * @param scale 缩放比例 * @param loop 是否循环播放 */ private async createAndPlayEffect(effectPath: string, position: Vec3, scale: number = 1.0, loop: boolean = false) { try { // 尝试从缓存获取特效数据 let skeletonData = this.effectCache.get(effectPath); if (!skeletonData) { // 从Bundle加载特效数据 skeletonData = await BundleLoader.loadSkeletonData(effectPath); if (!skeletonData) { console.warn(`[WeaponEffectManager] 加载特效失败: ${effectPath}`); return; } // 缓存特效数据 this.effectCache.set(effectPath, skeletonData); } // 创建特效节点 const effectNode = new Node('WeaponEffect'); const skeleton = effectNode.addComponent(sp.Skeleton); skeleton.skeletonData = skeletonData; skeleton.premultipliedAlpha = false; // 设置动画 skeleton.setAnimation(0, 'animation', loop); // 设置完成监听器(非循环动画) if (!loop) { skeleton.setCompleteListener(() => { this.removeEffect(effectNode); }); } // 添加到Canvas并设置位置和缩放 const canvas = find('Canvas'); if (canvas) { canvas.addChild(effectNode); effectNode.setWorldPosition(position); effectNode.setScale(scale, scale, 1); // 设置特效颜色(增加亮度) const color = new Color(255, 255, 255, 255); color.r = Math.min(255, color.r * 1.2); color.g = Math.min(255, color.g * 1.2); color.b = Math.min(255, color.b * 1.2); skeleton.color = color; // 添加到活动特效列表 this.activeEffects.push(effectNode); console.log(`[WeaponEffectManager] 播放武器特效: ${effectPath}`); } else { effectNode.destroy(); console.warn('[WeaponEffectManager] 未找到Canvas节点,无法播放特效'); } } catch (error) { console.error(`[WeaponEffectManager] 播放特效失败: ${effectPath}`, error); } } /** * 移除特效节点 * @param effectNode 要移除的特效节点 */ private removeEffect(effectNode: Node) { // 从活动特效列表中移除 const index = this.activeEffects.indexOf(effectNode); if (index !== -1) { this.activeEffects.splice(index, 1); } // 销毁节点 if (effectNode && effectNode.isValid) { effectNode.destroy(); } } /** * 清理所有活动的特效 */ public clearAllEffects() { for (const effect of this.activeEffects) { if (effect && effect.isValid) { effect.destroy(); } } this.activeEffects = []; console.log('[WeaponEffectManager] 清理所有武器特效'); } /** * 清理特效缓存 */ public clearEffectCache() { this.effectCache.clear(); console.log('[WeaponEffectManager] 清理特效缓存'); } }