| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- import { _decorator, Component, Node, Vec3, director, sp, NodePool, find } from 'cc';
- const { ccclass, property } = _decorator;
- export enum AttackEffectType {
- Mage = 'mage',
- Archer = 'archer',
- }
- @ccclass('EnemyAttackEffectManager')
- export class EnemyAttackEffectManager extends Component {
- public static instance: EnemyAttackEffectManager = null;
- @property({ type: sp.SkeletonData })
- public mageImpactEffect: sp.SkeletonData = null;
- @property({ type: sp.SkeletonData })
- public archerImpactEffect: sp.SkeletonData = null;
- @property
- public mageImpactAnimName: string = 'hit';
- @property
- public archerImpactAnimName: string = 'hit';
- @property
- public effectScale: number = 1.0;
- private magePool: NodePool = new NodePool();
- private archerPool: NodePool = new NodePool();
- onLoad() {
- EnemyAttackEffectManager.instance = this;
- console.log(`[EnemyAttackEffectManager] 组件已加载,挂载节点: ${this.node?.name}`);
- }
- /**
- * 在指定世界坐标播放墙体命中特效
- */
- public playImpactEffect(type: AttackEffectType, worldPos: Vec3): void {
- const parent = this.resolveEffectParent();
- if (!parent) {
- console.warn('[EnemyAttackEffectManager] 未找到特效父节点,取消播放');
- return;
- }
- const node = this.getEffectNode(type);
- console.log(`[EnemyAttackEffectManager] 准备播放命中特效: type=${type}, 位置=(${worldPos.x.toFixed(1)}, ${worldPos.y.toFixed(1)}),父节点=${parent.name}`);
- parent.addChild(node);
- node.setWorldPosition(worldPos);
- const skeleton = node.getComponent(sp.Skeleton);
- if (!skeleton) {
- // 没有Spine组件,不播放(安全返回)
- console.warn('[EnemyAttackEffectManager] 节点无sp.Skeleton组件,跳过播放并回收');
- this.recycleNode(type, node);
- return;
- }
- if (!skeleton.skeletonData) {
- console.warn('[EnemyAttackEffectManager] 未绑定SkeletonData资源,无法播放特效');
- this.recycleNode(type, node);
- return;
- }
- const animName = type === AttackEffectType.Mage ? this.mageImpactAnimName : this.archerImpactAnimName;
- if (animName) {
- console.log(`[EnemyAttackEffectManager] setAnimation: ${animName}`);
- skeleton.setAnimation(0, animName, false);
- skeleton.setCompleteListener(() => {
- console.log('[EnemyAttackEffectManager] 特效播放完成,回收节点');
- this.recycleNode(type, node);
- });
- } else {
- // 未提供动画名时,直接回收
- console.warn('[EnemyAttackEffectManager] 未提供动画名,直接回收节点');
- this.recycleNode(type, node);
- }
- }
- private resolveEffectParent(): Node | null {
- // 优先挂在GameArea,保障层级正确
- const parent = find('Canvas/GameLevelUI/GameArea') || find('Canvas/GameLevelUI') || director.getScene();
- if (parent) {
- console.log(`[EnemyAttackEffectManager] 选择特效父节点: ${parent.name}`);
- }
- return parent;
- }
- private getEffectNode(type: AttackEffectType): Node {
- const pool = type === AttackEffectType.Mage ? this.magePool : this.archerPool;
- const data = type === AttackEffectType.Mage ? this.mageImpactEffect : this.archerImpactEffect;
- let node = pool.get();
- if (!node) {
- node = new Node(`EnemyWallImpact_${type}`);
- const skeleton = node.addComponent(sp.Skeleton);
- skeleton.skeletonData = data;
- console.log(`[EnemyAttackEffectManager] 新建特效节点: ${node.name},绑定数据=${data ? '有' : '无'}`);
- } else {
- const skeleton = node.getComponent(sp.Skeleton);
- if (skeleton) skeleton.skeletonData = data;
- console.log(`[EnemyAttackEffectManager] 复用池中节点: ${node.name},重新绑定数据=${data ? '有' : '无'}`);
- }
- // 统一缩放到 X/Y 轴,Z 轴保持 1
- node.setScale(new Vec3(this.effectScale, this.effectScale, 1));
- console.log(`[EnemyAttackEffectManager] 设置缩放: ${this.effectScale}`);
- return node;
- }
- private recycleNode(type: AttackEffectType, node: Node): void {
- if (!node || !node.isValid) return;
- node.removeFromParent();
- const pool = type === AttackEffectType.Mage ? this.magePool : this.archerPool;
- pool.put(node);
- console.log(`[EnemyAttackEffectManager] 节点已回收到对象池: ${node.name}`);
- }
- }
|