import { _decorator, Component, Node, Vec3, Vec2, v3, v2, CCInteger, CCFloat, Collider2D, Contact2DType, IPhysics2DContact, RigidBody2D, ERigidBody2DType, sp } from 'cc'; import { BattleDataManager, BezierFallEffectType } from '../BattleDataManager'; import { Monster } from '../Monster'; const { ccclass, property } = _decorator; // 无人机状态枚举 enum DroneState { ORBITING, // 轨道飞行 CHASING, // 追击目标 ATTACKING, // 攻击中 RETURNING // 返回轨道 } @ccclass('Drone') export class Drone extends Component { private getDroneManager() { return this.node.parent?.getComponent('DroneManager') as any; } @property public moveSpeed: number = 300; // 移动速度 @property public attackRange: number = 100; // 攻击范围 @property public attackDamage: number = 20; // 攻击力 @property public attackInterval: number = 1; // 攻击间隔(秒) @property public orbitSpeed: number = 1; // 轨道旋转速度(弧度/秒) @property(Node) public bulletLaunchNode: Node | null = null; // 子弹发射节点 private currentState: DroneState = DroneState.ORBITING; private orbitParams: {center: Vec3, a: number, b: number} = { center: new Vec3(200, 360, 0), // 椭圆中心 a: 150, // 长轴 b: 100 // 短轴 }; private orbitAngle: number = 0; // 轨道角度 private targetMonster: Node | null = null; private attackTimer: number = 0; private returnPoint: Vec3 = new Vec3(); private positionOffset: Vec3 = new Vec3(); // 用于避免重叠的偏移 private _tmpOrbitPos: Vec3 = new Vec3(); private _tmpTargetPos: Vec3 = new Vec3(); private _targetMonsterComp: Monster | null = null; private _offsetRecalcTimer = 0; private _offsetRecalcInterval = 0.12; private fuelCount: number = 0; // 燃油计数 _sk:sp.Skeleton = null!; curAnimationName = '' protected onLoad(): void { this._sk = this.node.getComponentInChildren(sp.Skeleton) } /** * 播放 Spine 动画 * @param name 动画名称 * @param callback 动画播放完成后的回调函数 * @param timeScale 动画播放速度倍率 * @param loop 是否循环播放 */ private playSpine(name: string, callback: Function = null, timeScale = 1, loop = true) { // 如果节点不存在,则直接返回 if (this.node == null) return; // 设置动画播放完成后的回调函数 // this._sk.setCompleteListener(() => { // if (callback) callback(); // }); // 设置动画播放速度倍率,考虑了战斗速度的影响 // this._sk.timeScale = timeScale * 1//BattleManager.Instance.LevelBattle.BattleSpeed; // 播放指定名称的动画,并设置是否循环播放 this._sk.setAnimation(0, name, loop); } playEffect(){ let nameStr = '' if(this.currentState == DroneState.ATTACKING){ nameStr = '1无人机漂浮' }else{ nameStr = '2无人机原地飞' } if(this.curAnimationName != nameStr){ this.curAnimationName = nameStr this.playSpine(nameStr) } } // 初始化无人机轨道参数 public init(orbitCenter: Vec3, a: number, b: number, startAngle: number) { this.orbitParams.center = orbitCenter; this.orbitParams.a = a; this.orbitParams.b = b; this.orbitAngle = startAngle; // 设置初始位置 const startPos = this.calculateOrbitPosition(startAngle); this.node.setPosition(startPos); } // 计算椭圆轨道上的位置 private calculateOrbitPosition(angle: number): Vec3 { const x = this.orbitParams.center.x + this.orbitParams.a * Math.cos(angle); const y = this.orbitParams.center.y + this.orbitParams.b * Math.sin(angle); this._tmpOrbitPos.x = x; this._tmpOrbitPos.y = y; this._tmpOrbitPos.z = 0; return this._tmpOrbitPos; } update(deltaTime: number) { if (!gg.game.CurentBattle.canUpdateFrame()) return; const scaledDt = deltaTime * gg.game.CurentBattle.TimeScale; this.playEffect() switch (this.currentState) { case DroneState.ORBITING: this.updateOrbiting(scaledDt); break; case DroneState.CHASING: this.updateChasing(scaledDt); break; case DroneState.ATTACKING: this.updateAttacking(scaledDt); break; case DroneState.RETURNING: this.updateReturning(scaledDt); break; } } // 寻找攻击范围内的怪物 private findTarget(){ // const monsters = find('Monsters')?.getComponentsInChildren(Monster) || []; // const dronePos = this.node.position; // for (const monster of monsters) { // if (!monster.node.active) continue; // const distance = Vec3.distance(dronePos, monster.node.position); // if (distance <= this.attackRange) { // return monster; // } // } // return null; let monster = gg.game.CurentBattle.MonsterSpawner.getDroneTargetMonster(this.node,this.attackRange); return monster; } // 检查攻击同一目标的其他无人机 private getSameTargetDrones(): Drone[] { if (!this.targetMonster) return []; const allDrones = this.node.parent.getComponentsInChildren(Drone) || []; return allDrones.filter(drone => drone.targetMonster === this.targetMonster && (drone.currentState === DroneState.ATTACKING || drone.currentState === DroneState.CHASING) ); } // 更新位置偏移以避免重叠 private updatePositionOffset() { const sameTargetDrones = this.getSameTargetDrones(); const totalCount = sameTargetDrones.length + 1; // 包括自己 if (totalCount <= 1) { this.positionOffset.set(0, 0, 0); return; } // 计算偏移角度,形成圆形分布 const index = sameTargetDrones.indexOf(this); // const angle = (index + 1) * (Math.PI * 2 / totalCount); const offsetDistance = 50; // 偏移距离,小于无人机一半大小(25) let x = -(totalCount-1)*offsetDistance/2 + index*offsetDistance; this.positionOffset.set(x, 0, 0); // this.positionOffset.set( // Math.cos(angle) * offsetDistance, // Math.sin(angle) * offsetDistance, // 0 // ); } // 移动到目标位置 private moveToTarget(targetPos: Vec3, deltaTime: number): boolean { const currentPos = this.node.position; const dx = targetPos.x - currentPos.x; const dy = targetPos.y - currentPos.y; const dz = targetPos.z - currentPos.z; const distSq = dx * dx + dy * dy + dz * dz; const moveStep = this.moveSpeed * deltaTime; const moveStepSq = moveStep * moveStep; if (distSq <= moveStepSq) { this.node.setPosition(targetPos.x, targetPos.y, targetPos.z); return true; } const dist = Math.sqrt(distSq); const ratio = moveStep / dist; this.node.setPosition( currentPos.x + dx * ratio, currentPos.y + dy * ratio, currentPos.z + dz * ratio ); return false; } // 寻找最近的轨道点 private findNearestOrbitPoint(): Vec3 { const currentPos = this.node.position; let nearestAngle = 0; let minDistance = Infinity; // 采样多个角度找到最近点 for (let angle = 0; angle < Math.PI * 2; angle += 0.1) { const pos = this.calculateOrbitPosition(angle); const dist = Vec3.distance(currentPos, pos); if (dist < minDistance) { minDistance = dist; nearestAngle = angle; } } this.orbitAngle = nearestAngle; return this.calculateOrbitPosition(nearestAngle); } // 轨道飞行状态更新 private updateOrbiting(deltaTime: number) { // 检查是否有可攻击目标 const target = this.findTarget(); if (target) { this.targetMonster = target; this._targetMonsterComp = target.getComponent(Monster); this.currentState = DroneState.CHASING; this._offsetRecalcTimer = this._offsetRecalcInterval; return; } // 沿椭圆轨道移动 this.orbitAngle += this.orbitSpeed * deltaTime; if (this.orbitAngle >= Math.PI * 2) { this.orbitAngle -= Math.PI * 2; } const newPos = this.calculateOrbitPosition(this.orbitAngle); this.node.setPosition(newPos); } // 追击状态更新 private updateChasing(deltaTime: number) { if (!this.targetMonster || !this.targetMonster.isValid) { this._targetMonsterComp = null; this.currentState = DroneState.RETURNING; this.returnPoint = this.findNearestOrbitPoint(); return; } if (!this._targetMonsterComp) this._targetMonsterComp = this.targetMonster.getComponent(Monster); if (this._targetMonsterComp?.getIsDead()) { this._targetMonsterComp = null; this.currentState = DroneState.RETURNING; this.returnPoint = this.findNearestOrbitPoint(); return; } // 计算目标上方50单位的位置并应用偏移 this._offsetRecalcTimer += deltaTime; if (this._offsetRecalcTimer >= this._offsetRecalcInterval) { this._offsetRecalcTimer = 0; this.updatePositionOffset(); } const tp = this.targetMonster.position; this._tmpTargetPos.x = tp.x + this.positionOffset.x; this._tmpTargetPos.y = tp.y + 200 + this.positionOffset.y; this._tmpTargetPos.z = tp.z + this.positionOffset.z; // 移动到目标位置 const reached = this.moveToTarget(this._tmpTargetPos, deltaTime); if (reached) { this.currentState = DroneState.ATTACKING; this._offsetRecalcTimer = this._offsetRecalcInterval; this.attackTimer = 0; } } // 攻击状态更新 private updateAttacking(deltaTime: number) { if (!this.targetMonster || !this.targetMonster.isValid) { this._targetMonsterComp = null; // 目标已消失,检查新目标 const newTarget = this.findTarget(); if (newTarget) { this.targetMonster = newTarget; this._targetMonsterComp = newTarget.getComponent(Monster); this.currentState = DroneState.CHASING; this._offsetRecalcTimer = this._offsetRecalcInterval; } else { this.currentState = DroneState.RETURNING; this._targetMonsterComp = null; this.returnPoint = this.findNearestOrbitPoint(); } return; } if (!this._targetMonsterComp) this._targetMonsterComp = this.targetMonster.getComponent(Monster); if (this._targetMonsterComp?.getIsDead()) { const newTarget = this.findTarget(); if (newTarget) { this.targetMonster = newTarget; this._targetMonsterComp = newTarget.getComponent(Monster); this.currentState = DroneState.CHASING; this._offsetRecalcTimer = this._offsetRecalcInterval; } else { this.currentState = DroneState.RETURNING; this._targetMonsterComp = null; this.returnPoint = this.findNearestOrbitPoint(); } return; } // 保持在目标上方 this._offsetRecalcTimer += deltaTime; if (this._offsetRecalcTimer >= this._offsetRecalcInterval) { this._offsetRecalcTimer = 0; this.updatePositionOffset(); } const tp = this.targetMonster.position; this.node.setPosition( tp.x + this.positionOffset.x, tp.y + 200 + this.positionOffset.y, tp.z + this.positionOffset.z ); // 攻击计时器 // this.attackTimer += deltaTime; // if (this.attackTimer >= this.attackInterval) { // this.attackTimer = 0; // this.attackTarget(); // } } // 返回轨道状态更新 private updateReturning(deltaTime: number) { // 检查是否有新目标 const newTarget = this.findTarget(); if (newTarget) { this.targetMonster = newTarget; this._targetMonsterComp = newTarget.getComponent(Monster); this.currentState = DroneState.CHASING; this._offsetRecalcTimer = this._offsetRecalcInterval; return; } // 返回轨道 const reached = this.moveToTarget(this.returnPoint, deltaTime); if (reached) { this.currentState = DroneState.ORBITING; this.targetMonster = null; this._targetMonsterComp = null; } } // 攻击目标 public attackTarget(weaponId:number) { if (this.currentState == DroneState.ATTACKING && this.targetMonster && this.targetMonster.isValid && !this.targetMonster.getComponent(Monster).getIsDead()) { // console.log("攻击目标attackTarget"); let now = new Date().getTime() const manager = this.getDroneManager(); if(manager && now - manager.audio_timeStamp > 140){ manager.audio_timeStamp = now gg.audio.playEffect({ name: '无人机攻击',path:'sound/武器音效/'}); } let isFuelBomb = false //特殊技能(丢燃烧弹) let weaponPropertyData = BattleDataManager.getWeaponAddProperty(weaponId) let specialSkillAddFallEffectInfo = weaponPropertyData.specialSkillAddFallEffectInfo for(let i = 0;i= atkCount){ isFuelBomb = true this.fuelCount = 0; gg.game.CurentBattle.SkillController.executeDroneWeaponSkill(weaponId,this.bulletLaunchNode.worldPosition.clone().add(v3(0,-20,0)),this.targetMonster.position.clone(),true) break } } } if(!isFuelBomb){ gg.game.CurentBattle.SkillController.executeDroneWeaponSkill(weaponId,this.bulletLaunchNode.worldPosition.clone().add(v3(0,-20,0)),this.targetMonster.position.clone(),false) } } } }