181404010226 пре 5 месеци
родитељ
комит
937f3025e8

+ 3 - 3
assets/Scenes/GameLevel.scene

@@ -115,7 +115,7 @@
     "_lpos": {
       "__type__": "cc.Vec3",
       "x": 360,
-      "y": 667.0000000000001,
+      "y": 667,
       "z": 0
     },
     "_lrot": {
@@ -306,7 +306,7 @@
     "_lpos": {
       "__type__": "cc.Vec3",
       "x": 0,
-      "y": 1.1368683772161603e-13,
+      "y": 0,
       "z": 0
     },
     "_lrot": {
@@ -42924,7 +42924,7 @@
     "_contentSize": {
       "__type__": "cc.Size",
       "width": 720,
-      "height": 1334.0000000000002
+      "height": 1334
     },
     "_anchorPoint": {
       "__type__": "cc.Vec2",

Разлика између датотеке није приказан због своје велике величине
+ 833 - 166
assets/Scenes/TestScene.scene


+ 44 - 13
assets/assets/Prefabs/WeaponBlock.prefab

@@ -24,9 +24,6 @@
     ],
     "_active": true,
     "_components": [
-      {
-        "__id__": 16
-      },
       {
         "__id__": 18
       },
@@ -35,10 +32,13 @@
       },
       {
         "__id__": 22
+      },
+      {
+        "__id__": 24
       }
     ],
     "_prefab": {
-      "__id__": 24
+      "__id__": 26
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -88,11 +88,11 @@
     "_active": true,
     "_components": [
       {
-        "__id__": 13
+        "__id__": 15
       }
     ],
     "_prefab": {
-      "__id__": 15
+      "__id__": 17
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -227,10 +227,13 @@
       },
       {
         "__id__": 10
+      },
+      {
+        "__id__": 12
       }
     ],
     "_prefab": {
-      "__id__": 12
+      "__id__": 14
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -311,7 +314,10 @@
       "b": 255,
       "a": 255
     },
-    "_spriteFrame": null,
+    "_spriteFrame": {
+      "__uuid__": "7efa8afb-730c-43f8-9d0a-5cbf71082a54@f9941",
+      "__expectedType__": "cc.SpriteFrame"
+    },
     "_type": 0,
     "_fillType": 0,
     "_sizeMode": 1,
@@ -331,6 +337,31 @@
     "__type__": "cc.CompPrefabInfo",
     "fileId": "8cVih+ArJFb5TbjLHK7rfX"
   },
+  {
+    "__type__": "1325eBeZRVH5KbFuC2JSfeY",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 7
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 13
+    },
+    "bulletPrefab": {
+      "__uuid__": "54638943-4461-43b1-b351-a0380e1f30ce",
+      "__expectedType__": "cc.Prefab"
+    },
+    "fireInterval": 0.5,
+    "bulletSpeed": 800,
+    "rotateSpeed": 0.5,
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "d1XitZJDhCxodHoe0dv//w"
+  },
   {
     "__type__": "cc.PrefabInfo",
     "root": {
@@ -354,7 +385,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 14
+      "__id__": 16
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -395,7 +426,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 17
+      "__id__": 19
     },
     "_contentSize": {
       "__type__": "cc.Size",
@@ -423,7 +454,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 19
+      "__id__": 21
     },
     "_customMaterial": null,
     "_srcBlendFactor": 2,
@@ -468,7 +499,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 21
+      "__id__": 23
     },
     "tag": 2,
     "_group": 4,
@@ -539,7 +570,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 23
+      "__id__": 25
     },
     "enabledContactListener": true,
     "bullet": false,

+ 2 - 1
assets/resources/data/weapons.json

@@ -220,9 +220,10 @@
           "burstDelay": 0
         },
         "trajectory": {
-          "type": "straight",
+          "type": "arc",
           "speed": 20,
           "gravity": 0,
+          "rotateSpeed": 0.5,
           "homingStrength": 0,
           "homingDelay": 0
         },

+ 7 - 3
assets/scripts/CombatSystem/BulletEffects/BulletLifecycle.ts

@@ -164,11 +164,15 @@ export class BulletLifecycle extends Component {
                 this.state.shouldDestroy = true;
             }
             
+            return true;
+        } else {
+            console.log('💥 ground_impact 命中非地面,准备销毁');
+            // 延迟销毁,确保爆炸延迟(如0.1s)能正常触发
+            this.scheduleOnce(() => {
+                this.state.shouldDestroy = true;
+            }, 0.2);
             return true;
         }
-        
-        // 如果不是地面,按穿透逻辑处理
-        return this.handleRangeLimit();
     }
     
     /**

+ 92 - 9
assets/scripts/CombatSystem/BulletEffects/BulletTrajectory.ts

@@ -7,11 +7,15 @@ const { ccclass, property } = _decorator;
  */
 
 export interface BulletTrajectoryConfig {
-    type: 'straight' | 'parabolic' | 'homing';  // 弹道类型
-    speed: number;                              // 初始速度
-    gravity: number;                            // 重力影响
-    homingStrength: number;                     // 追踪强度 (0-1)
-    homingDelay: number;                        // 追踪延迟(秒)
+    type: 'straight' | 'parabolic' | 'homing' | 'arc';  // 弹道类型
+    speed: number;                                      // 初始速度
+    gravity: number;                                    // 重力影响 (arc/straight 可为0)
+    homingStrength: number;                             // 追踪强度 (0-1)
+    homingDelay: number;                                // 追踪延迟(秒)
+    /**
+     * 旋转速率 (0~1)。仅在 arc 弹道中使用,值越大转向越快。
+     */
+    rotateSpeed?: number;
 }
 
 export interface TrajectoryState {
@@ -31,6 +35,10 @@ export class BulletTrajectory extends Component {
     private targetNode: Node = null;
     private homingTimer: number = 0;
     
+    // === Arc Trajectory ===
+    private arcDir: Vec3 = null;          // 当前方向
+    private arcTargetDir: Vec3 = null;    // 目标方向(最终方向)
+    
     /**
      * 初始化弹道
      */
@@ -38,8 +46,8 @@ export class BulletTrajectory extends Component {
         this.config = { ...config };
         this.rigidBody = this.getComponent(RigidBody2D);
         
-        if (!this.rigidBody) {
-            console.error('BulletTrajectory: 需要RigidBody2D组件');
+        if (!this.rigidBody && config.type !== 'arc') {
+            console.error('BulletTrajectory: 需要 RigidBody2D 组件 (除 arc 弹道外)');
             return;
         }
         
@@ -90,6 +98,30 @@ export class BulletTrajectory extends Component {
                     this.state.initialVelocity.y
                 );
                 break;
+                
+            case 'arc':
+                // 计算带 45° 随机偏移的初始方向
+                const baseDir = this.state.initialVelocity.clone().normalize();
+                const sign = Math.random() < 0.5 ? 1 : -1;
+                const rad = 45 * Math.PI / 180 * sign;
+                const cos = Math.cos(rad);
+                const sin = Math.sin(rad);
+                const offsetDir = new Vec3(
+                    baseDir.x * cos - baseDir.y * sin,
+                    baseDir.x * sin + baseDir.y * cos,
+                    0
+                ).normalize();
+
+                this.arcDir = offsetDir;
+                this.arcTargetDir = baseDir;
+
+                // 如果有物理组件,则设置线速度;否则后续 updateArcTrajectory 将直接操作位移
+                if (this.rigidBody) {
+                    this.rigidBody.linearVelocity = new Vec2(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed);
+                }
+                // 保存当前速度
+                this.state.currentVelocity.set(offsetDir.x * this.config.speed, offsetDir.y * this.config.speed, 0);
+                break;
         }
     }
     
@@ -176,7 +208,7 @@ export class BulletTrajectory extends Component {
     }
     
     update(dt: number) {
-        if (!this.config || !this.state || !this.rigidBody) return;
+        if (!this.config || !this.state) return;
         
         this.state.elapsedTime += dt;
         
@@ -190,6 +222,9 @@ export class BulletTrajectory extends Component {
             case 'homing':
                 this.updateHomingTrajectory(dt);
                 break;
+            case 'arc':
+                this.updateArcTrajectory(dt);
+                break;
         }
     }
     
@@ -274,11 +309,58 @@ export class BulletTrajectory extends Component {
         this.state.currentVelocity.set(newVelocity);
     }
     
+    /**
+     * 更新弧线弹道
+     */
+    private updateArcTrajectory(dt: number) {
+        if (!this.arcDir || !this.arcTargetDir) return;
+
+        // === 动态追踪目标 ===
+        // 若当前没有有效目标,则尝试重新寻找
+        if (!this.targetNode || !this.targetNode.isValid) {
+            this.findTarget();
+        }
+
+        // 每帧计算目标方向,使得弹道持续朝向敌人
+        if (this.targetNode && this.targetNode.isValid) {
+            const pos = this.node.worldPosition;
+            const dirToTarget = this.targetNode.worldPosition.clone().subtract(pos).normalize();
+            this.arcTargetDir.set(dirToTarget);
+        }
+
+        // 根据与目标距离动态调整转向速率:越近转得越快,避免打圈
+        let rotateFactor = (this.config.rotateSpeed ?? 0.5) * dt;
+        if (this.targetNode && this.targetNode.isValid) {
+            const distance = Vec3.distance(this.node.worldPosition, this.targetNode.worldPosition);
+            rotateFactor *= (2000 / Math.max(distance, 50));
+        }
+
+        const newDir = new Vec3();
+        Vec3.slerp(newDir, this.arcDir, this.arcTargetDir, Math.min(1, rotateFactor));
+        this.arcDir.set(newDir);
+
+        // 更新位移 / 速度
+        if (this.rigidBody) {
+            this.rigidBody.linearVelocity = new Vec2(this.arcDir.x * this.config.speed, this.arcDir.y * this.config.speed);
+        } else {
+            const displacement = this.arcDir.clone().multiplyScalar(this.config.speed * dt);
+            this.node.worldPosition = this.node.worldPosition.add(displacement);
+        }
+
+        // 更新状态中的当前速度
+        this.state.currentVelocity.set(this.arcDir.x * this.config.speed, this.arcDir.y * this.config.speed, 0);
+    }
+    
     /**
      * 获取当前速度
      */
     public getCurrentVelocity(): Vec3 {
-        const vel = this.rigidBody.linearVelocity;
+        if (this.config?.type === 'arc') {
+            return this.arcDir ? this.arcDir.clone().multiplyScalar(this.config.speed) : new Vec3();
+        }
+
+        const vel = this.rigidBody?.linearVelocity;
+        if (!vel) return new Vec3();
         return new Vec3(vel.x, vel.y, 0);
     }
     
@@ -346,6 +428,7 @@ export class BulletTrajectory extends Component {
         if (config.gravity < 0) return false;
         if (config.homingStrength < 0 || config.homingStrength > 1) return false;
         if (config.homingDelay < 0) return false;
+        if (config.rotateSpeed !== undefined && (config.rotateSpeed < 0 || config.rotateSpeed > 1)) return false;
         
         return true;
     }

+ 9 - 0
assets/scripts/test.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "934a5014-666a-4731-ac09-e3ad814f6f00",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 86 - 0
assets/scripts/test/ArcBullet.ts

@@ -0,0 +1,86 @@
+import { _decorator, Component, Node, Vec3, v3 } from 'cc';
+const { ccclass, property } = _decorator;
+
+/**
+ * ArcBullet
+ * 负责控制子弹沿弧线(曲线)飞行
+ * 思路:
+ * 1. 初始化时给子弹一个带有随机左右偏移的初始方向(45°)。
+ * 2. 在 update 中使用 Vec3.slerp 将当前方向逐步朝向目标方向插值,形成弧线效果。
+ * 3. 用方向 * 速度 * dt 更新 worldPosition 即可。
+ */
+@ccclass('ArcBullet')
+export class ArcBullet extends Component {
+    /** 子弹飞行速度(像素/秒) */
+    @property
+    public moveSpeed: number = 800;
+
+    /** 转向速率(0~1 越大越快) */
+    @property
+    public rotateSpeed: number = 0.5;
+
+    /** 追踪目标(可选)。不设置则默认向屏幕上方飞行。 */
+    @property(Node)
+    public target: Node | null = null;
+
+    // 当前方向
+    private _dir: Vec3 = new Vec3();
+
+    start() {
+        const pos = this.node.worldPosition;
+
+        // 计算目标点
+        let targetPos: Vec3;
+        if (this.target && this.target.isValid) {
+            targetPos = this.target.worldPosition.clone();
+        } else {
+            // 没有目标时,默认向上方 1200 像素处飞行
+            targetPos = v3(pos.x, pos.y + 1200, pos.z);
+        }
+
+        // 朝向目标的单位向量
+        const dirToTarget = targetPos.subtract(pos).normalize();
+
+        // 在初始方向基础上增加 45° 随机左右偏转,形成弧线起始
+        const sign = Math.random() < 0.5 ? 1 : -1; // +1 向左,-1 向右
+        const rad = 45 * Math.PI / 180 * sign;
+        const cos = Math.cos(rad);
+        const sin = Math.sin(rad);
+
+        const offsetDir = new Vec3(
+            dirToTarget.x * cos - dirToTarget.y * sin,
+            dirToTarget.x * sin + dirToTarget.y * cos,
+            0
+        ).normalize();
+
+        this._dir.set(offsetDir);
+    }
+
+    update(dt: number) {
+        if (dt === 0) return;
+
+        // 如果有有效目标,则逐步朝向目标
+        if (this.target && this.target.isValid) {
+            const pos = this.node.worldPosition;
+            const dirToTarget = this.target.worldPosition.clone().subtract(pos).normalize();
+
+            // 根据距离动态调整转向速率:越近转得越快,避免近距离打圈
+            const distance = Vec3.distance(pos, this.target.worldPosition);
+            // 基础转向速率(可调节 rotateSpeed),距离越小,factor 越大
+            const factor = this.rotateSpeed * dt * (2000 / Math.max(distance, 50));
+
+            const newDir = new Vec3();
+            Vec3.slerp(newDir, this._dir, dirToTarget, Math.min(1, factor));
+            this._dir.set(newDir);
+        }
+
+        // 位移更新
+        const displacement = this._dir.clone().multiplyScalar(this.moveSpeed * dt);
+        this.node.worldPosition = this.node.worldPosition.add(displacement);
+
+        // 超出屏幕范围时自动销毁(简易处理)
+        if (Math.abs(this.node.worldPosition.x) > 4000 || Math.abs(this.node.worldPosition.y) > 4000) {
+            this.node.destroy();
+        }
+    }
+} 

+ 9 - 0
assets/scripts/test/ArcBullet.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "8af2e872-503d-4978-aeab-7c4725209721",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 68 - 0
assets/scripts/test/AutoFireWeapon.ts

@@ -0,0 +1,68 @@
+import { _decorator, Component, Prefab, instantiate, Node, Vec3, macro, find } from 'cc';
+import { ArcBullet } from './ArcBullet';
+const { ccclass, property } = _decorator;
+
+/**
+ * AutoFireWeapon
+ * 绑定在武器(Block001)节点上的脚本,负责定时发射子弹。
+ */
+@ccclass('AutoFireWeapon')
+export class AutoFireWeapon extends Component {
+    /** 将 Pellet 预制体拖到此处 */
+    @property(Prefab)
+    public bulletPrefab: Prefab | null = null;
+
+    /** 射速:两发子弹之间的间隔(秒) */
+    @property
+    public fireInterval: number = 0.5;
+
+    /** 子弹速度(像素/秒) */
+    @property
+    public bulletSpeed: number = 800;
+
+    /** 子弹转向速率(0~1) */
+    @property
+    public rotateSpeed: number = 0.5;
+
+    /** 目标敌人(可选)。不设置则在 Canvas 下查找名为 Enemy 的节点 */
+    @property({ type: Node, tooltip: '敌人节点(如果为空,则自动在 Canvas 下查找名为 Enemy 的节点)' })
+    public enemyNode: Node | null = null;
+
+    onEnable() {
+        // 若未指定敌人,尝试在场景中查找
+        if (!this.enemyNode) {
+            this.enemyNode = find('Canvas/Enemy');
+        }
+        // 立即发射一枚,然后按间隔循环
+        this.schedule(this.spawnBullet.bind(this), this.fireInterval, macro.REPEAT_FOREVER, 0);
+    }
+
+    onDisable() {
+        this.unscheduleAllCallbacks();
+    }
+
+    private spawnBullet() {
+        if (!this.bulletPrefab) {
+            console.warn('[AutoFireWeapon] 未设置 bulletPrefab');
+            return;
+        }
+
+        // 实例化子弹
+        const bullet = instantiate(this.bulletPrefab);
+        if (!bullet) return;
+
+        // 将子弹添加到场景
+        this.node.parent?.addChild(bullet);
+
+        // 将子弹世界位置设置为武器当前位置
+        const worldPos = this.node.worldPosition;
+        bullet.setWorldPosition(new Vec3(worldPos.x, worldPos.y, worldPos.z));
+
+        // 配置 ArcBullet 组件
+        const arcBullet = bullet.getComponent(ArcBullet) || bullet.addComponent(ArcBullet);
+        arcBullet.moveSpeed = this.bulletSpeed;
+        arcBullet.rotateSpeed = this.rotateSpeed;
+        arcBullet.target = this.enemyNode;
+        // 可根据需要设置 arcBullet.target = 某个目标节点
+    }
+} 

+ 9 - 0
assets/scripts/test/AutoFireWeapon.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "1325e05e-6515-47e4-a6c5-b82d8949f798",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

Неке датотеке нису приказане због велике количине промена