BulletLifecycle.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { _decorator, Component, Vec3 } from 'cc';
  2. import { BulletLifecycleConfig, HitResult } from '../BulletTypes';
  3. const { ccclass } = _decorator;
  4. /**
  5. * 生命周期控制(可移植版):范围、时间、穿透/弹射计数、返回发射源等
  6. */
  7. @ccclass('BCBulletLifecycle')
  8. export class BulletLifecycle extends Component {
  9. private cfg: BulletLifecycleConfig | null = null;
  10. private origin: Vec3 = new Vec3();
  11. private lifetime = 0;
  12. private distance = 0;
  13. private piercingLeft = 0;
  14. private ricochetLeft = 0;
  15. private shouldReturn = false;
  16. private returnTimer = 0;
  17. public init(cfg: BulletLifecycleConfig, startPos: Vec3, pierceInit?: number, ricochetInit?: number) {
  18. this.cfg = { ...cfg };
  19. this.origin.set(startPos);
  20. this.lifetime = 0;
  21. this.distance = 0;
  22. this.piercingLeft = Math.max(0, pierceInit ?? (cfg?.penetration ?? 0));
  23. this.ricochetLeft = Math.max(0, ricochetInit ?? (cfg?.ricochetCount ?? 0));
  24. this.returnTimer = 0;
  25. this.shouldReturn = !!cfg?.returnToOrigin;
  26. }
  27. update(dt: number) {
  28. if (!this.cfg) return;
  29. this.lifetime += dt;
  30. const pos = this.node.worldPosition;
  31. const dx = pos.x - this.origin.x;
  32. const dy = pos.y - this.origin.y;
  33. this.distance = Math.sqrt(dx * dx + dy * dy);
  34. // 处理返回延迟
  35. if (this.shouldReturn) {
  36. const delay = Math.max(0, this.cfg.returnDelay ?? 0);
  37. if (this.lifetime >= delay) {
  38. // 上层可以根据 shouldReturnNow 触发回程逻辑(例如改变弹道方向)
  39. }
  40. }
  41. }
  42. /** 命中时通知,用于更新计数 */
  43. public notifyHit(hit: HitResult) {
  44. if (hit.pierced) {
  45. if (this.piercingLeft > 0) this.piercingLeft -= 1;
  46. } else {
  47. // 非穿透命中默认视为消耗一次 penetration(例如普通 hit_destroy)
  48. if (this.piercingLeft > 0) this.piercingLeft -= 1;
  49. }
  50. if (hit.ricochet) {
  51. if (this.ricochetLeft > 0) this.ricochetLeft -= 1;
  52. }
  53. }
  54. /** 是否应该立即销毁 */
  55. public shouldDestroyNow(): boolean {
  56. if (!this.cfg) return true;
  57. // 时间限制
  58. const maxLifetime = this.cfg.maxLifetime ?? Infinity;
  59. if (this.lifetime > maxLifetime) return true;
  60. // 射程限制
  61. const maxRange = this.cfg.maxRange ?? Infinity;
  62. if (this.cfg.type === 'range_limit' && this.distance > maxRange) return true;
  63. // 穿透/弹射计数耗尽
  64. if (this.cfg.type === 'hit_destroy' && this.piercingLeft <= 0) return true;
  65. if (this.cfg.type === 'ricochet_counter' && this.ricochetLeft <= 0) return true;
  66. // ground_impact:由外层根据地面碰撞事件触发销毁,此处不主动判定
  67. return false;
  68. }
  69. /** 是否进入回程阶段(供上层改变弹道方向) */
  70. public shouldReturnNow(): boolean {
  71. if (!this.cfg || !this.shouldReturn) return false;
  72. const delay = Math.max(0, this.cfg.returnDelay ?? 0);
  73. return this.lifetime >= delay;
  74. }
  75. }