import { _decorator, Component, Node, UITransform, Vec2, Vec3 } from 'cc'; const { ccclass, executeInEditMode } = _decorator; /** * line 长度/角度随 startPos→endPos 更新(移动 endPos 即自动拉线)。 * 子节点 Spine 动画不改;收招须 resetEndPos,否则拆字换格后 endPos 留在旧世界坐标会拉出乱线。 */ @ccclass('SkillEye') @executeInEditMode(true) export class SkillEye extends Component { startPos: Node; endPos: Node; line: Node; dir: Vec2 = new Vec2(); private _endPosRestLocal: Vec3 | null = null; /** line UITransform 宽度:scale=1 时光束本地长度(戬/杨 prefab ≈543) */ private _beamBaseLen = 0; protected onLoad(): void { this.ensureBound(); } ensureBound(): void { if (!this.startPos?.isValid) { this.startPos = this.node.getChildByName('startPos'); } if (!this.endPos?.isValid) { this.endPos = this.node.getChildByName('endPos') ?? this.node.getChildByPath('line/endPos'); } if (!this.line?.isValid) { this.line = this.node.getChildByName('line'); } if (this.endPos?.isValid && !this._endPosRestLocal) { this._endPosRestLocal = this.endPos.position.clone(); } if (this.line?.isValid && this._beamBaseLen <= 0) { this._beamBaseLen = this.line.getComponent(UITransform)?.width ?? 200; } } protected update(_dt: number): void { if (this.line?.active) this.refreshBeam(); } refreshBeam(): void { this.ensureBound(); if (!this.startPos?.isValid || !this.endPos?.isValid || !this.line?.isValid) return; if (!this.endPos.active) return; this.line.setPosition(this.startPos.position); const sx = this.startPos.position.x; const sy = this.startPos.position.y; const ex = this.endPos.position.x; const ey = this.endPos.position.y; const dx = ex - sx; const dy = ey - sy; const distance = Math.hypot(dx, dy); if (distance <= 1e-4) { this.line.setScale(0, 1, 1); return; } this.dir.set(sx - ex, sy - ey); this.dir.normalize(); this.line.angle = Math.atan2(this.dir.y, this.dir.x) * 180 / Math.PI - 180; const baseLen = this._beamBaseLen > 0 ? this._beamBaseLen : 200; this.line.setScale(distance / baseLen, 1, 1); } /** endPos 落到目标世界坐标(UI 层级走父节点 convertToNodeSpaceAR) */ setEndWorldPos(world: Readonly): void { this.ensureBound(); if (!this.endPos?.isValid) return; const parent = this.endPos.parent; const ui = parent?.getComponent(UITransform); if (ui) { this.endPos.setPosition(ui.convertToNodeSpaceAR(world as Vec3)); } else { this.endPos.setWorldPosition(world); } this.refreshBeam(); } /** 收招/隐藏:endPos 回到预制体本地坐标,避免换格后 startPos/endPos 各画一条线 */ resetEndPos(): void { this.ensureBound(); if (this.endPos?.isValid && this._endPosRestLocal) { this.endPos.setPosition(this._endPosRestLocal); } } }