ArcBullet.ts 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { _decorator, Component, Node, Vec3, v3 } from 'cc';
  2. const { ccclass, property } = _decorator;
  3. /**
  4. * ArcBullet
  5. * 负责控制子弹沿弧线(曲线)飞行
  6. * 思路:
  7. * 1. 初始化时给子弹一个带有随机左右偏移的初始方向(45°)。
  8. * 2. 在 update 中使用 Vec3.slerp 将当前方向逐步朝向目标方向插值,形成弧线效果。
  9. * 3. 用方向 * 速度 * dt 更新 worldPosition 即可。
  10. */
  11. @ccclass('ArcBullet')
  12. export class ArcBullet extends Component {
  13. /** 子弹飞行速度(像素/秒) */
  14. @property
  15. public moveSpeed: number = 800;
  16. /** 转向速率(0~1 越大越快) */
  17. @property
  18. public rotateSpeed: number = 0.5;
  19. /** 追踪目标(可选)。不设置则默认向屏幕上方飞行。 */
  20. @property(Node)
  21. public target: Node | null = null;
  22. // 当前方向
  23. private _dir: Vec3 = new Vec3();
  24. start() {
  25. const pos = this.node.worldPosition;
  26. // 计算目标点
  27. let targetPos: Vec3;
  28. if (this.target && this.target.isValid) {
  29. targetPos = this.target.worldPosition.clone();
  30. } else {
  31. // 没有目标时,默认向上方 1200 像素处飞行
  32. targetPos = v3(pos.x, pos.y + 1200, pos.z);
  33. }
  34. // 朝向目标的单位向量
  35. const dirToTarget = targetPos.subtract(pos).normalize();
  36. // 在初始方向基础上增加 45° 随机左右偏转,形成弧线起始
  37. const sign = Math.random() < 0.5 ? 1 : -1; // +1 向左,-1 向右
  38. const rad = 45 * Math.PI / 180 * sign;
  39. const cos = Math.cos(rad);
  40. const sin = Math.sin(rad);
  41. const offsetDir = new Vec3(
  42. dirToTarget.x * cos - dirToTarget.y * sin,
  43. dirToTarget.x * sin + dirToTarget.y * cos,
  44. 0
  45. ).normalize();
  46. this._dir.set(offsetDir);
  47. }
  48. update(dt: number) {
  49. if (dt === 0) return;
  50. // 如果有有效目标,则逐步朝向目标
  51. if (this.target && this.target.isValid) {
  52. const pos = this.node.worldPosition;
  53. const dirToTarget = this.target.worldPosition.clone().subtract(pos).normalize();
  54. // 根据距离动态调整转向速率:越近转得越快,避免近距离打圈
  55. const distance = Vec3.distance(pos, this.target.worldPosition);
  56. // 基础转向速率(可调节 rotateSpeed),距离越小,factor 越大
  57. const factor = this.rotateSpeed * dt * (2000 / Math.max(distance, 50));
  58. const newDir = new Vec3();
  59. Vec3.slerp(newDir, this._dir, dirToTarget, Math.min(1, factor));
  60. this._dir.set(newDir);
  61. }
  62. // 位移更新
  63. const displacement = this._dir.clone().multiplyScalar(this.moveSpeed * dt);
  64. this.node.worldPosition = this.node.worldPosition.add(displacement);
  65. // 超出屏幕范围时自动销毁(简易处理)
  66. if (Math.abs(this.node.worldPosition.x) > 4000 || Math.abs(this.node.worldPosition.y) > 4000) {
  67. this.node.destroy();
  68. }
  69. }
  70. }