import { _decorator, Component, Node, ProgressBar, Label, Vec3, find, UITransform, Collider2D, Contact2DType, IPhysics2DContact, instantiate, resources, Prefab, JsonAsset, RigidBody2D, ERigidBody2DType, BoxCollider2D, CircleCollider2D } from 'cc'; import { JsonConfigLoader } from '../Core/JsonConfigLoader'; import { sp } from 'cc'; import { DamageNumberAni } from '../Animations/DamageNumberAni'; import { HPBarAnimation } from '../Animations/HPBarAnimation'; import { EnemyComponent } from './EnemyComponent'; import { EnemyAudio } from '../AudioManager/EnemyAudios'; const { ccclass, property } = _decorator; // 前向声明EnemyController接口,避免循环引用 interface EnemyControllerType { gameBounds: { left: number; right: number; top: number; bottom: number; }; damageWall: (damage: number) => void; getComponent: (componentType: any) => any; } // 敌人状态枚举 enum EnemyState { DRIFTING, // 漂移中(从生成位置移动到线上) MOVING, // 移动中 ATTACKING, // 攻击中 DEAD // 死亡 } // 单个敌人实例的组件 @ccclass('EnemyInstance') export class EnemyInstance extends Component { // 敌人属性(从配置文件读取) public health: number = 0; public maxHealth: number = 0; public speed: number = 0; public attackPower: number = 0; // 敌人配置ID public enemyId: string = ''; // 敌人配置数据 private enemyConfig: any = null; // 敌人配置数据库 private static enemyDatabase: any = null; // === 新增属性 === /** 是否从上方生成 */ public spawnFromTop: boolean = true; /** 目标 Fence 节点(TopFence / BottomFence) */ public targetFence: Node | null = null; /** 目标位置(墙体上的随机点) */ public targetPosition: Vec3 | null = null; /** 漂移目标位置(线上的位置) */ public driftTargetPosition: Vec3 | null = null; // 移动相关属性 public movingDirection: number = 1; // 1: 向右, -1: 向左 public targetY: number = 0; // 目标Y位置 public changeDirectionTime: number = 0; // 下次改变方向的时间 // 摆动移动相关属性 private swayTime: number = 0; // 摆动时间累计 private basePosition: Vec3 = new Vec3(); // 基础位置(摆动的中心点) private swayOffset: Vec3 = new Vec3(); // 当前摆动偏移 // 攻击属性 public attackInterval: number = 0; // 攻击间隔(秒),从配置文件读取 private attackTimer: number = 0; // 对控制器的引用 public controller: EnemyControllerType = null; // 敌人当前状态 private state: EnemyState = EnemyState.DRIFTING; // 游戏区域中心 private gameAreaCenter: Vec3 = new Vec3(); // 碰撞的墙体 private collidedWall: Node = null; // 骨骼动画组件 private skeleton: sp.Skeleton | null = null; // 血条动画组件 private hpBarAnimation: HPBarAnimation | null = null; // 暂停状态标记 private isPaused: boolean = false; start() { // 初始化敌人 this.initializeEnemy(); } // 静态方法:加载敌人配置数据库 public static async loadEnemyDatabase(): Promise { if (EnemyInstance.enemyDatabase) return; try { EnemyInstance.enemyDatabase = await JsonConfigLoader.getInstance().loadConfig('enemies'); if (!EnemyInstance.enemyDatabase) { throw new Error('敌人配置文件内容为空'); } } catch (error) { console.error('[EnemyInstance] 加载敌人配置失败:', error); throw error; } } // 设置敌人配置 public setEnemyConfig(enemyId: string): void { this.enemyId = enemyId; if (!EnemyInstance.enemyDatabase) { console.error('[EnemyInstance] 敌人配置数据库未加载'); return; } // 从数据库中查找敌人配置 // 修复:enemies.json是直接的数组结构,不需要.enemies包装 const enemies = EnemyInstance.enemyDatabase; this.enemyConfig = enemies.find((enemy: any) => enemy.id === enemyId); if (!this.enemyConfig) { console.error(`[EnemyInstance] 未找到敌人配置: ${enemyId}`); return; } // 应用配置到敌人属性 this.applyEnemyConfig(); } // 应用敌人配置到属性 private applyEnemyConfig(): void { if (!this.enemyConfig) return; // 从stats节点读取基础属性 const stats = this.enemyConfig.stats || {}; this.health = stats.health || 30; this.maxHealth = stats.maxHealth || this.health; // 从movement节点读取移动速度 const movement = this.enemyConfig.movement || {}; this.speed = movement.speed || 50; // 从combat节点读取攻击力 const combat = this.enemyConfig.combat || {}; this.attackPower = combat.attackDamage || 10; // 设置攻击间隔 this.attackInterval = combat.attackCooldown || 2.0; } // 获取敌人配置信息 public getEnemyConfig(): any { return this.enemyConfig; } // 获取敌人名称 public getEnemyName(): string { return this.enemyConfig?.name || '未知敌人'; } // 获取敌人类型 public getEnemyType(): string { return this.enemyConfig?.type || 'basic'; } // 获取敌人稀有度 public getEnemyRarity(): string { return this.enemyConfig?.rarity || 'common'; } // 获取金币奖励 public getGoldReward(): number { return this.enemyConfig?.goldReward || 1; } // 获取掉落金币数量 public getDropCoins(): number { const stats = this.enemyConfig?.stats || {}; return stats.dropCoins || 1; } // 初始化敌人 private initializeEnemy() { // 确保血量正确设置 if (this.maxHealth > 0) { this.health = this.maxHealth; } this.state = EnemyState.DRIFTING; // 从漂移状态开始 // 只有在攻击间隔未设置时才使用默认值 if (this.attackInterval <= 0) { this.attackInterval = 2.0; // 默认攻击间隔 } this.attackTimer = 0; // 初始化血条动画组件 this.initializeHPBarAnimation(); // 初始化骨骼动画组件 - 从EnemySprite子节点获取 const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite) { this.skeleton = enemySprite.getComponent(sp.Skeleton); } else { console.error('[EnemyInstance] 未找到EnemySprite子节点,无法获取骨骼动画组件'); } this.playWalkAnimation(); // 计算游戏区域中心 this.calculateGameAreaCenter(); // 初始化碰撞检测 this.setupCollider(); } // 设置碰撞器 setupCollider() { // 从EnemySprite子节点获取碰撞器组件 const enemySprite = this.node.getChildByName('EnemySprite'); if (!enemySprite) { console.error('[EnemyInstance] 未找到EnemySprite子节点,无法设置碰撞器组件'); return; } // 检查EnemySprite节点是否有碰撞器 let collider = enemySprite.getComponent(Collider2D); if (!collider) { console.warn(`[EnemyInstance] 敌人节点 ${this.node.name} 的EnemySprite子节点没有碰撞器组件`); return; } // 确保有RigidBody2D组件,这对于碰撞检测是必需的 let rigidBody = enemySprite.getComponent(RigidBody2D); if (!rigidBody) { console.log(`[EnemyInstance] 为敌人EnemySprite节点添加RigidBody2D组件`); rigidBody = enemySprite.addComponent(RigidBody2D); } // 设置刚体属性 if (rigidBody) { rigidBody.type = ERigidBody2DType.Dynamic; // 动态刚体 rigidBody.enabledContactListener = true; // 启用碰撞监听 rigidBody.gravityScale = 0; // 不受重力影响 rigidBody.linearDamping = 0; // 无线性阻尼 rigidBody.angularDamping = 0; // 无角阻尼 rigidBody.allowSleep = false; // 不允许休眠 rigidBody.fixedRotation = true; // 固定旋转 } // 根据ContentSize自适应碰撞体大小 this.adaptColliderToContentSize(collider); // 设置碰撞事件监听 collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); console.log(`[EnemyInstance] 敌人 ${this.node.name} 碰撞器设置完成,碰撞器启用: ${collider.enabled}, 刚体启用: ${rigidBody?.enabled}`); } /** * 根据ContentSize自适应碰撞体大小 */ private adaptColliderToContentSize(collider: Collider2D): void { // 从EnemySprite子节点获取UITransform组件 const enemySprite = this.node.getChildByName('EnemySprite'); if (!enemySprite) { console.error('[EnemyInstance] 未找到EnemySprite子节点,无法获取UITransform组件'); return; } const uiTransform = enemySprite.getComponent(UITransform); if (!uiTransform) { console.warn(`[EnemyInstance] EnemySprite节点没有UITransform组件,无法自适应碰撞体大小`); return; } // 尝试获取骨骼动画组件来获取更准确的尺寸 const skeleton = enemySprite.getComponent(sp.Skeleton); let actualWidth = uiTransform.contentSize.width; let actualHeight = uiTransform.contentSize.height; if (skeleton && skeleton.skeletonData) { // 如果有骨骼动画,尝试从骨骼数据获取尺寸信息 try { // 使用骨骼数据的默认尺寸,或者根据动画缩放计算 const skeletonData = skeleton.skeletonData; if (skeletonData) { // 获取骨骼的缩放比例 const scaleX = Math.abs(skeleton.node.scale.x); const scaleY = Math.abs(skeleton.node.scale.y); // 使用合理的默认尺寸并应用缩放 actualWidth = Math.max(60 * scaleX, uiTransform.contentSize.width * 0.8); actualHeight = Math.max(80 * scaleY, uiTransform.contentSize.height * 0.8); console.log(`[EnemyInstance] 使用骨骼动画尺寸 (scale: ${scaleX.toFixed(2)}, ${scaleY.toFixed(2)}): ${actualWidth.toFixed(2)} x ${actualHeight.toFixed(2)}`); } else { // 如果无法获取骨骼数据,使用默认尺寸 actualWidth = Math.max(60, uiTransform.contentSize.width * 0.8); actualHeight = Math.max(80, uiTransform.contentSize.height * 0.8); console.log(`[EnemyInstance] 使用默认骨骼尺寸: ${actualWidth.toFixed(2)} x ${actualHeight.toFixed(2)}`); } } catch (error) { console.warn(`[EnemyInstance] 获取骨骼信息失败,使用默认尺寸:`, error); actualWidth = Math.max(60, uiTransform.contentSize.width * 0.8); actualHeight = Math.max(80, uiTransform.contentSize.height * 0.8); } } console.log(`[EnemyInstance] 敌人 ${this.node.name} 实际尺寸: ${actualWidth.toFixed(2)} x ${actualHeight.toFixed(2)}`); // 根据碰撞器类型设置大小 if (collider instanceof BoxCollider2D) { // 方形碰撞器:使用实际尺寸,稍微缩小以避免过于敏感的碰撞 const boxCollider = collider as BoxCollider2D; boxCollider.size.width = actualWidth * 0.9; // 缩小10% boxCollider.size.height = actualHeight * 0.9; // 缩小10% // 调整偏移量,使碰撞器居中对齐到EnemySprite const anchorPoint = uiTransform.anchorPoint; // 对于骨骼动画,通常锚点在底部中心(0.5, 0),需要向上偏移 boxCollider.offset.x = 0; // X轴居中 boxCollider.offset.y = actualHeight * (0.5 - anchorPoint.y) * 0.9; // Y轴根据锚点调整,并匹配缩放后的尺寸 console.log(`[EnemyInstance] BoxCollider2D 已自适应: size(${boxCollider.size.width.toFixed(2)}, ${boxCollider.size.height.toFixed(2)}), offset(${boxCollider.offset.x.toFixed(2)}, ${boxCollider.offset.y.toFixed(2)}), anchor(${anchorPoint.x.toFixed(2)}, ${anchorPoint.y.toFixed(2)})`); } else if (collider instanceof CircleCollider2D) { // 圆形碰撞器:使用实际尺寸的较小值作为直径,稍微缩小 const circleCollider = collider as CircleCollider2D; const radius = Math.min(actualWidth, actualHeight) / 2 * 0.9; // 缩小10% circleCollider.radius = radius; // 调整偏移量,使碰撞器居中对齐到EnemySprite const anchorPoint = uiTransform.anchorPoint; // 对于骨骼动画,通常锚点在底部中心(0.5, 0),需要向上偏移 circleCollider.offset.x = 0; // X轴居中 circleCollider.offset.y = actualHeight * (0.5 - anchorPoint.y) * 0.9; // Y轴根据锚点调整,并匹配缩放后的尺寸 console.log(`[EnemyInstance] CircleCollider2D 已自适应: radius(${radius.toFixed(2)}), offset(${circleCollider.offset.x.toFixed(2)}, ${circleCollider.offset.y.toFixed(2)}), anchor(${anchorPoint.x.toFixed(2)}, ${anchorPoint.y.toFixed(2)})`); } else { console.warn(`[EnemyInstance] 不支持的碰撞器类型: ${collider.constructor.name}`); } } // 碰撞开始事件 onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { const nodeName = otherCollider.node.name; // 如果碰到墙体,停止移动并开始攻击 if (nodeName.includes('Wall') || nodeName.includes('wall') || nodeName.includes('Fence') || nodeName.includes('Jiguang')) { this.state = EnemyState.ATTACKING; this.attackTimer = 0; // 立即开始攻击 // 切换攻击动画 this.playAttackAnimation(); } } // 获取节点路径 getNodePath(node: Node): string { let path = node.name; let current = node; while (current.parent) { current = current.parent; path = current.name + '/' + path; } return path; } // 计算游戏区域中心 private calculateGameAreaCenter() { const gameArea = find('Canvas/GameLevelUI/GameArea'); if (gameArea) { this.gameAreaCenter = gameArea.worldPosition; } } /** * 初始化血条动画组件 */ private initializeHPBarAnimation() { const hpBar = this.node.getChildByName('HPBar'); if (hpBar) { // 查找红色和黄色血条节点 const redBarNode = hpBar.getChildByName('RedBar'); const yellowBarNode = hpBar.getChildByName('YellowBar'); if (redBarNode && yellowBarNode) { // 添加血条动画组件 this.hpBarAnimation = this.node.addComponent(HPBarAnimation); if (this.hpBarAnimation) { // 正确设置红色和黄色血条节点引用 this.hpBarAnimation.redBarNode = redBarNode; this.hpBarAnimation.yellowBarNode = yellowBarNode; this.hpBarAnimation.hpBarRootNode = hpBar; // 设置HPBar根节点 console.log(`[EnemyInstance] 血条动画组件已初始化`); } } else { console.warn(`[EnemyInstance] HPBar下未找到RedBar或YellowBar节点,RedBar: ${!!redBarNode}, YellowBar: ${!!yellowBarNode}`); } } else { console.warn(`[EnemyInstance] 未找到HPBar节点,无法初始化血条动画`); } } // 更新血量显示 updateHealthDisplay(showBar: boolean = false) { // 确保血量值在有效范围内 this.health = Math.max(0, Math.min(this.maxHealth, this.health)); const healthProgress = this.maxHealth > 0 ? this.health / this.maxHealth : 0; console.log(`[EnemyInstance] 更新血量显示: ${this.health}/${this.maxHealth} (${(healthProgress * 100).toFixed(1)}%)`); // 使用血条动画组件更新血条 if (this.hpBarAnimation) { this.hpBarAnimation.updateProgress(healthProgress, showBar); } else { // 备用方案:直接更新血条 const hpBar = this.node.getChildByName('HPBar'); if (hpBar) { const progressBar = hpBar.getComponent(ProgressBar); if (progressBar) { progressBar.progress = healthProgress; } // 根据showBar参数控制血条显示 hpBar.active = showBar; } } // 更新血量数字 const hpLabel = this.node.getChildByName('HPLabel'); if (hpLabel) { const label = hpLabel.getComponent(Label); if (label) { // 显示整数血量值 label.string = Math.ceil(this.health).toString(); } } } // 受到伤害 takeDamage(damage: number, isCritical: boolean = false) { // 如果已经死亡,不再处理伤害 if (this.state === EnemyState.DEAD) { return; } // 确保伤害值为正数 if (damage <= 0) { console.warn(`[EnemyInstance] 无效的伤害值: ${damage}`); return; } // 应用防御力减少伤害 const enemyComponent = this.getComponent(EnemyComponent); const defense = enemyComponent ? enemyComponent.getDefense() : 0; let finalDamage = Math.max(1, damage - defense); // 至少造成1点伤害 // 检查格挡 if (enemyComponent && enemyComponent.canBlock() && this.tryBlock()) { finalDamage *= (1 - enemyComponent.getBlockDamageReduction()); this.showBlockEffect(); } // 计算新的血量,确保不会低于0 const oldHealth = this.health; const newHealth = Math.max(0, this.health - finalDamage); const actualHealthLoss = oldHealth - newHealth; // 实际血量损失 this.health = newHealth; // 检查是否触发狂暴 this.checkRageTrigger(); // 日志显示武器的真实伤害值,而不是血量差值 console.log(`[EnemyInstance] 敌人受到伤害: ${damage} (武器伤害), 防御后伤害: ${finalDamage}, 实际血量损失: ${actualHealthLoss}, 剩余血量: ${this.health}/${this.maxHealth}`); // 受击音效已移除 // 显示伤害数字动画(在敌人头顶)- 显示防御后的实际伤害 // 优先使用EnemyController节点上的DamageNumberAni组件实例 if (this.controller) { const damageAni = this.controller.getComponent(DamageNumberAni); if (damageAni) { damageAni.showDamageNumber(finalDamage, this.node.worldPosition, isCritical); } else { // 如果没有找到组件实例,使用静态方法作为备用 DamageNumberAni.showDamageNumber(finalDamage, this.node.worldPosition, isCritical); } } else { // 如果没有controller引用,使用静态方法 DamageNumberAni.showDamageNumber(finalDamage, this.node.worldPosition, isCritical); } // 更新血量显示和动画,受伤时显示血条 this.updateHealthDisplay(true); // 如果血量低于等于0,销毁敌人 if (this.health <= 0) { console.log(`[EnemyInstance] 敌人死亡,开始销毁流程`); this.state = EnemyState.DEAD; this.spawnCoin(); // 进入死亡流程,禁用碰撞避免重复命中 const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite) { const col = enemySprite.getComponent(Collider2D); if (col) col.enabled = false; } this.playDeathAnimationAndDestroy(); } } onDestroy() { console.log(`[EnemyInstance] onDestroy 被调用,准备通知控制器`); // 通知控制器 & GameManager if (this.controller && typeof (this.controller as any).notifyEnemyDead === 'function') { // 检查控制器是否处于清理状态,避免在清理过程中触发游戏事件 const isClearing = (this.controller as any).isClearing; if (isClearing) { console.log(`[EnemyInstance] 控制器处于清理状态,跳过死亡通知`); return; } console.log(`[EnemyInstance] 调用 notifyEnemyDead`); (this.controller as any).notifyEnemyDead(this.node); } else { console.warn(`[EnemyInstance] 无法调用 notifyEnemyDead: controller=${!!this.controller}`); } } update(deltaTime: number) { // 如果敌人被暂停,则不执行任何更新逻辑 if (this.isPaused) { return; } if (this.state === EnemyState.DRIFTING) { this.updateDrifting(deltaTime); } else if (this.state === EnemyState.MOVING) { this.updateMovement(deltaTime); } else if (this.state === EnemyState.ATTACKING) { this.updateAttack(deltaTime); } // 不再每帧播放攻击动画,避免日志刷屏 } // 更新漂移逻辑(从生成位置移动到线上) private updateDrifting(deltaTime: number) { if (!this.driftTargetPosition) { // 如果没有漂移目标位置,直接切换到移动状态 this.state = EnemyState.MOVING; return; } const currentWorldPos = this.node.worldPosition.clone(); const targetWorldPos = this.driftTargetPosition.clone(); const dir = targetWorldPos.subtract(currentWorldPos); const distanceToTarget = dir.length(); // 如果距离目标很近,切换到移动状态 const stopDistance = 10; // 停止距离 if (distanceToTarget <= stopDistance) { this.state = EnemyState.MOVING; return; } if (distanceToTarget === 0) return; dir.normalize(); // 使用较快的速度进行漂移 const driftSpeed = this.speed * 5; // 漂移速度比正常移动快 const moveDistance = driftSpeed * deltaTime; const newWorldPos = currentWorldPos.add(dir.multiplyScalar(moveDistance)); // 直接设置世界坐标 this.node.setWorldPosition(newWorldPos); } // 更新移动逻辑 private updateMovement(deltaTime: number) { // 继续移动到目标墙体 this.moveTowardsTarget(deltaTime); } // 移动到目标位置 private moveTowardsTarget(deltaTime: number) { // 使用世界坐标进行移动计算,确保不受父节点坐标系影响 const currentWorldPos = this.node.worldPosition.clone(); // 目标世界坐标:使用预设的目标位置,而不是墙体中心 let targetWorldPos: Vec3; if (this.targetPosition) { targetWorldPos = this.targetPosition.clone(); } else if (this.targetFence && this.targetFence.isValid) { // 如果没有预设目标位置,则使用墙体中心作为备用 targetWorldPos = this.targetFence.worldPosition.clone(); } else { targetWorldPos = this.gameAreaCenter.clone(); } const dir = targetWorldPos.subtract(currentWorldPos); const distanceToTarget = dir.length(); // 如果距离目标很近,停止移动并切换到攻击状态 const stopDistance = 30; // 停止距离,可以根据需要调整 if (distanceToTarget <= stopDistance) { this.state = EnemyState.ATTACKING; this.attackTimer = 0; // 立即开始攻击 this.playAttackAnimation(); return; } if (distanceToTarget === 0) return; dir.normalize(); // 获取当前速度(考虑狂暴加成) const enemyComponent = this.getComponent(EnemyComponent); const currentSpeed = enemyComponent ? enemyComponent.getCurrentSpeed() : this.speed; const moveDistance = currentSpeed * deltaTime; let newWorldPos = currentWorldPos.add(dir.multiplyScalar(moveDistance)); // 应用摆动效果 if (enemyComponent) { const swayAmplitude = enemyComponent.getSwingAmplitude(); const swayFrequency = enemyComponent.getSwingFrequency(); if (swayAmplitude > 0 && swayFrequency > 0) { this.swayTime += deltaTime; // 计算垂直于移动方向的摆动偏移 const swayOffset = Math.sin(this.swayTime * swayFrequency * 2 * Math.PI) * swayAmplitude; // 计算垂直于移动方向的向量 const perpDir = new Vec3(-dir.y, dir.x, 0); const swayVector = perpDir.multiplyScalar(swayOffset); newWorldPos = newWorldPos.add(swayVector); } } // 直接设置世界坐标 this.node.setWorldPosition(newWorldPos); } // 更新攻击逻辑 private updateAttack(deltaTime: number) { this.attackTimer -= deltaTime; if (this.attackTimer <= 0) { // 执行攻击 this.performAttack(); // 重置攻击计时器 this.attackTimer = this.attackInterval; } } // 执行攻击 private performAttack() { if (!this.controller) { return; } const enemyComponent = this.getComponent(EnemyComponent); const attackType = enemyComponent ? enemyComponent.getAttackType() : 'melee'; // 播放攻击音效 EnemyAudio.playAttackSound(this.enemyConfig); // 根据攻击类型执行不同的攻击逻辑 if (attackType === 'ranged' || attackType === 'projectile') { this.performRangedAttack(); } else { // 近战攻击:播放攻击动画,动画结束后造成伤害 this.playAttackAnimationWithDamage(); } } // 执行远程攻击(投掷) private performRangedAttack() { const enemyComponent = this.getComponent(EnemyComponent); if (!enemyComponent) return; const projectileType = enemyComponent.getProjectileType(); const projectileSpeed = enemyComponent.getProjectileSpeed(); console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 发射投掷物: ${projectileType}, 速度: ${projectileSpeed}`); // TODO: 实际创建和发射投掷物的逻辑 // 这里需要根据项目的投掷物系统来实现 // 例如:创建投掷物预制体,设置速度和方向,添加碰撞检测等 // 暂时直接造成伤害作为占位符 this.scheduleOnce(() => { this.dealDamageToWall(); }, 1.0); // 1秒后造成伤害,模拟投掷物飞行时间 } // 播放行走动画 private playWalkAnimation() { if (!this.skeleton) return; const enemyComp = this.getComponent('EnemyComponent') as any; const anims = enemyComp?.getAnimations ? enemyComp.getAnimations() : {}; const walkName = anims.walk ?? 'walk'; const idleName = anims.idle ?? 'idle'; if (this.skeleton.findAnimation(walkName)) { this.skeleton.setAnimation(0, walkName, true); // 行走音效已移除 } else if (this.skeleton.findAnimation(idleName)) { this.skeleton.setAnimation(0, idleName, true); } } // 播放攻击动画 private playAttackAnimation() { if (!this.skeleton) return; const enemyComp2 = this.getComponent('EnemyComponent') as any; const anims2 = enemyComp2?.getAnimations ? enemyComp2.getAnimations() : {}; const attackName = anims2.attack ?? 'attack'; // 移除频繁打印 if (this.skeleton.findAnimation(attackName)) { this.skeleton.setAnimation(0, attackName, true); } } // 播放攻击动画并在动画结束时造成伤害 private playAttackAnimationWithDamage() { if (!this.skeleton) { // 如果没有骨骼动画,直接造成伤害 this.dealDamageToWall(); return; } const enemyComp = this.getComponent('EnemyComponent') as any; const anims = enemyComp?.getAnimations ? enemyComp.getAnimations() : {}; const attackName = anims.attack ?? 'attack'; const animation = this.skeleton.findAnimation(attackName); if (animation) { // 播放攻击动画(不循环) this.skeleton.setAnimation(0, attackName, false); // 获取动画时长并在动画结束时造成伤害 const animationDuration = animation.duration; console.log(`[EnemyInstance] 攻击动画时长: ${animationDuration}秒`); // 使用定时器在动画结束时造成伤害 this.scheduleOnce(() => { this.dealDamageToWall(); // 动画完成后切换回行走动画 this.playWalkAnimation(); }, animationDuration); } else { // 如果找不到攻击动画,直接造成伤害 this.dealDamageToWall(); } } // 对墙体造成伤害 private dealDamageToWall() { if (this.controller) { // 获取当前攻击力(考虑狂暴加成) const enemyComponent = this.getComponent(EnemyComponent); const currentAttackPower = enemyComponent ? enemyComponent.getCurrentAttackPower() : this.attackPower; this.controller.damageWall(currentAttackPower); } } private playDeathAnimationAndDestroy() { console.log(`[EnemyInstance] 开始播放死亡动画并销毁`); // 播放死亡音效 EnemyAudio.playDeathSound(this.enemyConfig); if (this.skeleton) { const enemyComp = this.getComponent('EnemyComponent') as any; const anims = enemyComp?.getAnimations ? enemyComp.getAnimations() : {}; const deathName = anims.dead ?? 'dead'; if (this.skeleton.findAnimation(deathName)) { this.skeleton.setAnimation(0, deathName, false); // 销毁节点在动画完毕后 this.skeleton.setCompleteListener(() => { this.node.destroy(); }); return; } } this.node.destroy(); } private spawnCoin() { const ctrl = this.controller as any; // EnemyController if (!ctrl?.coinPrefab) return; // 获取配置中的掉落金币数量 const dropCoinsCount = this.getDropCoins(); console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 掉落 ${dropCoinsCount} 个金币`); // 根据配置掉落相应数量的金币 for (let i = 0; i < dropCoinsCount; i++) { const coin = instantiate(ctrl.coinPrefab); find('Canvas')!.addChild(coin); // 放到 UI 层 const pos = new Vec3(); this.node.getWorldPosition(pos); // 取死亡敌人的世界坐标 // 如果掉落多个金币,添加一些随机偏移避免重叠 if (dropCoinsCount > 1) { const offsetX = (Math.random() - 0.5) * 40; // 随机X偏移 -20到20 const offsetY = (Math.random() - 0.5) * 40; // 随机Y偏移 -20到20 pos.x += offsetX; pos.y += offsetY; } coin.worldPosition = pos; // 金币就在敌人身上出现 } } /** * 暂停敌人 */ public pause(): void { this.isPaused = true; console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 已暂停`); } /** * 恢复敌人 */ public resume(): void { this.isPaused = false; console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 已恢复`); } /** * 检查是否暂停 */ public isPausedState(): boolean { return this.isPaused; } /** * 获取敌人当前状态 */ public getState(): EnemyState { return this.state; } /** * 检查敌人是否处于漂移状态 */ public isDrifting(): boolean { return this.state === EnemyState.DRIFTING; } /** * 重置血条状态(满血并隐藏) */ public resetHealthBar(): void { if (this.hpBarAnimation) { this.hpBarAnimation.resetToFullAndHide(); } else { // 备用方案:直接隐藏血条 const hpBar = this.node.getChildByName('HPBar'); if (hpBar) { hpBar.active = false; const progressBar = hpBar.getComponent(ProgressBar); if (progressBar) { progressBar.progress = 1.0; } } } } /** * 尝试格挡攻击 */ private tryBlock(): boolean { const enemyComponent = this.getComponent(EnemyComponent); if (!enemyComponent) return false; const blockChance = enemyComponent.getBlockChance(); return Math.random() < blockChance; } /** * 显示格挡效果 */ private showBlockEffect(): void { console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 格挡了攻击`); // TODO: 添加格挡视觉效果 } /** * 检查是否触发狂暴状态 */ private checkRageTrigger(): void { const enemyComponent = this.getComponent(EnemyComponent); if (!enemyComponent) return; const healthPercent = this.health / this.maxHealth; const rageTriggerThreshold = enemyComponent.getRageTriggerThreshold(); if (healthPercent <= rageTriggerThreshold && !enemyComponent.isInRage()) { enemyComponent.triggerRage(); console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 进入狂暴状态`); } } }