import { _decorator, Component, Node, Vec3, RigidBody2D, Collider2D, Contact2DType, IPhysics2DContact, Sprite, find } from 'cc'; import { BundleLoader } from '../../Core/BundleLoader'; import { Audio } from '../../AudioManager/AudioManager'; import EventBus, { GameEvents } from '../../Core/EventBus'; const { ccclass, property } = _decorator; /** * 敌人抛掷物实例组件 * 负责处理单个抛掷物的移动、碰撞和伤害 * 此组件应该挂载到抛掷物预制体上 */ @ccclass('EnemyProjectileInstance') export class EnemyProjectileInstance extends Component { // 抛掷物属性 private damage: number = 10; private speed: number = 100; private direction: Vec3 = new Vec3(0, -1, 0); // 默认向下 private projectileType: string = 'arrow'; // 组件引用 private rigidBody: RigidBody2D = null; private collider: Collider2D = null; private sprite: Sprite = null; // 生命周期 private maxLifetime: number = 5.0; // 最大存活时间 private currentLifetime: number = 0; // 弧线弹道相关属性 private useArcTrajectory: boolean = true; // 是否使用弧线弹道 private arcDir: Vec3 = null; // 当前方向 private arcTargetDir: Vec3 = null; // 目标方向 private targetWallPosition: Vec3 = null; // 目标墙体位置 private rotateSpeed: number = 2.0; // 转向速度 onLoad() { console.log('[EnemyProjectileInstance] 抛掷物实例组件已加载'); this.setupComponents(); this.setupProjectileSprite(); } start() { this.setupCollisionListener(); } update(deltaTime: number) { // 如果已被标记为销毁,停止更新 if (this.isDestroyed) { return; } // 更新生命周期 this.currentLifetime += deltaTime; if (this.currentLifetime >= this.maxLifetime) { console.log('[EnemyProjectileInstance] 抛掷物超时,自动销毁'); this.isDestroyed = true; this.destroyProjectile(); return; } // 根据弹道类型移动抛掷物 if (this.useArcTrajectory) { this.updateArcTrajectory(deltaTime); } else { this.moveProjectile(deltaTime); } } /** * 初始化抛掷物 * @param config 抛掷物配置 */ public init(config: { damage: number; speed: number; direction: Vec3; projectileType: string; startPosition: Vec3; }) { console.log('[EnemyProjectileInstance] 初始化抛掷物配置:', config); this.damage = config.damage; this.speed = config.speed; this.direction = config.direction.clone().normalize(); this.projectileType = config.projectileType; // 注意:不在这里设置位置,因为节点还没有添加到Canvas // 位置设置由EnemyProjectile.ts在添加到Canvas后处理 // 初始化弧线弹道 this.initArcTrajectory(); console.log(`[EnemyProjectileInstance] 初始化抛掷物完成: 类型=${this.projectileType}, 伤害=${this.damage}, 速度=${this.speed}`); console.log('[EnemyProjectileInstance] 起始位置:', config.startPosition); } /** * 获取预制体中已配置的组件 */ private setupComponents() { // 获取预制体中已配置的刚体组件 this.rigidBody = this.getComponent(RigidBody2D); if (!this.rigidBody) { console.error('[EnemyProjectileInstance] 预制体中未找到RigidBody2D组件'); } else { console.log('[EnemyProjectileInstance] 找到RigidBody2D组件'); } // 获取预制体中已配置的碰撞器组件 this.collider = this.getComponent(Collider2D); if (!this.collider) { console.error('[EnemyProjectileInstance] 预制体中未找到Collider2D组件'); } else { console.log('[EnemyProjectileInstance] 找到Collider2D组件'); } // 获取预制体中已配置的精灵组件 this.sprite = this.getComponent(Sprite); if (!this.sprite) { console.error('[EnemyProjectileInstance] 预制体中未找到Sprite组件'); } else { console.log('[EnemyProjectileInstance] 找到Sprite组件'); } } /** * 设置抛掷物外观,使用3.png */ private async setupProjectileSprite() { if (!this.sprite) return; } /** * 设置碰撞监听 */ private setupCollisionListener() { if (this.collider) { this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); console.log('[EnemyProjectileInstance] 碰撞监听设置完成'); } } /** * 移动抛掷物 */ private moveProjectile(deltaTime: number) { if (!this.rigidBody) return; // 计算移动向量 const moveVector = this.direction.clone().multiplyScalar(this.speed * deltaTime); // 获取当前位置并添加移动向量 const currentPos = this.node.getWorldPosition(); const newPos = currentPos.add(moveVector); // 设置新位置 this.node.setWorldPosition(newPos); } /** * 初始化弧线弹道 */ private initArcTrajectory() { if (!this.useArcTrajectory) return; // 寻找最近的墙体作为目标 this.findNearestWall(); // 计算带45°随机偏移的初始方向 const baseDir = this.direction.clone().normalize(); const sign = Math.random() < 0.5 ? 1 : -1; const rad = 45 * Math.PI / 180 * sign; const cos = Math.cos(rad); const sin = Math.sin(rad); const offsetDir = new Vec3( baseDir.x * cos - baseDir.y * sin, baseDir.x * sin + baseDir.y * cos, 0 ).normalize(); this.arcDir = offsetDir; // 如果找到了目标墙体,设置目标方向 if (this.targetWallPosition) { const currentPos = this.node.getWorldPosition(); const dirToWall = this.targetWallPosition.clone().subtract(currentPos).normalize(); this.arcTargetDir = dirToWall; } else { // 如果没有找到墙体,使用基础方向 this.arcTargetDir = baseDir; } console.log(`[EnemyProjectileInstance] 初始化弧线弹道: 目标墙体位置=${this.targetWallPosition}`); } /** * 寻找最近的墙体 */ private findNearestWall() { const currentPos = this.node.getWorldPosition(); let nearestWall: Node = null; let nearestDistance = Infinity; // 方法1:通过EnemyController获取墙体节点 const enemyController = find('Canvas/GameLevelUI/EnemyController')?.getComponent('EnemyController') as any; if (enemyController) { const wallNodes = []; if (enemyController.topFenceNode) wallNodes.push(enemyController.topFenceNode); if (enemyController.bottomFenceNode) wallNodes.push(enemyController.bottomFenceNode); for (const wall of wallNodes) { if (wall && wall.active) { const distance = Vec3.distance(currentPos, wall.getWorldPosition()); if (distance < nearestDistance) { nearestDistance = distance; nearestWall = wall; } } } } // 方法2:在GameArea中搜索墙体节点 if (!nearestWall) { 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) && child.active) { const distance = Vec3.distance(currentPos, child.getWorldPosition()); if (distance < nearestDistance) { nearestDistance = distance; nearestWall = child; } } } } } // 方法3:直接搜索常见墙体路径 if (!nearestWall) { const wallPaths = [ 'Canvas/GameLevelUI/GameArea/TopFence', 'Canvas/GameLevelUI/GameArea/BottomFence', 'Canvas/GameLevelUI/Wall', 'Canvas/Wall', 'Canvas/TopWall', 'Canvas/BottomWall' ]; for (const path of wallPaths) { const wall = find(path); if (wall && wall.active) { const distance = Vec3.distance(currentPos, wall.getWorldPosition()); if (distance < nearestDistance) { nearestDistance = distance; nearestWall = wall; } } } } if (nearestWall) { this.targetWallPosition = nearestWall.getWorldPosition(); console.log(`[EnemyProjectileInstance] 找到目标墙体: ${nearestWall.name}, 距离: ${nearestDistance.toFixed(2)}`); } else { console.warn('[EnemyProjectileInstance] 未找到可用的墙体目标'); // 设置一个默认的目标位置(屏幕中心下方) this.targetWallPosition = new Vec3(0, -200, 0); } } /** * 更新弧线弹道 */ private updateArcTrajectory(deltaTime: number) { if (!this.arcDir || !this.arcTargetDir) return; // 重新寻找墙体目标(动态追踪) if (!this.targetWallPosition) { this.findNearestWall(); } // 更新目标方向 if (this.targetWallPosition) { const currentPos = this.node.getWorldPosition(); const dirToWall = this.targetWallPosition.clone().subtract(currentPos).normalize(); this.arcTargetDir.set(dirToWall); } // 计算转向 const rotateFactor = this.rotateSpeed * deltaTime; this.arcDir = this.arcDir.lerp(this.arcTargetDir, rotateFactor).normalize(); // 计算移动向量 const moveVector = this.arcDir.clone().multiplyScalar(this.speed * deltaTime); // 获取当前位置并添加移动向量 const currentPos = this.node.getWorldPosition(); const newPos = currentPos.add(moveVector); // 设置新位置 this.node.setWorldPosition(newPos); // 更新节点朝向(可选) const angle = Math.atan2(this.arcDir.y, this.arcDir.x) * 180 / Math.PI; this.node.angle = angle; } // 防止重复销毁的标志 private isDestroyed: boolean = false; /** * 碰撞开始事件 */ private onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) { // 如果已经被销毁,不再处理碰撞 if (this.isDestroyed) { console.log(`[EnemyProjectileInstance] 抛掷物已被销毁,忽略碰撞事件`); return; } const otherNode = otherCollider.node; const nodeName = otherNode.name; console.log(`[EnemyProjectileInstance] 抛掷物碰撞检测 - 碰撞对象: ${nodeName}, 节点路径: ${this.getNodePath(otherNode)}`); // 抛掷物的目标是墙体,检查是否碰到墙体 if (this.isWallNode(otherNode)) { console.log(`[EnemyProjectileInstance] 抛掷物击中目标墙体: ${nodeName}, 准备销毁`); this.hitWall(); return; } console.log(`[EnemyProjectileInstance] 抛掷物碰撞到非目标对象: ${nodeName}, 继续飞行`); } /** * 获取节点的完整路径 */ private getNodePath(node: Node): string { const path = []; let current = node; while (current) { path.unshift(current.name); current = current.parent; } return path.join('/'); } /** * 检查是否为墙体节点 */ 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; } /** * 检查是否为玩家方块 */ private isPlayerBlock(node: Node): boolean { // 检查节点名称或标签 return node.name.includes('Block') || node.name.includes('WeaponBlock'); } /** * 击中墙体 */ private hitWall() { // 防止重复销毁 if (this.isDestroyed) { console.log(`[EnemyProjectileInstance] 抛掷物已被销毁,忽略hitWall调用`); return; } this.isDestroyed = true; console.log(`[EnemyProjectileInstance] 抛掷物击中墙体,造成 ${this.damage} 点伤害,开始销毁流程`); // 对墙体造成伤害 EventBus.getInstance().emit(GameEvents.ENEMY_DAMAGE_WALL, { damage: this.damage, source: this.node }); // 播放击中音效 this.playHitSound(); // 销毁抛掷物 this.destroyProjectile(); } /** * 击中玩家方块(已移除,抛掷物不攻击玩家方块) */ /* private hitPlayerBlock(blockNode: Node) { // 防止重复销毁 if (this.isDestroyed) { console.log(`[EnemyProjectileInstance] 抛掷物已被销毁,忽略hitPlayerBlock调用`); return; } this.isDestroyed = true; console.log(`[EnemyProjectileInstance] 抛掷物击中玩家方块: ${blockNode.name},开始销毁流程`); // 对玩家方块造成伤害 - 使用事件系统 EventBus.getInstance().emit(GameEvents.ENEMY_DAMAGE_PLAYER_BLOCK, { damage: this.damage, blockNode: blockNode, source: this.node }); // 播放击中音效 this.playHitSound(); // 销毁抛掷物 this.destroyProjectile(); } */ /** * 播放击中音效 */ private playHitSound() { // 根据抛掷物类型播放不同音效 let soundPath = ''; switch (this.projectileType) { case 'arrow': soundPath = 'data/弹球音效/fire'; break; case 'magic_bolt': soundPath = 'data/弹球音效/fire'; break; default: soundPath = 'data/弹球音效/fire'; break; } if (soundPath) { Audio.playWeaponSound(soundPath); } } /** * 销毁抛掷物 */ private destroyProjectile() { // 防止重复销毁 if (this.isDestroyed && (!this.node || !this.node.isValid)) { console.log('[EnemyProjectileInstance] 抛掷物已被销毁,跳过重复销毁'); return; } if (this.node && this.node.isValid) { console.log('[EnemyProjectileInstance] 销毁抛掷物节点'); this.node.destroy(); } else { console.log('[EnemyProjectileInstance] 抛掷物节点已无效或不存在'); } } onDestroy() { // 清理碰撞监听 if (this.collider) { this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); } console.log('[EnemyProjectileInstance] 抛掷物实例组件已销毁'); } }