import { _decorator, Component, Node, UITransform, Vec3 } from 'cc'; import { EDITOR } from 'cc/env'; import { Monster, MonsterTag } from '../Monster'; import { IWeaponProperty } from '../BattleDataManager'; import { Skill } from './Skill'; import { GEvent } from 'db://assets/mx/module/event/GEvent'; const { ccclass, property, executeInEditMode } = _decorator; @ccclass('LaserLine') @executeInEditMode export class LaserLine extends Component { startNode: Node = null; endNode: Node = null; line: Node = null; Data: IWeaponProperty = null; index: number = 0; private _curentTarget: Monster = null; private _time = 10; private _cd = 0; private _weaponId = 0; private _findTargetDis = 200; private _tetriLevel = 1; private _lineUiTransform: UITransform | null = null; private _lineWidth = 1; protected onLoad() { this.startNode = this.node.getChildByName("起点"); this.endNode = this.node.getChildByName("末点"); this.line = this.node.getChildByName("line"); this._lineUiTransform = this.line.getComponent(UITransform); this._lineWidth = this._lineUiTransform?.width || 1; this.updateLine(); } protected onEnable(): void { GEvent.Ins.on(GEvent.BattleWarnTimeStart, this._onWarnTimeStart, this); } protected onDisable(): void { GEvent.Ins.off(GEvent.BattleWarnTimeStart, this._onWarnTimeStart, this); } private _onWarnTimeStart() { this.unschedule(this._warnRemoveScheduled); this.scheduleOnce(this._warnRemoveScheduled, 0); } private _warnRemoveScheduled = () => { if (this.node?.isValid) { this.remove(); } }; setData(weaponId: number, data: IWeaponProperty, index: number = 0, tetriLevel: number = 1) { this.index = index; this.Data = data; this._cd = 0; this._findTargetDis = 0; this._weaponId = weaponId; this._tetriLevel = Number.isFinite(tetriLevel as any) && (tetriLevel as any) > 0 ? (tetriLevel as any) : 1; this._time = this.Data.weaponContinueTime; this.getOneLaserAtkTarget(index); } protected update(dt: number): void { if (EDITOR) { // 编辑器模式下更新线 this.updateLine(); return; } const battle = gg.game?.CurentBattle; if (!battle) { return; } // 普通关卡:预警会 pause,canUpdateFrame 为 false 时 _time 不走、事件也可能早于本帧执行顺序漏掉。 // Boss 关预警不 pause,不在这里清激光(仍允许攻击)。 if (battle.IsWarnTimeActive && battle.IsGamePause) { this.remove(); return; } if (!battle.canUpdateFrame()) { return; } if (this._time < 0) { this.remove(); return; } this._cd -= dt * gg.game.CurentBattle.TimeScale; this._time -= dt * gg.game.CurentBattle.TimeScale; if (this._cd < 0) { this.atk(); } if (this._curentTarget && this._curentTarget.checkTag(MonsterTag.IS_BE_ATTACKED_BY_LASER + this.index) && this._curentTarget.node && this._curentTarget.node.isValid && this._curentTarget.node.active) { this.endNode.worldPosition = this._curentTarget.node.worldPosition; this.updateLine(); } else { if (this._curentTarget) { this._curentTarget.setTag(MonsterTag.IS_BE_ATTACKED_BY_LASER + this.index, false); this._curentTarget = null; } this.startNode.active = false; this.endNode.active = false; this.line.active = false; this.getOneLaserAtkTarget(this.index); } } /**设置目标坐标(世界坐标) */ setTargetPos(pos: Vec3) { this.endNode.worldPosition = pos; this.updateLine(); } private updateLine() { if(!this.Data){ return } this.startNode.active = true; this.endNode.active = true; this.line.active = true; let startPos = this.startNode.worldPosition; let endPos = this.endNode.worldPosition; const dx = startPos.x - endPos.x; const dy = startPos.y - endPos.y; const dz = startPos.z - endPos.z; const disSq = dx * dx + dy * dy + dz * dz; if (disSq < 1e-8) { return } const dis = Math.sqrt(disSq); const invDis = 1 / dis; const dirX = dx * invDis; const dirY = dy * invDis; const scole = dis / this._lineWidth; // Vec3.angle((-1,0,0), dir) 等价于 acos(dot);ref 向量长度为 1,因此 dot = (-1)*dirX = -dirX let dot = -dirX; if (dot > 1) dot = 1; else if (dot < -1) dot = -1; let angle = Math.acos(dot) * 180 / Math.PI; if (dirY > 0) angle = -angle; this.line.setWorldPosition(startPos.x, startPos.y, startPos.z); this.line.setScale(scole, 1, 1); this.line.angle = angle; let index = 0; // if (this.Data.isHavePurpleSkill) index = 1; // if (this.Data.isHaveOrangeSkill) index = 2; // for (let i = 0; i < this.line.children.length; i++) { // this.line.children[i].active = false; // } this.line.children[index].active = true; } private getOneLaserAtkTarget(index: number = 0) { const skill = this.node.getComponent(Skill); if (!skill) return; const findNode = skill.pickNearestMonsterByIndex(index); if (!findNode || !findNode.isValid) return; const monster = findNode.getComponent(Monster); //console.log('monster+++++++++===1111', monster) if (!monster || monster.IsDead || !monster.node || !monster.node.isValid) return; //console.log('monster+++++++++===222', monster) this._curentTarget = monster; this._curentTarget.setTag(MonsterTag.IS_BE_ATTACKED_BY_LASER + index, true); this.setTargetPos(this._curentTarget.node.worldPosition); this._findTargetDis = 2000; } atk() { if (this._curentTarget && !this._curentTarget.IsDead) { this._curentTarget.attackByWeapon(this._weaponId, undefined, this._tetriLevel); } this._cd = this.Data.damageInterval; } remove() { if (!this.isValid) { return; } this.unscheduleAllCallbacks(); if (this._curentTarget) { this._curentTarget.setTag(MonsterTag.IS_BE_ATTACKED_BY_LASER + this.index, false); this._curentTarget = null; } gg.res.putNode(this.node); } }