Browse Source

隐身及字体大小等优化

181404010226 2 months ago
parent
commit
45303ad7f0

File diff suppressed because it is too large
+ 210 - 177
assets/Scenes/GameLevel.scene


+ 26 - 61
assets/data/enemies.json

@@ -555,7 +555,14 @@
       "rage_damage_multiplier": 1.0,
       "rage_speed_multiplier": 1.0
     },
-    "special_abilities": []
+    "special_abilities": [
+      {
+        "type": "stealth",
+        "damage": 0,
+        "range": 0,
+        "cooldown": 3
+      }
+    ]
   },
   {
     "id": "bucket_zombie",
@@ -804,18 +811,18 @@
       "fuseSound": "",
       "volume": 1.0
     },
-    "specialAbilities": [
+    "special_abilities": [
       {
         "type": "charge_attack",
         "damage": 1,
-        "range": 150.0,
-        "cooldown": 8.0
+        "range": 150,
+        "cooldown": 8
       },
       {
         "type": "area_attack",
         "damage": 2,
-        "range": 100.0,
-        "cooldown": 12.0
+        "range": 100,
+        "cooldown": 12
       }
     ],
     "bossConfig": {
@@ -854,21 +861,7 @@
       "rage_threshold": 0.3,
       "rage_damage_multiplier": 1.5,
       "rage_speed_multiplier": 1.3
-    },
-    "special_abilities": [
-      {
-        "type": "charge_attack",
-        "damage": 1,
-        "range": 150,
-        "cooldown": 8
-      },
-      {
-        "type": "area_attack",
-        "damage": 2,
-        "range": 100,
-        "cooldown": 12
-      }
-    ]
+    }
   },
   {
     "id": "boss2_gravedigger",
@@ -931,18 +924,18 @@
       "fuseSound": "",
       "volume": 1.0
     },
-    "specialAbilities": [
+    "special_abilities": [
       {
         "type": "charge_attack",
         "damage": 1,
-        "range": 150.0,
-        "cooldown": 8.0
+        "range": 150,
+        "cooldown": 8
       },
       {
         "type": "area_attack",
         "damage": 2,
-        "range": 100.0,
-        "cooldown": 12.0
+        "range": 100,
+        "cooldown": 12
       }
     ],
     "bossConfig": {
@@ -981,21 +974,7 @@
       "rage_threshold": 0.3,
       "rage_damage_multiplier": 1.5,
       "rage_speed_multiplier": 1.3
-    },
-    "special_abilities": [
-      {
-        "type": "charge_attack",
-        "damage": 1,
-        "range": 150,
-        "cooldown": 8
-      },
-      {
-        "type": "area_attack",
-        "damage": 2,
-        "range": 100,
-        "cooldown": 12
-      }
-    ]
+    }
   },
   {
     "id": "boss3_cyborg",
@@ -1058,18 +1037,18 @@
       "fuseSound": "",
       "volume": 1.0
     },
-    "specialAbilities": [
+    "special_abilities": [
       {
         "type": "charge_attack",
         "damage": 1,
-        "range": 150.0,
-        "cooldown": 8.0
+        "range": 150,
+        "cooldown": 8
       },
       {
         "type": "area_attack",
         "damage": 2,
-        "range": 100.0,
-        "cooldown": 12.0
+        "range": 100,
+        "cooldown": 12
       }
     ],
     "bossConfig": {
@@ -1108,20 +1087,6 @@
       "rage_threshold": 0.3,
       "rage_damage_multiplier": 1.5,
       "rage_speed_multiplier": 1.3
-    },
-    "special_abilities": [
-      {
-        "type": "charge_attack",
-        "damage": 1,
-        "range": 150,
-        "cooldown": 8
-      },
-      {
-        "type": "area_attack",
-        "damage": 2,
-        "range": 100,
-        "cooldown": 12
-      }
-    ]
+    }
   }
 ]

BIN
assets/data/excel/敌人配置表.xlsx


+ 1 - 1
assets/resources/prefabs/DamageNumber.prefab

@@ -116,7 +116,7 @@
     "_horizontalAlign": 1,
     "_verticalAlign": 1,
     "_actualFontSize": 20,
-    "_fontSize": 20,
+    "_fontSize": 25,
     "_fontFamily": "Arial",
     "_lineHeight": 40,
     "_overflow": 2,

+ 2 - 2
assets/scripts/Animations/DamageNumberAni.ts

@@ -96,10 +96,10 @@ export class DamageNumberAni extends Component {
             // 暴击时使用不同颜色
             if (isCritical) {
                 label.color = Color.YELLOW; // 暴击显示黄色
-                label.fontSize = 24; // 暴击字体更大
+                label.fontSize = 30; // 暴击字体更大
             } else {
                 label.color = Color.WHITE; // 普通伤害白色
-                label.fontSize = 20;
+                label.fontSize = 25;
             }
         }
             

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

@@ -1396,10 +1396,7 @@ export class BallController extends Component {
 
             // === 根据武器配置选择合适的预制体 ===
             const trailEffect = finalConfig.bulletConfig?.visual?.trailEffect;
-            // 尖胡萝卜特殊处理:使用PelletContainer预制体但激活Spine节点
-            const isSharpCarrot = finalConfig.id === 'sharp_carrot';
-            // 当trailEffect为true或者是尖胡萝卜时都使用容器预制体
-            const needsContainerPrefab = Boolean(trailEffect) || isSharpCarrot;
+            const needsContainerPrefab = Boolean(trailEffect);
             const prefabToUse: Prefab = (needsContainerPrefab && this.bulletContainerPrefab) ? this.bulletContainerPrefab : this.bulletPrefab;
             if (!prefabToUse) {
                 console.warn(`[BallController] 没有可用的子弹预制体`);

+ 5 - 8
assets/scripts/CombatSystem/BlockManager.ts

@@ -2338,6 +2338,11 @@ export class BlockManager extends Component {
     private async performMerge(target: Node, source: Node, rarity: string) {
         try {
             console.log(`[BlockManager] 开始合成方块: 目标=${target.name}, 源=${source.name}, 稀有度=${rarity}`);
+            // 先立即触发合成特效,避免后续异步逻辑造成播放延迟
+            const worldPos = new Vec3();
+            target.getWorldPosition(worldPos);
+            this.spawnMergeSmoke(worldPos);
+            this.playUpgradeEffect(target);
             
             // 注意:参与合成的方块都在网格中,它们的db标签本来就应该是隐藏的
             // 不需要调用hideDbLabel,因为这些方块已经不在block容器中了
@@ -2415,14 +2420,6 @@ export class BlockManager extends Component {
                 this.loadBlockRarityImage(target, nextRarity);
             }
 
-            // 播放烟雾动画
-            const worldPos = new Vec3();
-            target.getWorldPosition(worldPos);
-            this.spawnMergeSmoke(worldPos);
-
-            // 播放升级特效动画
-            this.playUpgradeEffect(target);
-
             // 递归检查是否还能继续合成
             if (nextRarity) {
                 await this.tryMergeBlock(target);

+ 16 - 1
assets/scripts/CombatSystem/BulletEffects/BulletHitEffect.ts

@@ -31,6 +31,8 @@ export class BulletHitEffect extends Component {
     private pierceCount: number = 0;
     private ricochetCount: number = 0;
     private activeBurnAreas: Node[] = [];
+    // 爆炸一次性保护,防止同一子弹重复结算范围伤害
+    private hasExploded: boolean = false;
     
     // 检测范围相关
     private detectionCollider: CircleCollider2D | null = null;
@@ -60,10 +62,19 @@ export class BulletHitEffect extends Component {
         
         // 重置锯齿草状态
         this.resetSawGrassState();
+        // 重置爆炸状态
+        this.hasExploded = false;
         
         // 检查是否有手动添加的检测范围碰撞器
         this.setupDetectionColliderEvents();
     }
+
+    /**
+     * 是否包含爆炸效果
+     */
+    public hasExplosionEffect(): boolean {
+        return Array.isArray(this.hitEffects) && this.hitEffects.some(e => e.type === 'explosion');
+    }
     
     /**
      * 处理命中事件
@@ -138,7 +149,11 @@ export class BulletHitEffect extends Component {
                 break;
                 
             case 'explosion':
-                result.damageDealt = this.processExplosion(effect, contactPos);
+                // 若已爆炸则不再重复结算伤害,只标记销毁
+                if (!this.hasExploded) {
+                    result.damageDealt = this.processExplosion(effect, contactPos);
+                    this.hasExploded = true;
+                }
                 result.shouldDestroy = true;
                 break;
                 

+ 81 - 5
assets/scripts/CombatSystem/EnemyComponent.ts

@@ -1,4 +1,5 @@
-import { _decorator, Component } from 'cc';
+import { _decorator, Component, Node, Color, UIOpacity } from 'cc';
+import { sp } from 'cc';
 import { ConfigManager, EnemyConfig } from '../Core/ConfigManager';
 const { ccclass, property } = _decorator;
 
@@ -12,6 +13,33 @@ export class EnemyComponent extends Component {
     public spawner: any = null; // 对生成器的引用
     private rageActive: boolean = false;
     private rageStartTime: number = 0;
+    // 隐身状态
+    private stealthActive: boolean = false;
+
+    start() {
+        // 如果具备隐身能力,则初始化隐身循环
+        const ability = this.getSpecialAbility();
+        if (ability === 'stealth') {
+            const cooldown = this.getSpecialAbilityCooldown();
+            // 初始进入隐身
+            this.applyStealthOpacity(100);
+            this.stealthActive = true;
+            // 按冷却时间循环切换显隐
+            this.schedule(() => {
+                if (this.stealthActive) {
+                    this.applyStealthOpacity(255);
+                } else {
+                    this.applyStealthOpacity(100);
+                }
+                this.stealthActive = !this.stealthActive;
+            }, Math.max(0.1, cooldown));
+        }
+    }
+
+    onDisable() {
+        // 组件禁用时取消调度,避免泄漏
+        this.unscheduleAllCallbacks();
+    }
 
     // 获取敌人生命值
     public getHealth(): number {
@@ -160,20 +188,68 @@ export class EnemyComponent extends Component {
     // === 特殊能力 ===
     // 获取特殊能力类型
     public getSpecialAbility(): string {
-        if (this.enemyConfig?.special_abilities && this.enemyConfig.special_abilities.length > 0) {
-            return this.enemyConfig.special_abilities[0].type || 'none';
+        const abilities = this.enemyConfig?.special_abilities;
+        if (Array.isArray(abilities) && abilities.length > 0) {
+            return abilities[0]?.type || 'none';
         }
         return 'none';
     }
 
     // 获取特殊能力冷却时间
     public getSpecialAbilityCooldown(): number {
-        if (this.enemyConfig?.special_abilities && this.enemyConfig.special_abilities.length > 0) {
-            return this.enemyConfig.special_abilities[0].cooldown || 5.0;
+        const abilities = this.enemyConfig?.special_abilities;
+        if (Array.isArray(abilities) && abilities.length > 0) {
+            const cooldown = abilities[0]?.cooldown;
+            return typeof cooldown === 'number' ? cooldown : 5.0;
         }
         return 5.0;
     }
 
+    /**
+     * 应用隐身透明度到 EnemySprite 的 Skeleton
+     * @param opacity 透明度(0-255),示例:50 表示半透明
+     */
+    private applyStealthOpacity(opacity: number): void {
+        const enemySprite = this.findEnemySpriteNode();
+        if (!enemySprite) {
+            console.warn('[EnemyComponent] 未找到 EnemySprite 节点,无法应用隐身透明度');
+            return;
+        }
+
+        // 设置 UIOpacity 以保证整节点透明度生效
+        let uiOpacity = enemySprite.getComponent(UIOpacity);
+        if (!uiOpacity) {
+            uiOpacity = enemySprite.addComponent(UIOpacity);
+        }
+        uiOpacity.opacity = Math.max(0, Math.min(255, opacity));
+
+        // 同时设置 Skeleton 的颜色 alpha(与编辑器表现一致)
+        const skeleton = enemySprite.getComponent(sp.Skeleton);
+        if (skeleton) {
+            const current = skeleton.color ?? new Color(255, 255, 255, 255);
+            const next = new Color(current.r, current.g, current.b, Math.max(0, Math.min(255, opacity)));
+            skeleton.color = next;
+        }
+    }
+
+    /**
+     * 在当前节点层级下查找 EnemySprite 节点(兼容不同层级路径)
+     */
+    private findEnemySpriteNode(): Node | null {
+        // 直接子节点命中
+        const direct = this.node.getChildByName('EnemySprite');
+        if (direct) return direct;
+
+        // 深度遍历查找
+        const stack: Node[] = [...this.node.children];
+        while (stack.length > 0) {
+            const cur = stack.pop()!;
+            if (cur.name === 'EnemySprite') return cur;
+            stack.push(...cur.children);
+        }
+        return null;
+    }
+
     // === 狂暴状态管理 ===
     // 检查是否处于狂暴状态
     public isInRage(): boolean {

+ 1 - 1
assets/scripts/CombatSystem/EnemyInstance.ts

@@ -690,7 +690,7 @@ export class EnemyInstance extends Component {
         dir.normalize();
         
         // 使用代码控制移动,不依赖物理系统
-        const driftSpeed = Math.min(this.speed * 2, 500); // 限制最大漂移速度为500
+        const driftSpeed = Math.min(this.speed * 8, 500); // 限制最大漂移速度为5500
         const moveDistance = driftSpeed * deltaTime;
         const actualMoveDistance = Math.min(moveDistance, distanceToTarget);
         const newWorldPos = currentWorldPos.add(dir.multiplyScalar(actualMoveDistance));

+ 31 - 22
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -304,23 +304,29 @@ export class WeaponBullet extends Component {
         // 计算方向
         const direction = initData.direction || this.calculateDirection(initData.autoTarget);
         
-        // === 根据发射方向调整容器朝向(仅首次,后续不再旋转) ===
+        // === 根据发射方向调整朝向 ===
+        // 说明:
+        // - 当 shouldRotate !== false 时,保持父节点按发射方向旋转,并启用持续自旋转(原逻辑)。
+        // - 当 shouldRotate === false 时,仅旋转 Pellet 子节点,避免父子叠加导致角度加倍。
+        // - 对尖胡萝卜(sharp_carrot)应用图片朝向校正:以图片上方为正方向,发射时上方朝向发射方向,尖头沿轨迹。
         if (direction) {
-            // 角度 = atan2(y,x),转成度数
             const deg = math.toDegree(Math.atan2(direction.y, direction.x));
-            this.node.angle = deg;
-        }
-        
-        // 根据配置决定旋转行为
-        if (config.shouldRotate !== false) { 
-            // 默认为true,启用持续自旋转
-            this.applyAutoRotation();
-        } else {
-            // shouldRotate为false时,让Pellet子节点根据发射方向旋转
-            const pelletNode = this.node.getChildByName('Pellet');
-            if (pelletNode && direction) {
-                const deg = math.toDegree(Math.atan2(direction.y, direction.x));
-                pelletNode.angle = deg;
+            if (config.shouldRotate !== false) {
+                // 父节点按发射方向对齐
+                this.node.angle = deg;
+                // 启用持续自旋转
+                this.applyAutoRotation();
+            } else {
+                const pelletNode = this.node.getChildByName('Pellet');
+                // 针对尖胡萝卜图片的朝向校正:图片默认“上”为正,需减去90度使“上”对齐发射方向
+                const carrotOffset = (this.weaponConfig?.id === 'sharp_carrot') ? -90 : 0;
+                const finalDeg = deg + carrotOffset;
+                if (pelletNode) {
+                    pelletNode.angle = finalDeg;
+                } else {
+                    // 若无Pellet子节点,退化为旋转父节点
+                    this.node.angle = finalDeg;
+                }
             }
         }
         
@@ -465,13 +471,16 @@ export class WeaponBullet extends Component {
             const isCritical = Math.random() < this.getCritChance();
             const finalDamage = isCritical ? this.getFinalCritDamage() : this.getFinalDamage();
             
-            // 触发子弹命中敌人事件
-            EventBus.getInstance().emit(GameEvents.BULLET_HIT_ENEMY, {
-                enemyNode: enemyRootNode,
-                damage: finalDamage,
-                isCritical: isCritical,
-                source: `WeaponBullet-${this.weaponConfig?.id || 'unknown'}`
-            });
+            // 若为爆炸类型子弹,则不在碰撞处直接结算一次伤害,避免与范围爆炸重复
+            if (!this.bulletHitEffect || !this.bulletHitEffect.hasExplosionEffect()) {
+                // 触发子弹命中敌人事件(非爆炸型)
+                EventBus.getInstance().emit(GameEvents.BULLET_HIT_ENEMY, {
+                    enemyNode: enemyRootNode,
+                    damage: finalDamage,
+                    isCritical: isCritical,
+                    source: `WeaponBullet-${this.weaponConfig?.id || 'unknown'}`
+                });
+            }
         }
         
         // 获取碰撞世界坐标

+ 1 - 1
assets/scripts/Core/ConfigManager.ts

@@ -478,4 +478,4 @@ export class ConfigManager extends BaseSingleton {
 
     // 获取球控制器配置
     // getBallControllerConfig方法已移除,球控制器配置现在通过装饰器直接在BallController中加载
-}
+}

Some files were not shown because too many files changed in this diff