/** * 泡泡持续攻击 */ import { _decorator, Component, isValid, Node, random, sp, UITransform, Vec2, Vec3 } from 'cc'; import { BattleDataManager, IWeaponProperty } from '../BattleDataManager'; import { GEvent } from 'db://assets/mx/module/event/GEvent'; import { Skill } from './Skill'; const { ccclass, property } = _decorator; @ccclass('BubbleSchedule') export class BubbleSchedule extends Component { _dt: number = 0; _dtAll: number = 0; //累计时间 _weaponid: number = 0; _weaponContinueTime: number = 0; //持续时间 isCanAtk: boolean = false; //是否可以攻击造成伤害 isStartGame: boolean = false; //是否开始游戏 _updatePosiTime: number = 0//0.05; //更新位置时间 _updatePosiInterval: number = 0.03; //更新位置间隔 _speed: number = 0; //速度 targetPosi: Vec2 = null; //目标位置 isCanAtkTime = 0 private _skillComp: any = null; private _isDestroying: boolean = false; protected onLoad(): void { //console.log('手电筒激光武器持续攻击 onLoad') this.isStartGame = false this._skillComp = this.node.getComponent('Skill') as any; } start() { //console.log('手电筒激光武器持续攻击 start') //EVENT_UPDATE_BUBBLE GEvent.Ins.on(GEvent.EVENT_UPDATE_BUBBLE, this.updateBubble, this) } updateBubble() { //console.log('更新泡泡持续攻击时间和速度') let weaponProperty = BattleDataManager.getWeaponAddProperty(this._weaponid) if (this._speed !== weaponProperty.speed) { this._speed = weaponProperty.speed } if (this._weaponContinueTime !== weaponProperty.weaponContinueTime) { this._weaponContinueTime = weaponProperty.weaponContinueTime } } update(deltaTime: number) { const scaledDt = deltaTime * gg.game.CurentBattle.TimeScale; if (!this.isCanAtk) { this.isCanAtkTime += scaledDt if (this.isCanAtkTime >= 0.05) { this.isCanAtkTime = 0 this.isCanAtk = true } } if (!this._skillComp) this._skillComp = this.node.getComponent('Skill') as any; if (!this.isStartGame || !this.isCanAtk || this._skillComp.getIsDead()) { //没有开始或者死亡 return } if (!gg.game.CurentBattle.canUpdateFrame()) return; this._dtAll += scaledDt this._dt += scaledDt if (this._dtAll >= this._weaponContinueTime) { this.isStartGame = false this.destroyNode() } else { this._updatePosiTime += scaledDt if (this._updatePosiTime >= this._updatePosiInterval) { this._updatePosiTime = 0 //移到到目标位置, 到了targetPosi之后,更新下一个位置。(只有碰到怪物或者时间到了才会销毁) //this._speed 移动this.targetPosi // 检查是否已经有目标位置,如果没有则生成一个 if (!this.targetPosi || (this.targetPosi.x === 0 && this.targetPosi.y === 0)) { this.targetPosi = this.getRundomPosi(); } const currentPos = this.node.position; const dx = this.targetPosi.x - currentPos.x; const dy = this.targetPosi.y - currentPos.y; // 增加目标点检测阈值,避免在小范围内频繁切换 const arrivalThreshold = Math.max(20, this._speed * this._updatePosiInterval * 2); // 动态阈值,与速度相关 const distSq = dx * dx + dy * dy; const arrivalThresholdSq = arrivalThreshold * arrivalThreshold; if (distSq < arrivalThresholdSq) { // 使用动态阈值 // 到达目标位置,生成新的随机目标位置(确保与当前位置有最小距离) let newTarget = this.getRundomPosi(); const minDistance = Math.max(50, this._speed); // 最小距离不小于50,且与速度成正比 const minDistanceSq = minDistance * minDistance; let dx2 = newTarget.x - currentPos.x; let dy2 = newTarget.y - currentPos.y; let newDistanceSq = dx2 * dx2 + dy2 * dy2; let retryCount = 0; while (newDistanceSq < minDistanceSq && retryCount < 10) { newTarget = this.getRundomPosi(); dx2 = newTarget.x - currentPos.x; dy2 = newTarget.y - currentPos.y; newDistanceSq = dx2 * dx2 + dy2 * dy2; retryCount++; } this.targetPosi = newTarget; // 停留一秒 this.isCanAtkTime = 0 this.isCanAtk = false } else { const dist = Math.sqrt(distSq); // 归一化后的方向向量 const dirX = dx / dist; const dirY = dy / dist; // 根据速度计算移动距离 const moveDistance = this._speed * this._updatePosiInterval; // 计算新位置(避免超过目标点) let newX = currentPos.x + dirX * moveDistance; let newY = currentPos.y + dirY * moveDistance; const targetX = this.targetPosi.x; const targetY = this.targetPosi.y; const ndx = targetX - newX; const ndy = targetY - newY; const newDistanceToTargetSq = ndx * ndx + ndy * ndy; if (newDistanceToTargetSq < distSq) { // 正常移动 this.node.setPosition(newX, newY, currentPos.z); } else { // 即将超过目标点,直接到达目标点 this.node.setPosition(this.targetPosi.x, this.targetPosi.y, currentPos.z); // 立即生成新的目标点 let newTarget = this.getRundomPosi(); let dx2 = newTarget.x - this.targetPosi.x; let dy2 = newTarget.y - this.targetPosi.y; let newDistanceSq = dx2 * dx2 + dy2 * dy2; const minDistance = Math.max(50, this._speed); const minDistanceSq = minDistance * minDistance; let retryCount = 0; while (newDistanceSq < minDistanceSq && retryCount < 10) { newTarget = this.getRundomPosi(); dx2 = newTarget.x - this.targetPosi.x; dy2 = newTarget.y - this.targetPosi.y; newDistanceSq = dx2 * dx2 + dy2 * dy2; retryCount++; } this.targetPosi = newTarget; } } } } } atkToMonster(weaponId: number, index: number, weaponProperty: IWeaponProperty) { let playAnimStart = this.node.getChildByName('泡泡') playAnimStart.getComponent(sp.Skeleton).setAnimation(0, '泡泡飞行', true) this._weaponid = weaponId this._speed = weaponProperty.speed this._weaponContinueTime = weaponProperty.weaponContinueTime this.targetPosi = this.getRundomPosi() this._dtAll = 0 this._updatePosiTime = 0 this.isCanAtkTime = 0 this.isStartGame = true this.isCanAtk = true } getRundomPosi() { //从14*10的格子选取一个格子位置, 格子位置从(-237,-343)开始 //每一列14个,总共10列, y轴每个位置+53, x轴每个位置+51 //第一列第一个(-237,-343), (-237,-290),(-237,-238)以此类推 //第二列(-186,-343), (-186,-290),(-186,-238)以此类推 //划分的在细一点,100*140 let randY = Math.floor(random() * 120) // 0-10不包括10 let randX = Math.floor(random() * 140) // 0-14不包括14 //console.log('随机到的格子位置',randX,randY) let y = -237 + randY * 7; let x = -343 + randX * 5.5; x = Math.random() * 700 - 350; y = Math.random() * 400 - 200; //console.log('随机到的格子位置',randX,randY, x,y) let posi = new Vec2(x, y) return posi } destroyNode() { if (this._isDestroying) return; this._isDestroying = true; // 生成毒液区域:泡泡节点在 SkillController 下,需把世界坐标转为 Map 本地坐标 // (SkillBase.createBombComponent 是 setParent(Map.node) + setPosition(localPos)) const worldPos = this.node.worldPosition.clone(); const mapNode = gg.game.CurentBattle.Map.node; const mapUi = mapNode.getComponent(UITransform); const originPos = mapUi ? mapUi.convertToNodeSpaceAR(worldPos) : this.node.position.clone(); this.node.getComponent(Skill).playBubbleSanitizer(originPos) this.node.getComponent(Skill).setIsDead(true) // console.log('销毁泡泡武器') //播放动效 let playAnimStart = this.node.getChildByName('泡泡') const sk = playAnimStart?.getComponent(sp.Skeleton); sk?.setAnimation(0, '泡泡炸裂', false) this.scheduleOnce(() => { //移除当前脚本 GEvent.Ins.off(GEvent.EVENT_UPDATE_BUBBLE, this.updateBubble, this) this.removeSelf() gg.res.putNode(this.node) }, 0.4) } /**刷新激光武器ui */ removeSelf() { this.destroy() } onDestroy() { // 确保回收时不会残留事件订阅/定时回调 this.unscheduleAllCallbacks(); GEvent.Ins.off(GEvent.EVENT_UPDATE_BUBBLE, this.updateBubble, this); } }