181404010226 hai 5 meses
pai
achega
11eadd1a26

+ 3 - 28
assets/assets/Prefabs/Pellet.prefab

@@ -31,13 +31,10 @@
       },
       {
         "__id__": 8
-      },
-      {
-        "__id__": 10
       }
     ],
     "_prefab": {
-      "__id__": 12
+      "__id__": 10
     },
     "_lpos": {
       "__type__": "cc.Vec3",
@@ -141,28 +138,6 @@
     "__type__": "cc.CompPrefabInfo",
     "fileId": "a58czs9GZLJp7XWpINUriv"
   },
-  {
-    "__type__": "3bb4bsR7i5BR4vFW/LFJVbl",
-    "_name": "",
-    "_objFlags": 0,
-    "__editorExtras__": {},
-    "node": {
-      "__id__": 1
-    },
-    "_enabled": true,
-    "__prefab": {
-      "__id__": 7
-    },
-    "speed": 300,
-    "damage": 1,
-    "lifetime": 5,
-    "hitEffectPath": "",
-    "_id": ""
-  },
-  {
-    "__type__": "cc.CompPrefabInfo",
-    "fileId": "eeE26q02xN9apy23DVkrCR"
-  },
   {
     "__type__": "cc.RigidBody2D",
     "_name": "",
@@ -173,7 +148,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 9
+      "__id__": 7
     },
     "enabledContactListener": true,
     "bullet": true,
@@ -207,7 +182,7 @@
     },
     "_enabled": true,
     "__prefab": {
-      "__id__": 11
+      "__id__": 9
     },
     "tag": 3,
     "_group": 8,

+ 0 - 9
assets/resources/assets.meta

@@ -1,9 +0,0 @@
-{
-  "ver": "1.2.0",
-  "importer": "directory",
-  "imported": true,
-  "uuid": "5f92fef7-3dff-4d96-8c4d-38704d67c9cb",
-  "files": [],
-  "subMetas": {},
-  "userData": {}
-}

+ 28 - 38
assets/scripts/CombatSystem/BallController.ts

@@ -1,5 +1,4 @@
 import { _decorator, Component, Node, Vec2, Vec3, UITransform, Collider2D, Contact2DType, IPhysics2DContact, RigidBody2D, Prefab, instantiate, find, CircleCollider2D } from 'cc';
-import { BulletController } from './BulletController';
 import { PhysicsManager } from '../Core/PhysicsManager';
 import { WeaponConfig } from '../Core/ConfigManager';
 import { WeaponBullet } from './WeaponBullet';
@@ -651,11 +650,12 @@ export class BallController extends Component {
     }
     
     /**
-     * 创建并发射子弹 - 安全版本
+     * 创建并发射子弹 - 使用WeaponBullet系统
      * @param firePosition 发射位置(世界坐标)
+     * @param weaponConfig 武器配置
      */
     private createAndFireBullet(firePosition: Vec3, weaponConfig: WeaponConfig | null) {
-        console.log('🔫 === 开始创建子弹(安全模式)===');
+        console.log('🔫 === 开始创建子弹(WeaponBullet系统)===');
         
         // 创建子弹实例
         const bullet = instantiate(this.bulletPrefab);
@@ -664,14 +664,6 @@ export class BallController extends Component {
             return;
         }
 
-        // 获取子弹控制器并设置发射位置
-        const bulletController = bullet.getComponent(BulletController);
-        if (!bulletController) {
-            console.error('❌ 子弹预制体缺少BulletController组件');
-            bullet.destroy();
-            return;
-        }
-
         // 查找GameArea
         const gameArea = find('Canvas/GameLevelUI/GameArea');
         if (!gameArea) {
@@ -699,16 +691,32 @@ export class BallController extends Component {
         bullet.position = localPos;
         gameArea.addChild(bullet);
 
-        // 设定发射位置供 BulletController 使用
-        bulletController.setFirePosition(firePosition);
-
-        // 设置击中特效路径
-        if (weaponConfig && (bulletController as any).setEffectPaths) {
-            const hitEff = weaponConfig.bulletConfig?.hitEffect || '';
-            const trailEff = weaponConfig.bulletConfig?.trailEffect || undefined;
-            (bulletController as any).setEffectPaths(hitEff, trailEff);
+        // 添加或获取WeaponBullet组件
+        let weaponBullet = bullet.getComponent(WeaponBullet);
+        if (!weaponBullet) {
+            weaponBullet = bullet.addComponent(WeaponBullet);
         }
 
+        // 初始化WeaponBullet配置
+        const bulletConfig = weaponConfig?.bulletConfig || {};
+        weaponBullet.init({
+            behavior: ((bulletConfig as any).bulletType as any) || 'explosive',
+            speed: weaponConfig?.stats?.bulletSpeed || 300,
+            damage: weaponConfig?.stats?.damage || 1,
+            hitEffect: (bulletConfig as any).hitEffect || '',
+            trailEffect: (bulletConfig as any).trailEffect || '',
+            lifetime: 5,
+            ricochetCount: (bulletConfig as any).ricochetCount || 0,
+            ricochetAngle: (bulletConfig as any).ricochetAngle || 30,
+            arcHeight: (bulletConfig as any).arcHeight || 0,
+            returnDelay: (bulletConfig as any).returnDelay || 1,
+            burnDuration: weaponConfig?.stats?.burnDuration || 5,
+            homingDelay: (bulletConfig as any).homingDelay || 0.3,
+            homingStrength: weaponConfig?.stats?.homingStrength || 0.8,
+            firePosition: firePosition,
+            autoTarget: true
+        });
+
         // 延迟启用物理组件,确保 Box2D world 已就绪,避免 m_world 报错
         this.scheduleOnce(() => {
             if (!bullet || !bullet.isValid) return;
@@ -722,25 +730,7 @@ export class BallController extends Component {
         }, 0.05);
 
         console.log('✅ 子弹生成完毕并将在 0.05s 后激活物理组件');
-        console.log('�� === 子弹创建完成 ===');
-
-        // 添加WeaponBullet组件并初始化
-        const wb = bullet.getComponent(WeaponBullet) || bullet.addComponent(WeaponBullet);
-        const bCfg = weaponConfig.bulletConfig;          // weaponConfig 来自 ConfigManager
-        wb.init({
-            behavior: bCfg.bulletType as any,              // explosive / piercing / ricochet …
-            speed:    weaponConfig.stats.bulletSpeed,
-            damage:   weaponConfig.stats.damage,
-            hitEffect: bCfg.hitEffect,
-            trailEffect: bCfg.trailEffect,
-            ricochetCount: bCfg.ricochetCount,
-            ricochetAngle: bCfg.ricochetAngle,
-            arcHeight: bCfg.arcHeight,
-            returnDelay: bCfg.returnDelay,
-            burnDuration: weaponConfig.stats.burnDuration,
-            homingDelay: bCfg.homingDelay,
-            homingStrength: weaponConfig.stats.homingStrength
-        });
+        console.log('🔫 === 子弹创建完成 ===');
     }
     
     // 递归查找Weapon节点

+ 520 - 120
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -1,20 +1,24 @@
 import { _decorator, Component, Node, Vec2, Vec3, RigidBody2D, Collider2D, Contact2DType, IPhysics2DContact, find, Prefab, instantiate, UITransform, resources, sp } from 'cc';
+import { WeaponBlockExample } from './WeaponBlockExample';
 const { ccclass, property } = _decorator;
 
 /**
- * WeaponBullet 负责两种子弹行为:
- * 1) explosive  – 击中敌人立即销毁并播放 hitEffect。
- * 2) piercing   – 击中敌人不销毁,持续飞行;若设置 trailEffect 则在尾部循环播放。
- *
- * 使用方式:
- *   const wb = bullet.addComponent(WeaponBullet);
- *   wb.init({
- *        behavior : 'explosive' | 'piercing',
- *        speed    : number,
- *        damage   : number,
- *        hitEffect: 'Animation/tx/tx000X',
- *        trailEffect?: 'Animation/tx/tx000X'
- *   });
+ * WeaponBullet - 统一的子弹控制器
+ * 
+ * 整合了原 BulletController 的所有功能:
+ * 1. 自动瞄准最近敌人
+ * 2. 物理属性设置
+ * 3. 生命周期管理
+ * 4. 特效系统
+ * 5. 多种子弹行为
+ * 
+ * 支持的子弹行为:
+ * - explosive: 击中敌人立即销毁并播放hitEffect
+ * - piercing: 击中敌人不销毁,持续飞行
+ * - ricochet: 弹射子弹,可设置弹射次数
+ * - boomerang: 回旋镖,延迟后返回
+ * - area_burn: 区域灼烧效果
+ * - homing: 导弹追踪
  */
 
 export type BulletBehaviour = 'explosive' | 'piercing' | 'ricochet' | 'boomerang' | 'area_burn' | 'homing' | 'normal';
@@ -24,7 +28,7 @@ export interface BulletInitData {
     damage: number;
     hitEffect?: string;   // resources 路径
     trailEffect?: string; // resources 路径
-    lifetime?: number;    // piercing 子弹有效时间
+    lifetime?: number;    // 子弹生存时间
     ricochetCount?: number; // ricochet 弹射次数
     ricochetAngle?: number; // 每次弹射随机角度 (deg)
     arcHeight?: number;      // boomerang
@@ -32,16 +36,34 @@ export interface BulletInitData {
     burnDuration?: number;   // area burn
     homingDelay?: number;    // homing missile
     homingStrength?: number;
+    // 从 BulletController 迁移的配置
+    firePosition?: Vec3;     // 发射位置(世界坐标)
+    autoTarget?: boolean;    // 是否自动瞄准最近敌人
 }
 
 @ccclass('WeaponBullet')
 export class WeaponBullet extends Component {
+    // === 基础配置 ===
+    @property
+    public speed: number = 300;
+
+    @property
+    public damage: number = 1;
+
+    @property
+    public lifetime: number = 5;
+
+    @property({ tooltip: '击中特效路径(resources 相对路径,例如 Animation/tx/tx0001)' })
+    public hitEffectPath: string = '';
+
+    @property({ tooltip: '拖尾特效路径' })
+    public trailEffectPath: string = '';
+
+    @property({ tooltip: '是否自动瞄准最近敌人' })
+    public autoTarget: boolean = true;
+
+    // === 子弹行为配置 ===
     private behavior: BulletBehaviour = 'explosive';
-    private speed    = 300;
-    private damage   = 1;
-    private hitEffectPath  = '';
-    private trailEffectPath = '';
-    private lifetime = 5;
     private ricochetLeft = 0;
     private ricochetAngle = 30; // deg
 
@@ -58,71 +80,314 @@ export class WeaponBullet extends Component {
     private homingStrength = 0.8;
     private homingTimer = 0;
 
-    private dir: Vec3 = new Vec3(1,0,0);
-    private rb: RigidBody2D = null;
-    private timer = 0;
-
-    public init(cfg: BulletInitData){
-        this.behavior = cfg.behavior;
-        this.speed    = cfg.speed;
-        this.damage   = cfg.damage;
-        this.hitEffectPath   = cfg.hitEffect || '';
-        this.trailEffectPath = cfg.trailEffect || '';
-        if(cfg.lifetime) this.lifetime = cfg.lifetime;
-        if(cfg.ricochetCount!==undefined) this.ricochetLeft = cfg.ricochetCount;
-        if(cfg.ricochetAngle) this.ricochetAngle = cfg.ricochetAngle;
-        if(this.behavior==='normal') this.behavior='explosive';
-        if(cfg.arcHeight){ /* placeholder visual only */ }
-        if(cfg.returnDelay){ this.boomerangReturnDelay = cfg.returnDelay; }
-        if(cfg.burnDuration){ this.burnDuration = cfg.burnDuration; }
-        if(cfg.homingDelay){ this.homingDelay = cfg.homingDelay; this.homingTimer = cfg.homingDelay; }
-        if(cfg.homingStrength){ this.homingStrength = cfg.homingStrength; }
-    }
-
-    onLoad(){
-        this.rb = this.getComponent(RigidBody2D);
-        const col = this.getComponent(Collider2D);
-        if(col){ col.on(Contact2DType.BEGIN_CONTACT,this.onHit,this); }
-        // 添加拖尾
-        if(this.behavior==='piercing' && this.trailEffectPath){
+    // === 从 BulletController 迁移的属性 ===
+    private targetEnemy: Node = null;
+    private rigidBody: RigidBody2D = null;
+    private lifeTimer: number = 0;
+    private direction: Vec3 = new Vec3(1, 0, 0);
+    private firePosition: Vec3 = null;
+    private isInitialized: boolean = false;
+    private needsPhysicsSetup: boolean = false;
+
+    /**
+     * 初始化子弹配置
+     */
+    public init(cfg: BulletInitData) {
+        this.behavior = cfg.behavior || 'explosive';
+        this.speed = cfg.speed || this.speed;
+        this.damage = cfg.damage || this.damage;
+        this.hitEffectPath = cfg.hitEffect || this.hitEffectPath;
+        this.trailEffectPath = cfg.trailEffect || this.trailEffectPath;
+        this.lifetime = cfg.lifetime !== undefined ? cfg.lifetime : this.lifetime;
+        this.autoTarget = cfg.autoTarget !== undefined ? cfg.autoTarget : this.autoTarget;
+        
+        // 子弹行为配置
+        if (cfg.ricochetCount !== undefined) this.ricochetLeft = cfg.ricochetCount;
+        if (cfg.ricochetAngle) this.ricochetAngle = cfg.ricochetAngle;
+        if (cfg.returnDelay) this.boomerangReturnDelay = cfg.returnDelay;
+        if (cfg.burnDuration) this.burnDuration = cfg.burnDuration;
+        if (cfg.homingDelay) { 
+            this.homingDelay = cfg.homingDelay; 
+            this.homingTimer = cfg.homingDelay; 
+        }
+        if (cfg.homingStrength) this.homingStrength = cfg.homingStrength;
+        
+        // 发射位置设置
+        if (cfg.firePosition) {
+            this.setFirePosition(cfg.firePosition);
+        }
+        
+        // 兼容性处理
+        if (this.behavior === 'normal') this.behavior = 'explosive';
+        
+        console.log('🔫 WeaponBullet 初始化完成:', {
+            behavior: this.behavior,
+            speed: this.speed,
+            damage: this.damage,
+            autoTarget: this.autoTarget
+        });
+    }
+
+    /**
+     * 设置子弹的发射位置(从 BulletController 迁移)
+     * @param position 发射位置(世界坐标)
+     */
+    public setFirePosition(position: Vec3) {
+        this.firePosition = position.clone();
+        this.needsPhysicsSetup = true;
+        console.log('🎯 子弹发射位置已设置:', this.firePosition);
+    }
+
+    /**
+     * 设置特效路径(从 BulletController 迁移)
+     */
+    public setEffectPaths(hit: string | null, trail?: string | null) {
+        if (hit) this.hitEffectPath = hit;
+        if (trail) this.trailEffectPath = trail;
+    }
+
+    onLoad() {
+        this.rigidBody = this.getComponent(RigidBody2D);
+        const collider = this.getComponent(Collider2D);
+        if (collider) {
+            collider.on(Contact2DType.BEGIN_CONTACT, this.onHit, this);
+        }
+    }
+
+    start() {
+        console.log('🚀 WeaponBullet start()开始...');
+        
+        // 设置生命周期
+        this.lifeTimer = this.lifetime;
+        
+        // 如果需要设置物理属性,延迟处理
+        if (this.needsPhysicsSetup) {
+            this.scheduleOnce(() => {
+                this.setupPhysics();
+            }, 0.05);
+        } else {
+            // 直接初始化
+            this.setupPhysics();
+        }
+        
+        // 添加拖尾特效
+        if (this.behavior === 'piercing' && this.trailEffectPath) {
             this.attachTrail();
         }
+        
+        console.log('✅ WeaponBullet start()完成');
     }
 
-    start(){
-        if(this.rb){
-            const v = new Vec2(this.dir.x*this.speed, this.dir.y*this.speed);
-            this.rb.linearVelocity = v;
+    /**
+     * 设置物理属性(从 BulletController 迁移)
+     */
+    private setupPhysics() {
+        console.log('🔧 开始设置子弹物理属性...');
+        
+        // 确保有刚体组件
+        if (!this.rigidBody) {
+            console.error('❌ 子弹预制体缺少RigidBody2D组件');
+            return;
         }
-        this.timer = this.lifetime;
-        this.initialDir.set(this.dir);
+        
+        const collider = this.getComponent(Collider2D);
+        if (!collider) {
+            console.error('❌ 子弹预制体缺少Collider2D组件');
+            return;
+        }
+        
+        // 设置物理属性
+        this.rigidBody.enabledContactListener = true;
+        this.rigidBody.gravityScale = 0; // 不受重力影响
+        this.rigidBody.linearDamping = 0; // 无阻尼
+        this.rigidBody.angularDamping = 0; // 无角阻尼
+        
+        console.log('✅ 物理组件设置成功');
+        
+        // 设置子弹位置
+        if (this.firePosition) {
+            this.setPositionInGameArea();
+        }
+        
+        // 初始化方向和速度
+        this.initializeDirection();
+        this.isInitialized = true;
+        
+        console.log('✅ 子弹物理属性设置完成');
+    }
+
+    /**
+     * 在GameArea中设置子弹位置(从 BulletController 迁移)
+     */
+    private setPositionInGameArea() {
+        const gameArea = find('Canvas/GameLevelUI/GameArea');
+        if (gameArea) {
+            const gameAreaTransform = gameArea.getComponent(UITransform);
+            if (gameAreaTransform) {
+                const localPos = gameAreaTransform.convertToNodeSpaceAR(this.firePosition);
+                this.node.position = localPos;
+                console.log('✅ 子弹位置已设置:', localPos);
+            }
+        }
+    }
+
+    /**
+     * 初始化子弹方向(从 BulletController 迁移并增强)
+     */
+    private initializeDirection() {
+        console.log('🎯 开始初始化子弹方向...');
+        
+        // 如果启用自动瞄准,寻找最近的敌人
+        if (this.autoTarget) {
+            this.findNearestEnemy();
+        }
+        
+        // 设置方向
+        if (this.targetEnemy && this.autoTarget) {
+            this.setDirectionToTarget();
+        } else {
+            // 随机方向或使用默认方向
+            const randomAngle = Math.random() * Math.PI * 2;
+            this.direction = new Vec3(Math.cos(randomAngle), Math.sin(randomAngle), 0);
+            console.log('🎲 使用随机方向');
+        }
+        
+        // 保存初始方向(用于boomerang)
+        this.initialDir.set(this.direction);
+        
+        // 应用速度
+        this.applyVelocity();
     }
 
-    private onHit(self:Collider2D, other:Collider2D, contact:IPhysics2DContact|null){
-        // 仅处理敌人
-        const node = other.node;
-        const isEnemy = node.getComponent('EnemyInstance')||node.getComponent('EnemyComponent');
-        if(!isEnemy) return;
+    /**
+     * 寻找最近的敌人(从 BulletController 迁移)
+     */
+    private findNearestEnemy() {
+        const enemyContainer = find('Canvas/GameLevelUI/enemyContainer');
+        if (!enemyContainer) {
+            console.log('❌ 未找到敌人容器');
+            return;
+        }
+        
+        const enemies = enemyContainer.children.filter(child => 
+            child.active && 
+            (child.name.toLowerCase().includes('enemy') || 
+             child.getComponent('EnemyInstance') !== null)
+        );
+        
+        if (enemies.length === 0) {
+            console.log('❌ 没有找到敌人');
+            return;
+        }
+        
+        let nearestEnemy: Node = null;
+        let nearestDistance = Infinity;
+        const bulletPos = this.node.worldPosition;
+        
+        for (const enemy of enemies) {
+            const distance = Vec3.distance(bulletPos, enemy.worldPosition);
+            if (distance < nearestDistance) {
+                nearestDistance = distance;
+                nearestEnemy = enemy;
+            }
+        }
+        
+        if (nearestEnemy) {
+            this.targetEnemy = nearestEnemy;
+            console.log(`🎯 锁定目标: ${nearestEnemy.name}`);
+        }
+    }
 
-        // 伤害
-        const ei = node.getComponent('EnemyInstance') as any;
-        if(ei && typeof ei.takeDamage==='function') ei.takeDamage(this.damage);
+    /**
+     * 设置朝向目标的方向(从 BulletController 迁移)
+     */
+    private setDirectionToTarget() {
+        if (!this.targetEnemy) return;
+        
+        const targetPos = this.targetEnemy.worldPosition;
+        const currentPos = this.node.worldPosition;
+        
+        this.direction = targetPos.clone().subtract(currentPos).normalize();
+        console.log('🎯 方向已设置,朝向目标敌人');
+    }
 
-        // 播放击中特效
-        this.spawnEffect(this.hitEffectPath, node.worldPosition);
+    /**
+     * 应用速度(从 BulletController 迁移并增强)
+     */
+    private applyVelocity() {
+        if (!this.rigidBody || !this.direction) {
+            console.error('❌ 无法应用速度:缺少刚体或方向');
+            return;
+        }
+        
+        // 覆盖速度值(全局控制)
+        const weaponGlobal = WeaponBlockExample.getInstance && WeaponBlockExample.getInstance();
+        if (weaponGlobal) {
+            this.speed = weaponGlobal.getCurrentBulletSpeed();
+        }
+        
+        const velocity = new Vec2(
+            this.direction.x * this.speed,
+            this.direction.y * this.speed
+        );
+        
+        this.rigidBody.linearVelocity = velocity;
+        console.log('✅ 子弹速度已应用:', velocity);
+    }
 
-        switch(this.behavior){
+    /**
+     * 碰撞处理(整合原有逻辑)
+     */
+    private onHit(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
+        const otherNode = otherCollider.node;
+        console.log('💥 子弹碰撞:', otherNode.name);
+        
+        // 获取碰撞世界坐标
+        let contactWorldPos: Vec3 = null;
+        if (contact && (contact as any).getWorldManifold) {
+            const wm = (contact as any).getWorldManifold();
+            if (wm && wm.points && wm.points.length > 0) {
+                contactWorldPos = new Vec3(wm.points[0].x, wm.points[0].y, 0);
+            }
+        }
+        if (!contactWorldPos) {
+            contactWorldPos = otherNode.worldPosition.clone();
+        }
+
+        // 检查是否击中敌人
+        if (this.isEnemyNode(otherNode)) {
+            console.log('🎯 击中敌人:', otherNode.name);
+            
+            // 对敌人造成伤害
+            this.damageEnemy(otherNode);
+            
+            // 播放击中特效
+            this.spawnHitEffect(contactWorldPos);
+
+            // 根据子弹行为处理
+            this.handleBehaviorOnHit(otherNode, contactWorldPos);
+        } else if (otherNode.name.toLowerCase().includes('wall')) {
+            console.log('🧱 击中墙体,销毁子弹');
+            this.node.destroy();
+        }
+    }
+
+    /**
+     * 根据子弹行为处理击中逻辑
+     */
+    private handleBehaviorOnHit(enemyNode: Node, contactPos: Vec3) {
+        switch (this.behavior) {
             case 'explosive':
                 this.node.destroy();
                 break;
             case 'piercing':
-                // 继续飞行
+                // 继续飞行,不销毁
                 break;
             case 'ricochet':
-                if(this.ricochetLeft>0){
+                if (this.ricochetLeft > 0) {
                     this.ricochetLeft--;
                     this.bounceDirection();
-                }else{
+                } else {
                     this.node.destroy();
                 }
                 break;
@@ -131,8 +396,8 @@ export class WeaponBullet extends Component {
                 break;
             case 'area_burn':
                 // 在地面生成灼烧特效
-                this.spawnEffect(this.trailEffectPath, node.worldPosition, true);
-                this.scheduleOnce(()=>{/* burn area ends */}, this.burnDuration);
+                this.spawnEffect(this.trailEffectPath, contactPos, true);
+                this.scheduleOnce(() => {/* burn area ends */}, this.burnDuration);
                 this.node.destroy();
                 break;
             case 'homing':
@@ -141,51 +406,147 @@ export class WeaponBullet extends Component {
         }
     }
 
-    update(dt:number){
-        if(this.behavior==='piercing'){
-            this.timer -= dt;
-            if(this.timer<=0){ this.node.destroy(); return; }
+    /**
+     * 判断是否为敌人节点(从 BulletController 迁移)
+     */
+    private isEnemyNode(node: Node): boolean {
+        const name = node.name.toLowerCase();
+        return name.includes('enemy') || 
+               name.includes('敌人') ||
+               node.getComponent('EnemyInstance') !== null ||
+               node.getComponent('EnemyComponent') !== null;
+    }
+
+    /**
+     * 对敌人造成伤害(从 BulletController 迁移)
+     */
+    private damageEnemy(enemyNode: Node) {
+        console.log('⚔️ 对敌人造成伤害:', this.damage);
+        
+        // 尝试调用敌人的受伤方法
+        const enemyInstance = enemyNode.getComponent('EnemyInstance') as any;
+        if (enemyInstance) {
+            if (typeof enemyInstance.takeDamage === 'function') {
+                enemyInstance.takeDamage(this.damage);
+                return;
+            }
+            if (typeof enemyInstance.health === 'number') {
+                enemyInstance.health -= this.damage;
+                if (enemyInstance.health <= 0) {
+                    enemyNode.destroy();
+                }
+                return;
+            }
+        }
+        
+        // 备用方案:通过EnemyController
+        const enemyController = find('Canvas/GameLevelUI/EnemyController')?.getComponent('EnemyController') as any;
+        if (enemyController && typeof enemyController.damageEnemy === 'function') {
+            enemyController.damageEnemy(enemyNode, this.damage);
         }
-        // 超界销毁
+    }
+
+    update(dt: number) {
+        // 只有在初始化完成后才进行更新
+        if (!this.isInitialized) return;
+        
+        // 生命周期管理
+        this.updateLifetime(dt);
+        
+        // 检查是否飞出游戏区域
+        this.checkOutOfBounds();
+        
+        // 子弹行为更新
+        this.updateBehavior(dt);
+    }
+
+    /**
+     * 更新生命周期
+     */
+    private updateLifetime(dt: number) {
+        if (this.behavior === 'piercing' || this.behavior === 'boomerang') {
+            this.lifeTimer -= dt;
+            if (this.lifeTimer <= 0) {
+                this.node.destroy();
+                return;
+            }
+        }
+    }
+
+    /**
+     * 检查越界(从 BulletController 迁移)
+     */
+    private checkOutOfBounds() {
         const canvas = find('Canvas');
-        if(!canvas) return;
-        const halfW = canvas.getComponent(UITransform).width/2;
-        const halfH = canvas.getComponent(UITransform).height/2;
+        if (!canvas || !this.node || !this.node.isValid) return;
+
+        const uiTransform = canvas.getComponent(UITransform);
+        if (!uiTransform) return;
+
+        const halfWidth = uiTransform.width / 2;
+        const halfHeight = uiTransform.height / 2;
+        const center = canvas.worldPosition;
+
+        const left = center.x - halfWidth;
+        const right = center.x + halfWidth;
+        const bottom = center.y - halfHeight;
+        const top = center.y + halfHeight;
+
+        // 允许子弹稍微超出边界后再销毁,避免边缘误差
+        const margin = 100;
+
         const pos = this.node.worldPosition;
-        if(Math.abs(pos.x) > halfW+200 || Math.abs(pos.y) > halfH+200){
+        if (pos.x < left - margin || pos.x > right + margin || 
+            pos.y < bottom - margin || pos.y > top + margin) {
             this.node.destroy();
         }
+    }
 
+    /**
+     * 更新子弹行为
+     */
+    private updateBehavior(dt: number) {
         // boomerang返回
-        if(this.behavior==='boomerang'){
+        if (this.behavior === 'boomerang') {
             this.boomerangTimer += dt;
-            if(this.boomerangTimer>=this.boomerangReturnDelay){
-                this.dir.multiplyScalar(-1);
-                if(this.rb){
-                    this.rb.linearVelocity = new Vec2(this.dir.x*this.speed, this.dir.y*this.speed);
+            if (this.boomerangTimer >= this.boomerangReturnDelay) {
+                this.direction.multiplyScalar(-1);
+                if (this.rigidBody) {
+                    this.rigidBody.linearVelocity = new Vec2(
+                        this.direction.x * this.speed, 
+                        this.direction.y * this.speed
+                    );
                 }
                 this.behavior = 'piercing'; // 之后按穿透逻辑处理
             }
         }
 
         // homing 导弹
-        if(this.behavior==='homing'){
-            if(this.homingTimer>0){ this.homingTimer-=dt; }
-            else{
+        if (this.behavior === 'homing') {
+            if (this.homingTimer > 0) { 
+                this.homingTimer -= dt; 
+            } else {
                 const enemyContainer = find('Canvas/GameLevelUI/enemyContainer');
-                if(enemyContainer && enemyContainer.children.length>0){
-                    let nearest:Node=null; let dist=1e9;
-                    for(const e of enemyContainer.children){
-                        if(!e.active) continue;
+                if (enemyContainer && enemyContainer.children.length > 0) {
+                    let nearest: Node = null; 
+                    let dist = 1e9;
+                    for (const e of enemyContainer.children) {
+                        if (!e.active) continue;
                         const d = Vec3.distance(this.node.worldPosition, e.worldPosition);
-                        if(d<dist){ dist=d; nearest=e; }
+                        if (d < dist) { 
+                            dist = d; 
+                            nearest = e; 
+                        }
                     }
-                    if(nearest){
+                    if (nearest) {
                         const dir = nearest.worldPosition.clone().subtract(this.node.worldPosition).normalize();
                         // 线性插值方向
-                        this.dir.lerp(dir, this.homingStrength*dt);
-                        if(this.rb){
-                            this.rb.linearVelocity = new Vec2(this.dir.x*this.speed, this.dir.y*this.speed);
+                        this.direction.lerp(dir, this.homingStrength * dt);
+                        if (this.rigidBody) {
+                            this.rigidBody.linearVelocity = new Vec2(
+                                this.direction.x * this.speed, 
+                                this.direction.y * this.speed
+                            );
                         }
                     }
                 }
@@ -193,43 +554,82 @@ export class WeaponBullet extends Component {
         }
     }
 
-    private attachTrail(){
+    /**
+     * 添加拖尾特效
+     */
+    private attachTrail() {
         this.spawnEffect(this.trailEffectPath, new Vec3(), true, this.node);
     }
 
-    private spawnEffect(path:string, worldPos:Vec3, loop=false, parent?:Node){
-        if(!path) return;
-        const m = path.match(/tx\/(tx\d{4})/);
-        const code = m? m[1]:'';
-        const skPath = code?`Animation/WeaponTx/${code}/${code}`:path;
-        resources.load(skPath, sp.SkeletonData, (err,sk:sp.SkeletonData)=>{
-            if(err) {console.warn('load effect fail', skPath); return;}
-            const n = new Node('Fx');
-            const s = n.addComponent(sp.Skeleton);
-            s.skeletonData = sk;
-            s.setAnimation(0,'animation',loop);
-            const targetParent = parent||find('Canvas/GameLevelUI/GameArea')||find('Canvas');
-            if(targetParent){
-                targetParent.addChild(n);
-                if(parent){ n.setPosition(0,0,0);}else{
-                    const trans = targetParent.getComponent(UITransform);
-                    n.position = trans?trans.convertToNodeSpaceAR(worldPos):worldPos;
+    /**
+     * 生成特效(整合并增强)
+     */
+    private spawnEffect(path: string, worldPos: Vec3, loop = false, parent?: Node) {
+        if (!path) return;
+        
+        const match = path.match(/tx\/(tx\d{4})/);
+        const code = match ? match[1] : '';
+        const skeletonPath = code ? `Animation/WeaponTx/${code}/${code}` : path;
+        
+        console.log('✨ 尝试加载特效:', skeletonPath);
+        resources.load(skeletonPath, sp.SkeletonData, (err, skData: sp.SkeletonData) => {
+            if (err) {
+                console.warn('加载特效失败:', skeletonPath, err);
+                return;
+            }
+
+            const effectNode = new Node('Effect');
+            const skeleton: sp.Skeleton = effectNode.addComponent(sp.Skeleton);
+            skeleton.skeletonData = skData;
+            skeleton.setAnimation(0, 'animation', loop);
+
+            const targetParent = parent || find('Canvas/GameLevelUI/GameArea') || find('Canvas');
+            if (targetParent) {
+                targetParent.addChild(effectNode);
+                if (parent) {
+                    effectNode.setPosition(0, 0, 0);
+                } else {
+                    const parentTrans = targetParent.getComponent(UITransform);
+                    if (parentTrans) {
+                        const localPos = parentTrans.convertToNodeSpaceAR(worldPos);
+                        effectNode.position = localPos;
+                    } else {
+                        effectNode.setWorldPosition(worldPos.clone());
+                    }
                 }
             }
-            if(!loop){ s.setCompleteListener(()=>n.destroy()); }
+
+            if (!loop) {
+                skeleton.setCompleteListener(() => {
+                    effectNode.destroy();
+                });
+            }
         });
     }
 
-    private bounceDirection(){
+    /**
+     * 生成击中特效(从 BulletController 迁移)
+     */
+    private spawnHitEffect(worldPos: Vec3) {
+        this.spawnEffect(this.hitEffectPath, worldPos, false);
+    }
+
+    /**
+     * 弹射方向计算
+     */
+    private bounceDirection() {
         // 简单随机旋转 ricochetAngle
         const sign = Math.random() < 0.5 ? -1 : 1;
-        const rad = this.ricochetAngle * (Math.PI/180) * sign;
+        const rad = this.ricochetAngle * (Math.PI / 180) * sign;
         const cos = Math.cos(rad), sin = Math.sin(rad);
-        const nx = this.dir.x * cos - this.dir.y * sin;
-        const ny = this.dir.x * sin + this.dir.y * cos;
-        this.dir.set(nx, ny, 0);
-        if(this.rb){
-            this.rb.linearVelocity = new Vec2(this.dir.x*this.speed, this.dir.y*this.speed);
+        const nx = this.direction.x * cos - this.direction.y * sin;
+        const ny = this.direction.x * sin + this.direction.y * cos;
+        this.direction.set(nx, ny, 0);
+        if (this.rigidBody) {
+            this.rigidBody.linearVelocity = new Vec2(
+                this.direction.x * this.speed, 
+                this.direction.y * this.speed
+            );
         }
     }
 }