| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- 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();
- }
- }
- }
|