| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- 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] 抛掷物实例组件已销毁');
- }
- }
|