import { _decorator, Component, Node, Vec3, Vec2, find, UITransform, RigidBody2D } from 'cc'; import { BulletTrajectory } from './BulletTrajectory'; const { ccclass, property } = _decorator; /** * 子弹生命周期控制器 * 负责管理子弹的生存时间和销毁条件 */ export interface BulletLifecycleConfig { type: 'hit_destroy' | 'range_limit' | 'ricochet_counter' | 'ground_impact' | 'return_trip' | 'ground_impact_with_effect' | 'target_impact'; maxLifetime: number; // 最大生存时间(秒) penetration: number; // 穿透次数 ricochetCount: number; // 弹射次数 returnToOrigin: boolean; // 是否返回原点 returnDelay?: number; // 返回延迟(秒) maxRange?: number; // 最大射程 effectDuration?: number; // 效果持续时间(用于地面效果) } export interface LifecycleState { elapsedTime: number; // 已存活时间 hitCount: number; // 命中次数 ricochetLeft: number; // 剩余弹射次数 pierceLeft: number; // 剩余穿透次数 travelDistance: number; // 已飞行距离 phase: 'active' | 'returning' | 'effect' | 'destroyed'; // 生命周期阶段 shouldDestroy: boolean; // 是否应该销毁 startPosition: Vec3; // 起始位置 returnTimer: number; // 返回计时器 } @ccclass('BulletLifecycle') export class BulletLifecycle extends Component { private config: BulletLifecycleConfig = null; private state: LifecycleState = null; private lastPosition: Vec3 = new Vec3(); /** * 初始化生命周期 */ public init(config: BulletLifecycleConfig, startPos: Vec3) { this.config = { ...config }; this.state = { elapsedTime: 0, hitCount: 0, ricochetLeft: config.ricochetCount, pierceLeft: config.penetration, travelDistance: 0, phase: 'active', shouldDestroy: false, startPosition: startPos.clone(), returnTimer: config.returnDelay || 0 }; this.lastPosition = this.node.worldPosition.clone(); } /** * 处理命中事件 */ public onHit(hitNode: Node): boolean { if (!this.config || !this.state) return true; this.state.hitCount++; switch (this.config.type) { case 'hit_destroy': return this.handleHitDestroy(); case 'range_limit': return this.handleRangeLimit(); case 'ricochet_counter': return this.handleRicochetCounter(); case 'ground_impact': case 'ground_impact_with_effect': return this.handleGroundImpact(hitNode); case 'return_trip': return this.handleReturnTrip(); case 'target_impact': return this.handleTargetImpact(hitNode); default: return true; // 默认销毁 } } /** * 处理命中即销毁逻辑 */ private handleHitDestroy(): boolean { this.state.shouldDestroy = true; return true; } /** * 处理射程限制逻辑 */ private handleRangeLimit(): boolean { // 穿透逻辑 if (this.state.pierceLeft > 0) { this.state.pierceLeft--; return false; // 不销毁,继续飞行 } else { this.state.shouldDestroy = true; return true; } } /** * 处理弹射计数逻辑 */ private handleRicochetCounter(): boolean { if (this.state.ricochetLeft > 0) { this.state.ricochetLeft--; // 触发弹射 const trajectory = this.getComponent(BulletTrajectory); if (trajectory) { // BulletTrajectory会处理方向改变 } return false; // 不销毁,继续弹射 } else { this.state.shouldDestroy = true; return true; } } /** * 处理地面撞击逻辑 */ private handleGroundImpact(hitNode: Node): boolean { const isGround = this.isGroundNode(hitNode); if (isGround) { // 进入效果阶段 this.state.phase = 'effect'; // 延迟销毁,等待效果结束 if (this.config.effectDuration && this.config.effectDuration > 0) { this.scheduleOnce(() => { this.state.shouldDestroy = true; }, this.config.effectDuration); } else { this.state.shouldDestroy = true; } return true; } else { // 延迟销毁,确保爆炸的 0.1 s 延迟能正常触发 this.scheduleOnce(() => { this.state.shouldDestroy = true; }, 0.2); // === 立即冻结子弹运动,避免命中后继续绕圈 === const trajectory = this.getComponent(BulletTrajectory); if (trajectory) { trajectory.enabled = false; // 停止后续 update } const rigidBody = this.getComponent(RigidBody2D); if (rigidBody) { rigidBody.linearVelocity = new Vec2(0, 0); rigidBody.angularVelocity = 0; } return true; } } /** * 处理回旋镖逻辑 */ private handleReturnTrip(): boolean { if (this.state.phase === 'active') { // 首次命中立即开始返程,不再依据pierceLeft this.startReturn(); return false; // 不销毁 } else if (this.state.phase === 'returning') { // 返回途中命中,仅造成伤害不销毁 return false; } return false; } /** * 处理目标撞击逻辑(导弹) */ private handleTargetImpact(hitNode: Node): boolean { const isEnemy = this.isEnemyNode(hitNode); if (isEnemy) { this.state.shouldDestroy = true; return true; } // 如果不是敌人,继续飞行(导弹不会被其他物体阻挡) return false; } /** * 判断是否为地面节点 */ private isGroundNode(node: Node): boolean { const name = node.name.toLowerCase(); return name.includes('ground') || name.includes('wall') || name.includes('地面') || name.includes('墙'); } /** * 判断是否为敌人节点 */ private isEnemyNode(node: Node): boolean { const name = node.name.toLowerCase(); return name.includes('enemy') || name.includes('敌人') || node.getComponent('EnemyInstance') !== null; } update(dt: number) { if (!this.config || !this.state) return; this.state.elapsedTime += dt; // 更新飞行距离 this.updateTravelDistance(); // 检查各种销毁条件 this.checkDestroyConditions(); // 处理特殊逻辑 this.updateSpecialLogic(dt); // 如果需要销毁,执行销毁 if (this.state.shouldDestroy) { this.destroyBullet(); } } /** * 更新飞行距离 */ private updateTravelDistance() { const currentPos = this.node.worldPosition; const distance = Vec3.distance(this.lastPosition, currentPos); this.state.travelDistance += distance; this.lastPosition.set(currentPos); } /** * 检查销毁条件 */ private checkDestroyConditions() { // 检查时间限制 if (this.state.elapsedTime >= this.config.maxLifetime) { this.state.shouldDestroy = true; return; } // === 射程限制逻辑优化 === if (this.config.maxRange && this.state.travelDistance >= this.config.maxRange) { if (this.config.type === 'range_limit') { this.state.shouldDestroy = true; } else if (this.config.type === 'return_trip') { // 回旋镖:首次超距时开始返回;返回途中不再因射程销毁 if (this.state.phase === 'active') { this.startReturn(); } } // 其他生命周期类型忽略射程限制 return; } // 检查越界 if (this.checkOutOfBounds()) { if (this.config.type === 'return_trip' && this.state.phase === 'active') { this.startReturn(); } else { this.state.shouldDestroy = true; } return; } } /** * 更新特殊逻辑 */ private updateSpecialLogic(dt: number) { switch (this.config.type) { case 'return_trip': this.updateReturnTrip(dt); break; } } /** * 更新回旋镖逻辑 */ private updateReturnTrip(dt: number) { if (this.state.phase === 'active' && this.state.returnTimer > 0) { this.state.returnTimer -= dt; if (this.state.returnTimer <= 0) { this.startReturn(); } } else if (this.state.phase === 'returning') { // 检查是否返回到原点 const distanceToOrigin = Vec3.distance(this.node.worldPosition, this.state.startPosition); if (distanceToOrigin <= 50) { // 50单位的容差 this.state.shouldDestroy = true; } } } /** * 开始返回 */ private startReturn() { this.state.phase = 'returning'; const trajectory = this.getComponent(BulletTrajectory); if (trajectory) { trajectory.setTargetPosition(this.state.startPosition); trajectory.reverseDirection(); } } /** * 检查越界 */ private checkOutOfBounds(): boolean { // 优先使用 GameArea 的可视区域(若存在) const gameArea = find('Canvas/GameLevelUI/GameArea'); let bounding = null; if (gameArea) { const tr = gameArea.getComponent(UITransform); if (tr) { bounding = tr.getBoundingBoxToWorld(); } } // fallback => Canvas 整体区域 if (!bounding) { const canvas = find('Canvas'); if (canvas) { const tr = canvas.getComponent(UITransform); if (tr) { bounding = tr.getBoundingBoxToWorld(); } } } // 若无法获取区域,则不做越界销毁 if (!bounding) return false; // 允许一定的 margin const margin = 300; // 扩大容差,防止大速度时瞬移出界 const pos = this.node.worldPosition; return pos.x < bounding.xMin - margin || pos.x > bounding.xMax + margin || pos.y < bounding.yMin - margin || pos.y > bounding.yMax + margin; } /** * 销毁子弹 */ private destroyBullet() { this.node.destroy(); } /** * 获取生命周期状态 */ public getState(): LifecycleState { return this.state; } /** * 检查是否应该销毁 */ public shouldDestroy(): boolean { return this.state ? this.state.shouldDestroy : true; } /** * 强制销毁 */ public forceDestroy() { this.state.shouldDestroy = true; } /** * 获取剩余生命时间 */ public getRemainingLifetime(): number { if (!this.config || !this.state) return 0; return Math.max(0, this.config.maxLifetime - this.state.elapsedTime); } /** * 验证配置 */ public static validateConfig(config: BulletLifecycleConfig): boolean { if (!config) return false; if (config.maxLifetime <= 0) return false; if (config.penetration < 0) return false; if (config.ricochetCount < 0) return false; if (config.maxRange && config.maxRange <= 0) return false; if (config.effectDuration && config.effectDuration < 0) return false; if (config.returnDelay && config.returnDelay < 0) return false; return true; } }