You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
3.2 KiB

3 weeks ago
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<Vec3>): 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);
}
}
}