import { _decorator, Component, Node, Vec3, Vec2, find, UITransform, RigidBody2D } from 'cc'; import { BulletTrajectory } from './BulletTrajectory'; import { BulletLifecycleConfig } from '../../Core/ConfigManager'; const { ccclass, property } = _decorator; /** * 子弹生命周期控制器 * 负责管理子弹的生存时间和销毁条件 */ 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 = startPos.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 { console.log(`[BulletLifecycle] 弹射计数检查 - 剩余弹射次数: ${this.state.ricochetLeft}`); if (this.state.ricochetLeft > 0) { this.state.ricochetLeft--; console.log(`[BulletLifecycle] 弹射次数递减 - 剩余: ${this.state.ricochetLeft}`); // 弹射方向改变由BulletHitEffect处理,这里只管理生命周期 return false; // 不销毁,继续弹射 } else { console.log(`[BulletLifecycle] 弹射次数耗尽,标记销毁`); 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 { // === 立即冻结子弹运动,避免命中后继续绕圈 === 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; } // 立即销毁,因为爆炸已经立即发生 this.state.shouldDestroy = true; return true; } } /** * 处理回旋镖逻辑 */ private handleReturnTrip(): boolean { if (this.state.phase === 'active') { // 首次命中敌人立即开始返程 console.log(`[BulletLifecycle] 回旋镖命中敌人,开始返回`); this.startReturn(); return false; // 不销毁 } else if (this.state.phase === 'returning') { // 返回途中命中,仅造成伤害不销毁 console.log(`[BulletLifecycle] 回旋镖返回途中命中目标`); 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 { // 检查是否为EnemySprite子节点 if (node.name === 'EnemySprite' && node.parent) { return node.parent.getComponent('EnemyInstance') !== null; } // 兼容旧的敌人检测逻辑 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') { console.log(`[BulletLifecycle] 回旋镖达到最大射程 ${this.config.maxRange},开始返回`); this.startReturn(); } } // 其他生命周期类型忽略射程限制 return; } // 检查越界 const outOfBounds = this.checkOutOfBounds(); if (outOfBounds) { 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') { // 检查返回计时器(如果配置了延迟返回) if (this.state.returnTimer > 0) { this.state.returnTimer -= dt; if (this.state.returnTimer <= 0) { console.log(`[BulletLifecycle] 回旋镖延迟时间到,开始返回`); this.startReturn(); } } } else if (this.state.phase === 'returning') { // 检查是否返回到原点 const distanceToOrigin = Vec3.distance(this.node.worldPosition, this.state.startPosition); if (distanceToOrigin <= 80) { // 增加容差到80单位,确保能够回收 console.log(`[BulletLifecycle] 回旋镖返回到原点,销毁`); this.state.shouldDestroy = true; } } } /** * 开始返回 */ private startReturn() { if (this.state.phase === 'returning') { return; // 避免重复调用 } this.state.phase = 'returning'; console.log(`[BulletLifecycle] 回旋镖进入返回阶段`); const trajectory = this.getComponent(BulletTrajectory); if (trajectory) { // 设置返回目标为起始位置 trajectory.setTargetPosition(this.state.startPosition); // 对于弧线弹道,不需要反转方向,让它自然转向回家 // 只有非弧线弹道才需要反转方向 const config = (trajectory as any).config; if (config && config.type !== 'arc') { 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; const outOfBounds = pos.x < bounding.xMin - margin || pos.x > bounding.xMax + margin || pos.y < bounding.yMin - margin || pos.y > bounding.yMax + margin; return outOfBounds; } /** * 销毁子弹 */ 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; } }