Browse Source

回旋镖路径

181404010226 2 months ago
parent
commit
85a680615a

+ 2 - 1
assets/data/weapons.json

@@ -506,9 +506,10 @@
           "burstDelay": 0
         },
         "trajectory": {
-          "type": "homing",
+          "type": "arc",
           "speed": 15,
           "gravity": 0,
+          "rotateSpeed": 0.6,
           "homingStrength": 0.5,
           "homingDelay": 0.3
         },

+ 6 - 4
assets/scripts/CombatSystem/BallController.ts

@@ -1297,8 +1297,9 @@ export class BallController extends Component {
      * 创建并发射子弹 - 使用新的WeaponBullet系统
      * @param firePosition 发射位置(世界坐标)
      * @param weaponNode 武器节点(包含WeaponInfo组件)
+     * @param sourceBlock 发射源方块节点(用于回旋镖返回)
      */
-    private createAndFireBullet(firePosition: Vec3, weaponNode: Node | null) {
+    private createAndFireBullet(firePosition: Vec3, weaponNode: Node | null, sourceBlock?: Node) {
         
         // 确保武器配置加载
         WeaponBullet.loadWeaponsData().then(() => {
@@ -1376,7 +1377,8 @@ export class BallController extends Component {
                 autoTarget: true,
                 weaponConfig: finalConfig,
                 weaponInfo: weaponInfoComponent,
-                blockInfo: blockInfoComponent
+                blockInfo: blockInfoComponent,
+                sourceBlock: sourceBlock  // 传递发射源方块节点
             };
             
             // 验证初始化数据
@@ -2045,8 +2047,8 @@ export class BallController extends Component {
             console.warn(`[BallController] 方块 ${blockName} 中未找到武器节点`);
         }
         
-        // 传递武器节点给createAndFireBullet方法
-        this.createAndFireBullet(fireWorldPos, weaponNode);
+        // 传递武器节点和方块节点给createAndFireBullet方法
+        this.createAndFireBullet(fireWorldPos, weaponNode, blockNode);
     }
 
     /**

+ 14 - 8
assets/scripts/CombatSystem/BulletEffects/BulletLifecycle.ts

@@ -306,7 +306,7 @@ export class BulletLifecycle extends Component {
     }
     
     /**
-     * 开始返回
+     * 开始返回阶段 - 重新设计为直接弧线返回
      */
     private startReturn() {
         if (this.state.phase === 'returning') {
@@ -318,14 +318,20 @@ export class BulletLifecycle extends Component {
         
         const trajectory = this.getComponent(BulletTrajectory);
         if (trajectory) {
-            // 设置返回目标为起始位置
-            trajectory.setTargetPosition(this.state.startPosition);
+            // 获取当前位置作为新的发射点
+            const currentPos = this.node.worldPosition.clone();
             
-            // 对于弧线弹道,不需要反转方向,让它自然转向回家
-            // 只有非弧线弹道才需要反转方向
-            const config = (trajectory as any).config;
-            if (config && config.type !== 'arc') {
-                trajectory.reverseDirection();
+            // 获取方块的当前位置作为目标
+            const targetPos = trajectory.getDynamicReturnTarget();
+            
+            if (targetPos) {
+                // 重新初始化轨道:以当前位置为起点,方块位置为终点
+                trajectory.reinitializeForReturn(currentPos, targetPos);
+                console.log(`[BulletLifecycle] 回旋镖从 ${currentPos} 重新发射到 ${targetPos}`);
+            } else {
+                // 如果无法获取方块位置,使用起始位置作为备选
+                trajectory.setTargetPosition(this.state.startPosition);
+                console.log(`[BulletLifecycle] 回旋镖返回到起始位置 ${this.state.startPosition}`);
             }
         }
     }

+ 230 - 32
assets/scripts/CombatSystem/BulletEffects/BulletTrajectory.ts

@@ -14,6 +14,13 @@ export interface TrajectoryState {
     targetPosition?: Vec3;     // 目标位置(追踪用)
     elapsedTime: number;       // 经过时间
     phase: 'launch' | 'homing' | 'return';  // 运动阶段
+    // 路径记录相关
+    flightPath: Vec3[];        // 飞行路径记录
+    pathRecordInterval: number; // 路径记录间隔(毫秒)
+    lastRecordTime: number;    // 上次记录时间
+    returnPathIndex: number;   // 返回路径索引
+    // 发射源方块节点
+    sourceBlock?: Node;        // 发射源方块节点(用于动态获取返回位置)
 }
 
 @ccclass('BulletTrajectory')
@@ -31,7 +38,7 @@ export class BulletTrajectory extends Component {
     /**
      * 初始化弹道
      */
-    public init(config: BulletTrajectoryConfig, direction: Vec3, startPos: Vec3) {
+    public init(config: BulletTrajectoryConfig, direction: Vec3, startPos: Vec3, sourceBlock?: Node) {
         this.config = { ...config };
         this.rigidBody = this.getComponent(RigidBody2D);
         
@@ -45,7 +52,14 @@ export class BulletTrajectory extends Component {
             currentVelocity: direction.clone().multiplyScalar(config.speed),
             startPosition: startPos.clone(),
             elapsedTime: 0,
-            phase: 'launch'
+            phase: 'launch',
+            // 路径记录初始化
+            flightPath: [startPos.clone()],
+            pathRecordInterval: 50, // 每50毫秒记录一次位置
+            lastRecordTime: 0,
+            returnPathIndex: -1,
+            // 存储发射源方块节点
+            sourceBlock: sourceBlock
         };
         
         this.homingTimer = config.homingDelay;
@@ -203,6 +217,9 @@ export class BulletTrajectory extends Component {
         
         this.state.elapsedTime += dt;
         
+        // 记录路径(仅对回旋镖)
+        this.recordFlightPath(dt);
+        
         switch (this.config.type) {
             case 'straight':
                 this.updateStraightTrajectory(dt);
@@ -312,46 +329,82 @@ export class BulletTrajectory extends Component {
             this.findTarget();
         }
 
-        // 检查是否到达目标位置(仅用于西瓜炸弹等爆炸武器,回旋镖不适用
+        // 检查是否到达目标位置(爆炸武器和回旋镖都适用,但处理方式不同
         const lifecycle = this.getComponent('BulletLifecycle') as any;
         const isBoomerang = lifecycle && lifecycle.getState && lifecycle.getState().phase !== undefined;
         
-        if (this.state.targetPosition && !isBoomerang) {
+        if (this.state.targetPosition) {
             const currentPos = this.node.worldPosition;
             const distanceToTarget = Vec3.distance(currentPos, this.state.targetPosition);
             
-            // 如果到达目标位置附近(50像素内),触发爆炸
+            // 如果到达目标位置附近(50像素内)
             if (distanceToTarget <= 50) {
-                // 停止运动
-                if (this.rigidBody) {
-                    this.rigidBody.linearVelocity = new Vec2(0, 0);
-                }
-                
-                // 先触发命中效果(包括音效播放)
-                const hitEffect = this.getComponent('BulletHitEffect') as any;
-                if (hitEffect && typeof hitEffect.processHit === 'function') {
-                    // 使用当前位置作为命中位置,传入子弹节点作为命中目标
-                    hitEffect.processHit(this.node, currentPos);
-                }
-                
-                // 然后通知生命周期组件触发爆炸
-                if (lifecycle && typeof lifecycle.onHit === 'function') {
-                    lifecycle.onHit(this.node); // 触发爆炸逻辑
+                if (isBoomerang) {
+                    // 回旋镖:触发命中效果但不爆炸,开始返回
+                    const hitEffect = this.getComponent('BulletHitEffect') as any;
+                    if (hitEffect && typeof hitEffect.processHit === 'function') {
+                        // 对目标位置附近的敌人造成伤害
+                        const enemyContainer = find('Canvas/GameLevelUI/enemyContainer');
+                        if (enemyContainer) {
+                            const enemies = enemyContainer.children.filter(child => 
+                                child.active && this.isEnemyNode(child)
+                            );
+                            
+                            // 寻找目标位置附近的敌人
+                            for (const enemy of enemies) {
+                                const distanceToEnemy = Vec3.distance(currentPos, enemy.worldPosition);
+                                if (distanceToEnemy <= 50) {
+                                    hitEffect.processHit(enemy, currentPos);
+                                    break; // 只命中一个敌人
+                                }
+                            }
+                        }
+                    }
+                    
+                    // 通知生命周期组件处理命中(回旋镖会开始返回而不是销毁)
+                    if (lifecycle && typeof lifecycle.onHit === 'function') {
+                        lifecycle.onHit(this.targetNode || this.node);
+                    }
+                } else {
+                    // 爆炸武器:停止运动并触发爆炸
+                    if (this.rigidBody) {
+                        this.rigidBody.linearVelocity = new Vec2(0, 0);
+                    }
+                    
+                    // 先触发命中效果(包括音效播放)
+                    const hitEffect = this.getComponent('BulletHitEffect') as any;
+                    if (hitEffect && typeof hitEffect.processHit === 'function') {
+                        // 使用当前位置作为命中位置,传入子弹节点作为命中目标
+                        hitEffect.processHit(this.node, currentPos);
+                    }
+                    
+                    // 然后通知生命周期组件触发爆炸
+                    if (lifecycle && typeof lifecycle.onHit === 'function') {
+                        lifecycle.onHit(this.node); // 触发爆炸逻辑
+                    }
+                    return;
                 }
-                return;
             }
         }
 
         // 根据回旋镖状态决定目标方向
         if (isBoomerang && lifecycle.getState().phase === 'returning') {
-            // 返回阶段:朝向起始位置
-             const pos = this.node.worldPosition;
-             const startPos = this.state.startPosition || pos; // 防止空指针
-             const dirToHome = startPos.clone().subtract(pos).normalize();
-             this.arcTargetDir.set(dirToHome);
-             
-             // 更新目标位置为起始位置
-             this.state.targetPosition = startPos.clone();
+            // 返回阶段:直接朝向目标位置(方块当前位置)
+            if (this.state.targetPosition) {
+                const pos = this.node.worldPosition;
+                const dirToTarget = this.state.targetPosition.clone().subtract(pos).normalize();
+                this.arcTargetDir.set(dirToTarget);
+                
+                // 检查是否到达目标位置
+                const distanceToTarget = Vec3.distance(pos, this.state.targetPosition);
+                if (distanceToTarget <= 30) {
+                    // 到达目标,销毁子弹
+                    if (lifecycle && typeof lifecycle.destroy === 'function') {
+                        lifecycle.destroy();
+                    }
+                    return;
+                }
+            }
         } else {
             // 主动追踪阶段:朝向敌人
             if (this.targetNode && this.targetNode.isValid) {
@@ -389,8 +442,21 @@ export class BulletTrajectory extends Component {
         let rotateFactor = (this.config.rotateSpeed ?? 0.5) * dt;
         
         if (isBoomerang && lifecycle.getState().phase === 'returning') {
-            // 回旋镖返回阶段:使用固定的转向速率,确保平滑返回
-            rotateFactor *= 1.5; // 返回时适中的转向速率
+            // 回旋镖返回阶段:根据距离调整转向速率,确保平滑返回
+            if (this.state.targetPosition) {
+                const distanceToTarget = Vec3.distance(this.node.worldPosition, this.state.targetPosition);
+                
+                // 根据距离动态调整转向速率,距离越近转向越快
+                if (distanceToTarget < 50) {
+                    rotateFactor *= 4.0; // 非常接近目标点时快速转向
+                } else if (distanceToTarget < 100) {
+                    rotateFactor *= 2.5; // 接近目标点时加快转向
+                } else if (distanceToTarget < 200) {
+                    rotateFactor *= 1.8; // 中等距离时适度加快转向
+                } else {
+                    rotateFactor *= 1.2; // 远距离时使用基础转向速率
+                }
+            }
         } else if (this.targetNode && this.targetNode.isValid) {
             // 主动追踪阶段:根据距离调整转向速率
             const distance = Vec3.distance(this.node.worldPosition, this.targetNode.worldPosition);
@@ -477,12 +543,144 @@ export class BulletTrajectory extends Component {
         this.config.gravity = gravity;
     }
     
+    /**
+     * 记录飞行路径(仅对回旋镖)
+     */
+    private recordFlightPath(dt: number) {
+        // 检查是否为回旋镖
+        const lifecycle = this.getComponent('BulletLifecycle') as any;
+        const isBoomerang = lifecycle && lifecycle.getState && lifecycle.getState().phase !== undefined;
+        
+        if (!isBoomerang) return;
+        
+        // 只在非返回阶段记录路径
+        if (lifecycle.getState().phase === 'returning') return;
+        
+        // 检查是否到了记录时间
+        this.state.lastRecordTime += dt * 1000; // 转换为毫秒
+        if (this.state.lastRecordTime >= this.state.pathRecordInterval) {
+            const currentPos = this.node.worldPosition.clone();
+            
+            // 避免记录重复位置
+            const lastPos = this.state.flightPath[this.state.flightPath.length - 1];
+            const distance = Vec3.distance(currentPos, lastPos);
+            
+            if (distance > 10) { // 只有移动距离超过10像素才记录
+                this.state.flightPath.push(currentPos);
+                this.state.lastRecordTime = 0;
+            }
+        }
+    }
+    
+    /**
+      * 获取返回路径中的下一个目标点
+      */
+     private getNextReturnTarget(): Vec3 | null {
+         if (this.state.flightPath.length === 0) return null;
+         
+         // 如果还没有开始返回路径,初始化索引
+         if (this.state.returnPathIndex === -1) {
+             this.state.returnPathIndex = this.state.flightPath.length - 1;
+         }
+         
+         // 检查当前位置是否接近目标点
+         if (this.state.returnPathIndex >= 0) {
+             const targetPos = this.state.flightPath[this.state.returnPathIndex];
+             const currentPos = this.node.worldPosition;
+             const distance = Vec3.distance(currentPos, targetPos);
+             
+             // 动态调整接近距离:路径点越少,接近距离越大,避免过于严格的路径跟随
+             const approachDistance = Math.max(25, Math.min(50, 200 / this.state.flightPath.length));
+             
+             // 如果接近当前目标点,移动到下一个点
+             if (distance <= approachDistance) {
+                 this.state.returnPathIndex--;
+             }
+             
+             // 返回当前目标点,如果有多个路径点则进行平滑插值
+             if (this.state.returnPathIndex >= 0) {
+                 const currentTarget = this.state.flightPath[this.state.returnPathIndex];
+                 
+                 // 如果还有下一个路径点,进行平滑插值
+                 if (this.state.returnPathIndex > 0) {
+                     const nextTarget = this.state.flightPath[this.state.returnPathIndex - 1];
+                     const progress = Math.min(1, (approachDistance - distance) / approachDistance);
+                     
+                     if (progress > 0) {
+                         // 在当前目标点和下一个目标点之间进行插值
+                         const smoothTarget = new Vec3();
+                         Vec3.lerp(smoothTarget, currentTarget, nextTarget, progress * 0.3);
+                         return smoothTarget;
+                     }
+                 }
+                 
+                 return currentTarget;
+             }
+         }
+         
+         // 如果已经遍历完所有路径点,返回发射源方块的当前位置
+         if (this.state.sourceBlock && this.state.sourceBlock.isValid) {
+             // 动态获取方块的当前世界位置
+             return this.state.sourceBlock.worldPosition.clone();
+         }
+         
+         // 如果方块节点无效,回退到起始位置
+         return this.state.startPosition;
+     }
+    
+    /**
+     * 获取动态返回目标位置(方块当前位置)
+     */
+    public getDynamicReturnTarget(): Vec3 | null {
+        if (this.state.sourceBlock && this.state.sourceBlock.isValid) {
+            return this.state.sourceBlock.worldPosition.clone();
+        }
+        return null;
+    }
+
+    /**
+     * 为返回阶段重新初始化轨道
+     * @param currentPos 当前位置(新的发射点)
+     * @param targetPos 目标位置(方块位置)
+     */
+    public reinitializeForReturn(currentPos: Vec3, targetPos: Vec3) {
+        if (!this.config || !this.state) return;
+        
+        // 清除当前所有速度和运动
+        if (this.rigidBody) {
+            this.rigidBody.linearVelocity = new Vec2(0, 0);
+            this.rigidBody.angularVelocity = 0;
+        }
+        
+        // 计算从当前位置到目标位置的方向
+        const direction = targetPos.clone().subtract(currentPos).normalize();
+        
+        // 重新设置状态
+        this.state.startPosition = currentPos.clone();
+        this.state.targetPosition = targetPos.clone();
+        this.state.initialVelocity = direction.clone().multiplyScalar(this.config.speed);
+        this.state.currentVelocity = direction.clone().multiplyScalar(this.config.speed);
+        this.state.elapsedTime = 0;
+        
+        // 重新初始化弧线轨道参数
+        if (this.config.type === 'arc') {
+            this.arcDir = direction.clone();
+            this.arcTargetDir = direction.clone();
+        }
+        
+        // 清除路径记录,开始新的返回轨道
+        this.state.flightPath = [currentPos.clone()];
+        this.state.returnPathIndex = -1;
+        this.state.lastRecordTime = 0;
+        
+        console.log(`[BulletTrajectory] 重新初始化返回轨道: ${currentPos} -> ${targetPos}`);
+    }
+
     /**
      * 验证配置
      */
     public static validateConfig(config: BulletTrajectoryConfig): boolean {
         if (!config) return false;
-        
         if (config.speed <= 0) return false;
         if (config.gravity < 0) return false;
         if (config.homingStrength < 0 || config.homingStrength > 1) return false;

+ 6 - 2
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -36,6 +36,7 @@ export interface BulletInitData {
     weaponConfig?: WeaponConfig; // 直接传入的武器配置(优先级更高)
     weaponInfo?: WeaponInfo;    // WeaponInfo组件实例(用于冷却管理等)
     blockInfo?: BlockInfo;      // BlockInfo组件实例(用于获取稀有度等级)
+    sourceBlock?: Node;         // 发射源方块节点(用于回旋镖返回)
 }
 
 @ccclass('WeaponBullet')
@@ -57,6 +58,7 @@ export class WeaponBullet extends Component {
     private weaponId: string = null; // 存储武器ID用于获取升级数据
     private weaponInfo: WeaponInfo = null; // WeaponInfo组件实例
     private blockInfo: BlockInfo = null; // BlockInfo组件实例
+    private sourceBlock: Node = null; // 发射源方块节点(用于回旋镖返回)
     private isInitialized: boolean = false;
     
     // === 静态武器配置缓存 ===
@@ -238,10 +240,11 @@ export class WeaponBullet extends Component {
             return;
         }
         
-        // 存储武器ID、WeaponInfo和BlockInfo
+        // 存储武器ID、WeaponInfo、BlockInfo和SourceBlock
         this.weaponId = initData.weaponId;
         this.weaponInfo = initData.weaponInfo || null;
         this.blockInfo = initData.blockInfo || null;
+        this.sourceBlock = initData.sourceBlock || null;
         
         // 获取武器配置
         this.weaponConfig = initData.weaponConfig || WeaponBullet.getWeaponConfig(initData.weaponId);
@@ -324,7 +327,8 @@ export class WeaponBullet extends Component {
         this.bulletTrajectory.init(
             trajCfg,
             direction,
-            initData.firePosition
+            initData.firePosition,
+            this.sourceBlock  // 传递发射源方块节点
         );
         
         // --- 额外偏移 ---