import { _decorator, Component, Node, ProgressBar, Label, Vec3, find, UITransform, Collider2D, Contact2DType, IPhysics2DContact, instantiate, resources, Prefab, JsonAsset, RigidBody2D, ERigidBody2DType, BoxCollider2D, CircleCollider2D, Vec2, Color, UIOpacity, tween } 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; }; topFenceNode: Node; bottomFenceNode: Node; 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; // 下次改变方向的时间 // 移动配置属性(从配置文件读取) public movementPattern: string = 'direct'; // 移动模式:direct, patrol等 public moveType: string = 'straight'; // 移动类型:straight, sway等 public patrolRange: number = 100; // 巡逻范围 public rotationSpeed: number = 90; // 旋转速度 public speedVariation: number = 0; // 速度变化 public swingAmplitude: number = 0; // 摆动幅度 public swingFrequency: number = 1; // 摆动频率 // 摆动移动相关属性 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; // 读取移动模式配置 this.movementPattern = movement.pattern || 'direct'; this.moveType = movement.moveType || 'straight'; this.patrolRange = movement.patrolRange || 100; this.rotationSpeed = movement.rotationSpeed || 90; this.speedVariation = movement.speedVariation || 0; // 读取摆动配置 this.swingAmplitude = movement.swingAmplitude || 0; this.swingFrequency = movement.swingFrequency || 1; // 从combat节点读取攻击力 const combat = this.enemyConfig.combat || {}; this.attackPower = combat.attackDamage || 10; // 设置攻击间隔 this.attackInterval = combat.attackCooldown || 2.0; console.log(`[EnemyInstance] 应用配置 ${this.enemyId}: 移动模式=${this.movementPattern}, 移动类型=${this.moveType}, 速度=${this.speed}, 摆动幅度=${this.swingAmplitude}`); } // 获取敌人配置信息 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}`); } // 碰撞体调整完成后,同步更新血条位置 this.updateHPBarPosition(); } /** * 动态调整血条位置,使其始终显示在碰撞体上方 */ private updateHPBarPosition(): void { // 获取血条根节点 const hpBarRoot = this.node.getChildByName('HPBar'); if (!hpBarRoot) { console.warn('[EnemyInstance] 未找到HPBar节点,无法调整血条位置'); return; } // 获取EnemySprite节点和其碰撞器 const enemySprite = this.node.getChildByName('EnemySprite'); if (!enemySprite) { console.error('[EnemyInstance] 未找到EnemySprite子节点,无法计算血条位置'); return; } const collider = enemySprite.getComponent(Collider2D); const uiTransform = enemySprite.getComponent(UITransform); if (!collider || !uiTransform) { console.warn('[EnemyInstance] EnemySprite节点缺少必要组件,无法计算血条位置'); return; } // 计算碰撞体的顶部位置 let colliderTopY = 0; const anchorPoint = uiTransform.anchorPoint; if (collider instanceof BoxCollider2D) { const boxCollider = collider as BoxCollider2D; // 碰撞体顶部 = 碰撞体偏移Y + 碰撞体高度的一半 colliderTopY = boxCollider.offset.y + boxCollider.size.height / 2; } else if (collider instanceof CircleCollider2D) { const circleCollider = collider as CircleCollider2D; // 碰撞体顶部 = 碰撞体偏移Y + 半径 colliderTopY = circleCollider.offset.y + circleCollider.radius; } // 血条位置设置为碰撞体顶部上方一定距离 const hpBarOffset = -35; // 血条距离碰撞体顶部的距离 const hpBarY = colliderTopY + hpBarOffset; // 设置血条位置 const hpBarTransform = hpBarRoot.getComponent(UITransform); if (hpBarTransform) { hpBarRoot.setPosition(0, hpBarY, 0); console.log(`[EnemyInstance] 血条位置已更新: Y = ${hpBarY.toFixed(2)} (碰撞体顶部: ${colliderTopY.toFixed(2)})`); } } // 碰撞开始事件 onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { const nodeName = otherCollider.node.name; console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 碰撞检测 - 碰撞对象: ${nodeName}, 当前状态: ${EnemyState[this.state]}`); // 只有在移动状态下才能切换到攻击状态,避免重复触发 if (this.state !== EnemyState.MOVING) { return; } // 检查是否碰到墙体(更严格的墙体识别) const isWall = this.isWallNode(otherCollider.node); if (isWall) { console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 碰撞到墙体 ${nodeName},切换到攻击状态`); this.state = EnemyState.ATTACKING; this.attackTimer = 0; // 立即开始攻击 this.collidedWall = otherCollider.node; // 记录碰撞的墙体 // 停止移动 this.stopRigidBodyMovement(); // 切换攻击动画 this.playAttackAnimation(); } } // 检查节点是否为墙体 private isWallNode(node: Node): boolean { const nodeName = node.name.toLowerCase(); // 检查节点名称是否包含墙体关键词 const wallKeywords = ['wall', 'fence', 'jiguang', '墙', '围栏']; const isWallByName = wallKeywords.some(keyword => nodeName.includes(keyword)); // 检查节点是否有Wall组件 const hasWallComponent = node.getComponent('Wall') !== null; return isWallByName || hasWallComponent; } // 获取节点路径 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); // 在漂移状态也检查墙体碰撞 this.checkWallCollisionByDistance(); } else if (this.state === EnemyState.MOVING) { this.updateMovement(deltaTime); // 在移动状态检查墙体碰撞 this.checkWallCollisionByDistance(); } 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 = 5; // 减小停止距离以提高精度 if (distanceToTarget <= stopDistance) { this.state = EnemyState.MOVING; // 确保刚体速度为零 this.stopRigidBodyMovement(); return; } if (distanceToTarget === 0) return; dir.normalize(); // 使用代码控制移动,不依赖物理系统 const driftSpeed = Math.min(this.speed * 2, 500); // 限制最大漂移速度为500 const moveDistance = driftSpeed * deltaTime; const actualMoveDistance = Math.min(moveDistance, distanceToTarget); const newWorldPos = currentWorldPos.add(dir.multiplyScalar(actualMoveDistance)); // 直接设置世界坐标 this.node.setWorldPosition(newWorldPos); // 确保刚体速度为零,避免物理系统干扰 this.stopRigidBodyMovement(); } // 更新移动逻辑 private updateMovement(deltaTime: number) { // 根据配置的移动模式执行不同的移动逻辑 switch (this.movementPattern) { case 'direct': this.updateDirectMovement(deltaTime); break; case 'patrol': this.updatePatrolMovement(deltaTime); break; default: this.updateDirectMovement(deltaTime); break; } } // 直线移动模式 private updateDirectMovement(deltaTime: number) { this.moveTowardsTarget(deltaTime); } // 巡逻移动模式 private updatePatrolMovement(deltaTime: number) { // 更新方向变化计时器 this.changeDirectionTime += deltaTime; // 根据巡逻范围决定方向变化 const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite && this.controller) { const currentPos = enemySprite.worldPosition; const bounds = this.controller.gameBounds; // 检查是否到达巡逻边界 const leftBound = bounds.left + this.patrolRange; const rightBound = bounds.right - this.patrolRange; if (currentPos.x <= leftBound && this.movingDirection < 0) { this.movingDirection = 1; // 向右 this.changeDirectionTime = 0; } else if (currentPos.x >= rightBound && this.movingDirection > 0) { this.movingDirection = -1; // 向左 this.changeDirectionTime = 0; } // 随机改变Y目标位置 if (this.changeDirectionTime >= 3 + Math.random() * 2) { const centerY = (bounds.top + bounds.bottom) / 2; const range = (bounds.top - bounds.bottom) * 0.4; this.targetY = centerY + (Math.random() - 0.5) * range; this.changeDirectionTime = 0; } } this.moveTowardsTarget(deltaTime); } // 向目标移动 private moveTowardsTarget(deltaTime: number) { // 获取当前速度(考虑狂暴加成和速度变化) const enemyComponent = this.getComponent(EnemyComponent); let currentSpeed = enemyComponent ? enemyComponent.getCurrentSpeed() : this.speed; // 应用速度变化配置,限制变化幅度防止过大跳跃 if (this.speedVariation > 0) { const maxVariation = Math.min(this.speedVariation, currentSpeed * 0.3); // 限制变化不超过当前速度的30% const variation = (Math.random() - 0.5) * 2 * maxVariation; currentSpeed = Math.max(currentSpeed + variation, currentSpeed * 0.5); // 确保速度不会过低 } // 统一使用主节点的世界坐标,与updateDrifting保持一致 const currentPos = this.node.worldPosition.clone(); let dir = new Vec3(); // 水平移动方向 dir.x = this.movingDirection; // 垂直移动方向 - 向目标Y位置移动 const yDiff = this.targetY - currentPos.y; if (Math.abs(yDiff) > 10) { // 如果距离目标Y位置超过10像素 dir.y = yDiff > 0 ? 0.5 : -0.5; // 缓慢向目标Y移动 } else { dir.y = 0; } // 如果有具体的目标位置(墙体),优先移动到墙体 if (this.targetPosition) { const targetDir = new Vec3(); Vec3.subtract(targetDir, this.targetPosition.clone(), currentPos); if (targetDir.length() > 20) { // 距离墙体还有一定距离时 targetDir.normalize(); dir.x = targetDir.x * 0.7; // 70%朝向墙体 dir.y = targetDir.y * 0.7; } } else if (this.targetFence) { const fencePos = this.targetFence.worldPosition.clone(); const targetDir = new Vec3(); Vec3.subtract(targetDir, fencePos, currentPos); if (targetDir.length() > 20) { targetDir.normalize(); dir.x = targetDir.x * 0.7; dir.y = targetDir.y * 0.7; } } // 归一化方向向量 if (dir.length() > 0) { dir.normalize(); } // 应用摆动效果(根据配置) if (this.moveType === 'sway' && this.swingAmplitude > 0) { this.swayTime += deltaTime; const swayAmount = Math.sin(this.swayTime * this.swingFrequency) * this.swingAmplitude; // 计算垂直于移动方向的摆动 const swayDir = new Vec3(-dir.y, dir.x, 0); swayDir.multiplyScalar(swayAmount / 100); // 将摆动幅度转换为合适的比例 dir.add(swayDir); } // 限制单帧移动距离,防止突然跳跃 const moveDistance = currentSpeed * deltaTime; const maxMoveDistance = Math.min(15, currentSpeed * 0.1); // 限制单帧最大移动距离 const actualMoveDistance = Math.min(moveDistance, maxMoveDistance); const moveVector = dir.multiplyScalar(actualMoveDistance); const newPos = new Vec3(); Vec3.add(newPos, currentPos, moveVector); // 统一使用主节点的setWorldPosition,与updateDrifting保持一致 this.node.setWorldPosition(newPos); // 确保刚体速度为零,避免物理系统干扰 this.stopRigidBodyMovement(); } // 更新攻击逻辑 private updateAttack(deltaTime: number) { // 在攻击状态下停止移动 const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite) { const rigidBody = enemySprite.getComponent(RigidBody2D); if (rigidBody) { // 停止物理移动 rigidBody.linearVelocity = new Vec2(0, 0); } } 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); } // 动画切换后延迟更新血条位置,确保动画尺寸已生效 this.scheduleOnce(() => { this.updateHPBarPosition(); }, 0.1); } // 播放攻击动画 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); } // 动画切换后延迟更新血条位置,确保动画尺寸已生效 this.scheduleOnce(() => { this.updateHPBarPosition(); }, 0.1); } // 播放攻击动画并在动画结束时造成伤害 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); // 动画切换后延迟更新血条位置,确保动画尺寸已生效 this.scheduleOnce(() => { this.updateHPBarPosition(); }, 0.1); // 获取动画时长并在动画结束时造成伤害 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; // 暂停物理系统 const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite) { const rigidBody = enemySprite.getComponent(RigidBody2D); if (rigidBody) { // 保存当前速度并停止物理移动 (this as any).savedVelocity = rigidBody.linearVelocity; rigidBody.linearVelocity = new Vec2(0, 0); } } console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 已暂停`); } /** * 恢复敌人 */ public resume(): void { this.isPaused = false; // 恢复物理系统 const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite) { const rigidBody = enemySprite.getComponent(RigidBody2D); if (rigidBody && (this as any).savedVelocity) { // 恢复之前保存的速度 rigidBody.linearVelocity = (this as any).savedVelocity; (this as any).savedVelocity = null; } } 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(); // 将百分比值转换为0-1范围(例如:30.0 -> 0.3) const blockChanceNormalized = blockChance / 100.0; return Math.random() < blockChanceNormalized; } /** * 基于距离检查墙体碰撞 */ private checkWallCollisionByDistance(): void { if (this.state === EnemyState.ATTACKING) { return; // 已经在攻击状态,不需要再检查 } const enemySprite = this.node.getChildByName('EnemySprite'); if (!enemySprite) return; const currentPos = enemySprite.worldPosition; const collisionDistance = 30; // 碰撞检测距离 // 获取所有墙体节点进行距离检测 const wallNodes = this.getAllWallNodes(); for (const wallNode of wallNodes) { const wallPos = wallNode.worldPosition; const distance = Vec3.distance(currentPos, wallPos); if (distance <= collisionDistance) { console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 距离检测到墙体 ${wallNode.name},距离: ${distance.toFixed(2)},切换到攻击状态`); this.state = EnemyState.ATTACKING; this.attackTimer = 0; // 立即开始攻击 this.collidedWall = wallNode; // 记录碰撞的墙体 // 停止移动 this.stopRigidBodyMovement(); // 切换攻击动画 this.playAttackAnimation(); return; // 找到一个墙体就停止检查 } } } /** * 获取所有墙体节点 */ private getAllWallNodes(): Node[] { const wallNodes: Node[] = []; // 通过EnemyController获取墙体节点 if (this.controller) { if (this.controller.topFenceNode) { wallNodes.push(this.controller.topFenceNode); } if (this.controller.bottomFenceNode) { wallNodes.push(this.controller.bottomFenceNode); } } // 备用方案:在场景中查找墙体节点 if (wallNodes.length === 0) { const gameArea = find('Canvas/GameLevelUI/GameArea'); if (gameArea) { for (let i = 0; i < gameArea.children.length; i++) { const child = gameArea.children[i]; if (this.isWallNode(child)) { wallNodes.push(child); } } } } return wallNodes; } /** * 停止刚体移动,确保物理系统不干扰代码控制的移动 */ private stopRigidBodyMovement(): void { const enemySprite = this.node.getChildByName('EnemySprite'); if (enemySprite) { const rigidBody = enemySprite.getComponent(RigidBody2D); if (rigidBody) { rigidBody.linearVelocity = new Vec2(0, 0); } } } /** * 显示格挡效果 */ private showBlockEffect(): void { console.log(`[EnemyInstance] 敌人 ${this.getEnemyName()} 格挡了攻击`); // 显示"格挡"文字效果 this.showBlockText(); } /** * 显示格挡文字 */ private showBlockText(): void { // 使用DamageNumberAni组件来显示格挡文字,就像显示伤害数字一样 if (this.controller) { const damageAni = this.controller.getComponent(DamageNumberAni); if (damageAni) { damageAni.showBlockText(this.node.worldPosition); } else { console.warn('[EnemyInstance] 未找到DamageNumberAni组件,无法显示格挡文字'); } } else { console.warn('[EnemyInstance] 未找到controller引用,无法显示格挡文字'); } } /** * 检查是否触发狂暴状态 */ 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()} 进入狂暴状态`); } } }