import { _decorator, Component, Node, ProgressBar, Label, Vec3, find, UITransform, Collider2D, Contact2DType, IPhysics2DContact, instantiate, resources, Prefab, JsonAsset } from 'cc'; import { sp } from 'cc'; import { DamageNumberAni } from '../Animations/DamageNumberAni'; import { HPBarAnimation } from '../Animations/HPBarAnimation'; import { EnemyComponent } from './EnemyComponent'; 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 { MOVING, // 移动中 ATTACKING, // 攻击中 DEAD // 死亡 } // 单个敌人实例的组件 @ccclass('EnemyInstance') export class EnemyInstance extends Component { // 敌人属性(从配置文件读取) public health: number = 30; public maxHealth: number = 30; public speed: number = 50; public attackPower: number = 10; // 敌人配置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 movingDirection: number = 1; // 1: 向右, -1: 向左 public targetY: number = 0; // 目标Y位置 public changeDirectionTime: number = 0; // 下次改变方向的时间 // 攻击属性 public attackInterval: number = 2; // 攻击间隔(秒) private attackTimer: number = 0; // 对控制器的引用 public controller: EnemyControllerType = null; // 敌人当前状态 private state: EnemyState = EnemyState.MOVING; // 游戏区域中心 private gameAreaCenter: Vec3 = new Vec3(); // 碰撞的墙体 private collidedWall: Node = null; // 骨骼动画组件 private skeleton: sp.Skeleton | null = null; // 血条动画组件 private hpBarAnimation: HPBarAnimation | null = null; start() { // 初始化敌人 this.initializeEnemy(); } // 静态方法:加载敌人配置数据库 public static async loadEnemyDatabase(): Promise { if (EnemyInstance.enemyDatabase) return; return new Promise((resolve, reject) => { resources.load('data/enemies', JsonAsset, (err, jsonAsset) => { if (err) { console.error('[EnemyInstance] 加载敌人配置失败:', err); reject(err); return; } EnemyInstance.enemyDatabase = jsonAsset.json; console.log('[EnemyInstance] 敌人配置数据库加载成功'); resolve(); }); }); } // 设置敌人配置 public setEnemyConfig(enemyId: string): void { this.enemyId = enemyId; if (!EnemyInstance.enemyDatabase) { console.error('[EnemyInstance] 敌人配置数据库未加载'); return; } // 从数据库中查找敌人配置 const enemies = EnemyInstance.enemyDatabase.enemies; 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; this.health = this.enemyConfig.health || 30; this.maxHealth = this.health; this.speed = this.enemyConfig.speed || 50; this.attackPower = this.enemyConfig.attack || 10; console.log(`[EnemyInstance] 应用敌人配置: ${this.enemyConfig.name}, 血量: ${this.health}, 速度: ${this.speed}, 攻击力: ${this.attackPower}`); } // 获取敌人配置信息 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; } // 初始化敌人 private initializeEnemy() { // 确保血量正确设置 if (this.maxHealth > 0) { this.health = this.maxHealth; } this.state = EnemyState.MOVING; this.attackInterval = 2.0; // 默认攻击间隔 this.attackTimer = 0; // 初始化血条动画组件 this.initializeHPBarAnimation(); // 获取骨骼动画组件 this.skeleton = this.getComponent(sp.Skeleton); this.playWalkAnimation(); // 计算游戏区域中心 this.calculateGameAreaCenter(); // 初始化碰撞检测 this.setupCollider(); } // 设置碰撞器 setupCollider() { // 检查节点是否有碰撞器 let collider = this.node.getComponent(Collider2D); if (!collider) { return; } // 设置碰撞事件监听 collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); } // 碰撞开始事件 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; console.log(`[EnemyInstance] 血条动画组件已初始化`); } } else { console.warn(`[EnemyInstance] HPBar下未找到RedBar或YellowBar节点,RedBar: ${!!redBarNode}, YellowBar: ${!!yellowBarNode}`); } } else { console.warn(`[EnemyInstance] 未找到HPBar节点,无法初始化血条动画`); } } // 更新血量显示 updateHealthDisplay() { const healthProgress = this.health / this.maxHealth; // 使用血条动画组件更新血条 if (this.hpBarAnimation) { this.hpBarAnimation.updateProgress(healthProgress); } else { // 备用方案:直接更新血条 const hpBar = this.node.getChildByName('HPBar'); if (hpBar) { const progressBar = hpBar.getComponent(ProgressBar); if (progressBar) { progressBar.progress = healthProgress; } } } // 更新血量数字 const hpLabel = this.node.getChildByName('HPLabel'); if (hpLabel) { const label = hpLabel.getComponent(Label); if (label) { label.string = this.health.toString(); } } } // 受到伤害 takeDamage(damage: number, isCritical: boolean = false) { // 如果已经死亡,不再处理伤害 if (this.state === EnemyState.DEAD) { return; } this.health -= damage; console.log(`[EnemyInstance] 敌人受到伤害: ${damage}, 剩余血量: ${this.health}`); // 显示伤害数字动画(在敌人头顶) // 优先使用EnemyController节点上的DamageNumberAni组件实例 if (this.controller) { const damageAni = this.controller.getComponent(DamageNumberAni); if (damageAni) { damageAni.showDamageNumber(damage, this.node.worldPosition, isCritical); } else { // 如果没有找到组件实例,使用静态方法作为备用 DamageNumberAni.showDamageNumber(damage, this.node.worldPosition, isCritical); } } else { // 如果没有controller引用,使用静态方法 DamageNumberAni.showDamageNumber(damage, this.node.worldPosition, isCritical); } // 更新血量显示和动画 this.updateHealthDisplay(); // 如果血量低于等于0,销毁敌人 if (this.health <= 0) { console.log(`[EnemyInstance] 敌人死亡,开始销毁流程`); this.state = EnemyState.DEAD; this.spawnCoin(); // 进入死亡流程,禁用碰撞避免重复命中 const col = this.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.state === EnemyState.MOVING) { this.updateMovement(deltaTime); } else if (this.state === EnemyState.ATTACKING) { this.updateAttack(deltaTime); } // 不再每帧播放攻击动画,避免日志刷屏 } // 更新移动逻辑 private updateMovement(deltaTime: number) { // 检查是否接近游戏区域边界 if (this.checkNearGameArea()) { this.state = EnemyState.ATTACKING; this.attackTimer = 0; this.playAttackAnimation(); return; } // 继续移动 this.moveTowardsTarget(deltaTime); } // 检查是否接近游戏区域 private checkNearGameArea(): boolean { const currentPos = this.node.worldPosition; // 获取游戏区域边界 const gameArea = find('Canvas/GameLevelUI/GameArea'); if (!gameArea) return false; const uiTransform = gameArea.getComponent(UITransform); if (!uiTransform) return false; const gameAreaPos = gameArea.worldPosition; const halfWidth = uiTransform.width / 2; const halfHeight = uiTransform.height / 2; const bounds = { left: gameAreaPos.x - halfWidth, right: gameAreaPos.x + halfWidth, top: gameAreaPos.y + halfHeight, bottom: gameAreaPos.y - halfHeight }; // 检查是否在游戏区域内或非常接近 const safeDistance = 50; // 安全距离 const isInside = currentPos.x >= bounds.left - safeDistance && currentPos.x <= bounds.right + safeDistance && currentPos.y >= bounds.bottom - safeDistance && currentPos.y <= bounds.top + safeDistance; if (isInside) { return true; } return false; } // 移动到目标位置 private moveTowardsTarget(deltaTime: number) { // 使用世界坐标进行移动计算,确保不受父节点坐标系影响 const currentWorldPos = this.node.worldPosition.clone(); // 目标世界坐标:优先使用指定的 Fence,其次退化到游戏区域中心 let targetWorldPos: Vec3; if (this.targetFence && this.targetFence.isValid) { targetWorldPos = this.targetFence.worldPosition.clone(); } else { targetWorldPos = this.gameAreaCenter.clone(); } const dir = targetWorldPos.subtract(currentWorldPos); if (dir.length() === 0) return; dir.normalize(); const moveDistance = this.speed * deltaTime; const newWorldPos = currentWorldPos.add(dir.multiplyScalar(moveDistance)); // 直接设置世界坐标 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; } // 对墙体造成伤害 this.controller.damageWall(this.attackPower); } // 播放行走动画 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 playDeathAnimationAndDestroy() { console.log(`[EnemyInstance] 开始播放死亡动画并销毁`); 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)) { console.log(`[EnemyInstance] 播放死亡动画: ${deathName}`); this.skeleton.setAnimation(0, deathName, false); // 销毁节点在动画完毕后 this.skeleton.setCompleteListener(() => { console.log(`[EnemyInstance] 死亡动画完成,销毁节点`); this.node.destroy(); }); return; } } // 若无动画直接销毁 console.log(`[EnemyInstance] 无死亡动画,直接销毁节点`); this.node.destroy(); } private spawnCoin() { const ctrl = this.controller as any; // EnemyController if (!ctrl?.coinPrefab) return; const coin = instantiate(ctrl.coinPrefab); find('Canvas')!.addChild(coin); // 放到 UI 层 const pos = new Vec3(); this.node.getWorldPosition(pos); // 取死亡敌人的世界坐标 coin.worldPosition = pos; // 金币就在敌人身上出现 } }