import { _decorator, Component, Node, Vec3, v3 } from 'cc'; const { ccclass, property } = _decorator; /** * ArcBullet * 负责控制子弹沿弧线(曲线)飞行 * 思路: * 1. 初始化时给子弹一个带有随机左右偏移的初始方向(45°)。 * 2. 在 update 中使用 Vec3.slerp 将当前方向逐步朝向目标方向插值,形成弧线效果。 * 3. 用方向 * 速度 * dt 更新 worldPosition 即可。 */ @ccclass('ArcBullet') export class ArcBullet extends Component { /** 子弹飞行速度(像素/秒) */ @property public moveSpeed: number = 800; /** 转向速率(0~1 越大越快) */ @property public rotateSpeed: number = 0.5; /** 追踪目标(可选)。不设置则默认向屏幕上方飞行。 */ @property(Node) public target: Node | null = null; // 当前方向 private _dir: Vec3 = new Vec3(); start() { const pos = this.node.worldPosition; // 计算目标点 let targetPos: Vec3; if (this.target && this.target.isValid) { targetPos = this.target.worldPosition.clone(); } else { // 没有目标时,默认向上方 1200 像素处飞行 targetPos = v3(pos.x, pos.y + 1200, pos.z); } // 朝向目标的单位向量 const dirToTarget = targetPos.subtract(pos).normalize(); // 在初始方向基础上增加 45° 随机左右偏转,形成弧线起始 const sign = Math.random() < 0.5 ? 1 : -1; // +1 向左,-1 向右 const rad = 45 * Math.PI / 180 * sign; const cos = Math.cos(rad); const sin = Math.sin(rad); const offsetDir = new Vec3( dirToTarget.x * cos - dirToTarget.y * sin, dirToTarget.x * sin + dirToTarget.y * cos, 0 ).normalize(); this._dir.set(offsetDir); } update(dt: number) { if (dt === 0) return; // 如果有有效目标,则逐步朝向目标 if (this.target && this.target.isValid) { const pos = this.node.worldPosition; const dirToTarget = this.target.worldPosition.clone().subtract(pos).normalize(); // 根据距离动态调整转向速率:越近转得越快,避免近距离打圈 const distance = Vec3.distance(pos, this.target.worldPosition); // 基础转向速率(可调节 rotateSpeed),距离越小,factor 越大 const factor = this.rotateSpeed * dt * (2000 / Math.max(distance, 50)); const newDir = new Vec3(); Vec3.slerp(newDir, this._dir, dirToTarget, Math.min(1, factor)); this._dir.set(newDir); } // 位移更新 const displacement = this._dir.clone().multiplyScalar(this.moveSpeed * dt); this.node.worldPosition = this.node.worldPosition.add(displacement); // 超出屏幕范围时自动销毁(简易处理) if (Math.abs(this.node.worldPosition.x) > 4000 || Math.abs(this.node.worldPosition.y) > 4000) { this.node.destroy(); } } }