181404010226 5 月之前
父节点
当前提交
d93cd2b758

文件差异内容过多而无法显示
+ 4778 - 243
assets/Scenes/GameLevel.scene


+ 168 - 0
assets/assets/Prefabs/CoinDrop.prefab

@@ -0,0 +1,168 @@
+[
+  {
+    "__type__": "cc.Prefab",
+    "_name": "CoinDrop",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "_native": "",
+    "data": {
+      "__id__": 1
+    },
+    "optimizationPolicy": 0,
+    "persistent": false
+  },
+  {
+    "__type__": "cc.Node",
+    "_name": "CoinDrop",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "_parent": null,
+    "_children": [],
+    "_active": true,
+    "_components": [
+      {
+        "__id__": 2
+      },
+      {
+        "__id__": 4
+      },
+      {
+        "__id__": 6
+      }
+    ],
+    "_prefab": {
+      "__id__": 8
+    },
+    "_lpos": {
+      "__type__": "cc.Vec3",
+      "x": 76.46,
+      "y": 0,
+      "z": 0
+    },
+    "_lrot": {
+      "__type__": "cc.Quat",
+      "x": 0,
+      "y": 0,
+      "z": 0,
+      "w": 1
+    },
+    "_lscale": {
+      "__type__": "cc.Vec3",
+      "x": 1,
+      "y": 1,
+      "z": 1
+    },
+    "_mobility": 0,
+    "_layer": 1073741824,
+    "_euler": {
+      "__type__": "cc.Vec3",
+      "x": 0,
+      "y": 0,
+      "z": 0
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.UITransform",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 1
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 3
+    },
+    "_contentSize": {
+      "__type__": "cc.Size",
+      "width": 39,
+      "height": 34
+    },
+    "_anchorPoint": {
+      "__type__": "cc.Vec2",
+      "x": 0.5,
+      "y": 0.5
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "22ELagfTdHgZWt2J39L2Zz"
+  },
+  {
+    "__type__": "cc.Sprite",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 1
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 5
+    },
+    "_customMaterial": null,
+    "_srcBlendFactor": 2,
+    "_dstBlendFactor": 4,
+    "_color": {
+      "__type__": "cc.Color",
+      "r": 255,
+      "g": 255,
+      "b": 255,
+      "a": 255
+    },
+    "_spriteFrame": {
+      "__uuid__": "bdf08b38-f5eb-4192-8e22-ca89e3512c1a@f9941",
+      "__expectedType__": "cc.SpriteFrame"
+    },
+    "_type": 0,
+    "_fillType": 0,
+    "_sizeMode": 0,
+    "_fillCenter": {
+      "__type__": "cc.Vec2",
+      "x": 0,
+      "y": 0
+    },
+    "_fillStart": 0,
+    "_fillRange": 0,
+    "_isTrimmedMode": true,
+    "_useGrayscale": false,
+    "_atlas": null,
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "89y44mWN5OHY0cjTXBEpnD"
+  },
+  {
+    "__type__": "e93delWf01FDKX6ogl4j7e9",
+    "_name": "",
+    "_objFlags": 0,
+    "__editorExtras__": {},
+    "node": {
+      "__id__": 1
+    },
+    "_enabled": true,
+    "__prefab": {
+      "__id__": 7
+    },
+    "_id": ""
+  },
+  {
+    "__type__": "cc.CompPrefabInfo",
+    "fileId": "64yc/D4xxNQoMh0/DBgM7x"
+  },
+  {
+    "__type__": "cc.PrefabInfo",
+    "root": {
+      "__id__": 1
+    },
+    "asset": {
+      "__id__": 0
+    },
+    "fileId": "9bXmDETeFEFrJzTvWIAyu0",
+    "instance": null,
+    "targetOverrides": null
+  }
+]

+ 13 - 0
assets/assets/Prefabs/CoinDrop.prefab.meta

@@ -0,0 +1,13 @@
+{
+  "ver": "1.1.50",
+  "importer": "prefab",
+  "imported": true,
+  "uuid": "70f7651c-5d17-45aa-8079-b58c5b191125",
+  "files": [
+    ".json"
+  ],
+  "subMetas": {},
+  "userData": {
+    "syncNodeName": "CoinDrop"
+  }
+}

+ 3 - 3
assets/resources/data/levels/Level1.json

@@ -12,7 +12,7 @@
       "enemies": [
         {
           "enemyType": "普通僵尸",
-          "count": 8,
+          "count": 5,
           "spawnInterval": 5,
           "characteristics": ["中速移动", "无技能"]
         }
@@ -23,7 +23,7 @@
       "enemies": [
         {
           "enemyType": "普通僵尸",
-          "count": 10,
+          "count": 7,
           "spawnInterval": 5,
           "characteristics": ["中速移动", "无技能"]
         }
@@ -45,7 +45,7 @@
       "enemies": [
         {
           "enemyType": "普通僵尸",
-          "count": 12,
+          "count": 6,
           "spawnInterval": 4,
           "characteristics": ["中速移动", "无技能"]
         }

二进制
assets/resources/images/UI/图标/XX.png


+ 134 - 0
assets/resources/images/UI/图标/XX.png.meta

@@ -0,0 +1,134 @@
+{
+  "ver": "1.0.27",
+  "importer": "image",
+  "imported": true,
+  "uuid": "516b459a-8635-40ab-a2e4-612b95c589db",
+  "files": [
+    ".json",
+    ".png"
+  ],
+  "subMetas": {
+    "6c48a": {
+      "importer": "texture",
+      "uuid": "516b459a-8635-40ab-a2e4-612b95c589db@6c48a",
+      "displayName": "XX",
+      "id": "6c48a",
+      "name": "texture",
+      "userData": {
+        "wrapModeS": "clamp-to-edge",
+        "wrapModeT": "clamp-to-edge",
+        "imageUuidOrDatabaseUri": "516b459a-8635-40ab-a2e4-612b95c589db",
+        "isUuid": true,
+        "visible": false,
+        "minfilter": "linear",
+        "magfilter": "linear",
+        "mipfilter": "none",
+        "anisotropy": 0
+      },
+      "ver": "1.0.22",
+      "imported": true,
+      "files": [
+        ".json"
+      ],
+      "subMetas": {}
+    },
+    "f9941": {
+      "importer": "sprite-frame",
+      "uuid": "516b459a-8635-40ab-a2e4-612b95c589db@f9941",
+      "displayName": "XX",
+      "id": "f9941",
+      "name": "spriteFrame",
+      "userData": {
+        "trimThreshold": 1,
+        "rotated": false,
+        "offsetX": 1.5,
+        "offsetY": 0,
+        "trimX": 3,
+        "trimY": 0,
+        "width": 97,
+        "height": 69,
+        "rawWidth": 100,
+        "rawHeight": 69,
+        "borderTop": 0,
+        "borderBottom": 0,
+        "borderLeft": 0,
+        "borderRight": 0,
+        "packable": true,
+        "pixelsToUnit": 100,
+        "pivotX": 0.5,
+        "pivotY": 0.5,
+        "meshType": 0,
+        "vertices": {
+          "rawPosition": [
+            -48.5,
+            -34.5,
+            0,
+            48.5,
+            -34.5,
+            0,
+            -48.5,
+            34.5,
+            0,
+            48.5,
+            34.5,
+            0
+          ],
+          "indexes": [
+            0,
+            1,
+            2,
+            2,
+            1,
+            3
+          ],
+          "uv": [
+            3,
+            69,
+            100,
+            69,
+            3,
+            0,
+            100,
+            0
+          ],
+          "nuv": [
+            0.03,
+            0,
+            1,
+            0,
+            0.03,
+            1,
+            1,
+            1
+          ],
+          "minPos": [
+            -48.5,
+            -34.5,
+            0
+          ],
+          "maxPos": [
+            48.5,
+            34.5,
+            0
+          ]
+        },
+        "isUuid": true,
+        "imageUuidOrDatabaseUri": "516b459a-8635-40ab-a2e4-612b95c589db@6c48a",
+        "atlasUuid": "",
+        "trimType": "auto"
+      },
+      "ver": "1.0.12",
+      "imported": true,
+      "files": [
+        ".json"
+      ],
+      "subMetas": {}
+    }
+  },
+  "userData": {
+    "type": "sprite-frame",
+    "hasAlpha": true,
+    "fixAlphaTransparencyArtifacts": false,
+    "redirect": "516b459a-8635-40ab-a2e4-612b95c589db@6c48a"
+  }
+}

+ 7 - 50
assets/scripts/CombatSystem/BlockManager.ts

@@ -41,11 +41,7 @@ export class BlockManager extends Component {
     // 游戏是否已开始
     public gameStarted: boolean = false;
     
-    // 方块移动冷却时间(秒)
-    @property({
-        tooltip: '游戏开始后方块移动的冷却时间(秒)'
-    })
-    public blockMoveCooldown: number = 1;
+    // 移除移动冷却:相关属性/逻辑已废弃
     
     // SaveDataManager 单例
     private saveDataManager: SaveDataManager = null; // 仅用于局外数据
@@ -83,10 +79,6 @@ export class BlockManager extends Component {
     // 方块当前所在的区域
     private blockLocations: Map<Node, string> = new Map();
     
-    // 方块移动冷却状态管理
-    private blockCooldowns: Map<Node, number> = new Map(); // 存储每个方块的冷却结束时间
-    private globalCooldownEndTime: number = 0; // 全局冷却结束时间
-    
     // 配置管理器
     private configManager: ConfigManager = null;
     
@@ -100,43 +92,12 @@ export class BlockManager extends Component {
     })
     public refreshButton: Node = null;
     
-    // 检查方块是否可以移动(冷却检查)
-    private canMoveBlock(block: Node): boolean {
-        if (!this.gameStarted) {
-            // 游戏未开始(备战阶段),可以自由移动
-            return true;
-        }
-        
-        const currentTime = Date.now() / 1000; // 转换为秒
-        
-        // 检查全局冷却
-        if (currentTime < this.globalCooldownEndTime) {
-            const remainingTime = Math.ceil(this.globalCooldownEndTime - currentTime);
-            return false;
-        }
-        
-        return true;
-    }
-    
-    // 设置方块移动冷却
-    private setBlockCooldown(block: Node) {
-        if (!this.gameStarted) {
-            // 游戏未开始,不设置冷却
-            return;
-        }
-        
-        const currentTime = Date.now() / 1000; // 转换为秒
-        const cooldownEndTime = currentTime + this.blockMoveCooldown;
-        
-        // 设置全局冷却
-        this.globalCooldownEndTime = cooldownEndTime;
-    }
-    
+    /** 冷却机制已废弃,方块随时可移动 */
+    private canMoveBlock(block: Node): boolean { return true; }
+
+
     // 清除所有冷却(游戏重置时调用)
-    public clearAllCooldowns() {
-        this.blockCooldowns.clear();
-        this.globalCooldownEndTime = 0;
-    }
+    public clearAllCooldowns() { /* no-op */ }
 
     start() {
         // 获取配置管理器
@@ -503,11 +464,7 @@ export class BlockManager extends Component {
             if (this.currentDragBlock) {
                 this.handleBlockDrop(event);
                 
-                // 如果成功移动且游戏已开始,设置冷却
-                if (this.gameStarted && this.blockLocations.get(this.currentDragBlock) === 'grid') {
-                    this.setBlockCooldown(this.currentDragBlock);
-                }
-                
+          
                 this.currentDragBlock = null;
             }
         }, this);

+ 36 - 0
assets/scripts/CombatSystem/CoinDrop.ts

@@ -0,0 +1,36 @@
+import { _decorator, Component, Vec3, tween, find, Label } from 'cc';
+import { LevelSessionManager } from '../Core/LevelSessionManager';
+const { ccclass } = _decorator;
+
+@ccclass('CoinDrop')
+export class CoinDrop extends Component {
+  private targetWorldPos: Vec3 = new Vec3();
+
+  onEnable() {
+    // 找到 UI 里金币图标的世界坐标
+    const icon = find('Canvas/GameLevelUI/CoinNode/CoinDrop');
+    if (!icon) { this.node.destroy(); return; }
+    icon.getWorldPosition(this.targetWorldPos);
+
+    // 初始弹跳向上 80px(0.3s)→ 落下 40px(0.2s)→ 飞向目标 (0.4s)
+    const start = this.node.worldPosition.clone();
+    const bounceUp = start.clone(); bounceUp.y += 80;
+    const settle  = bounceUp.clone(); settle.y -= 40;
+
+    tween(this.node)
+      .to(0.3, { worldPosition: bounceUp }, { easing: 'quadOut' })
+      .to(0.2, { worldPosition: settle  }, { easing: 'quadIn'  })
+      .to(0.4, { worldPosition: this.targetWorldPos }, { easing: 'quadIn' })
+      .call(() => {
+         // 飞到图标后 +1 并销毁
+         const lblNode = find('Canvas/GameLevelUI/CoinNode/CoinLabel');
+         const lbl = lblNode?.getComponent(Label);
+         if (lbl) {
+           lbl.string = (parseInt(lbl.string) + 1).toString();
+         }
+         LevelSessionManager.inst.addCoins(1);   // 记账(局内金币)
+         this.node.destroy();
+      })
+      .start();
+  }
+}

+ 9 - 0
assets/scripts/CombatSystem/CoinDrop.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "e93de956-7f4d-450c-a5fa-a209788fb7bd",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 4 - 0
assets/scripts/CombatSystem/EnemyController.ts

@@ -41,6 +41,10 @@ export class EnemyController extends BaseSingleton {
     })
     public enemyContainer: Node = null;
 
+    // 金币预制体
+    @property({ type: Prefab, tooltip: '金币预制体 CoinDrop' })
+    public coinPrefab: Prefab = null;
+
     // === 生成 & 属性参数(保留需要可在内部自行设定,Inspector 不再显示) ===
     private spawnInterval: number = 3;
 

+ 14 - 3
assets/scripts/CombatSystem/EnemyInstance.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, ProgressBar, Label, Vec3, find, UITransform, Collider2D, Contact2DType, IPhysics2DContact } from 'cc';
+import { _decorator, Component, Node, ProgressBar, Label, Vec3, find, UITransform, Collider2D, Contact2DType, IPhysics2DContact, instantiate, resources, Prefab } from 'cc';
 import { sp } from 'cc';
 const { ccclass, property } = _decorator;
 
@@ -53,8 +53,6 @@ export class EnemyInstance extends Component {
     // 骨骼动画组件
     private skeleton: sp.Skeleton | null = null;
 
-    // 调试标记,避免重复输出日志
-    private _debugFlag: boolean = false;
 
     start() {
         // 初始化敌人
@@ -159,6 +157,7 @@ export class EnemyInstance extends Component {
         // 如果血量低于等于0,销毁敌人
         if (this.health <= 0 && this.state !== EnemyState.DEAD) {
             this.state = EnemyState.DEAD;
+            this.spawnCoin();
             // 进入死亡流程,禁用碰撞避免重复命中
             const col = this.getComponent(Collider2D);
             if (col) col.enabled = false;
@@ -318,4 +317,16 @@ export class EnemyInstance extends Component {
         // 若无动画直接销毁
         this.node.destroy();
     }
+
+    private spawnCoin() {
+        const ctrl = this.controller as any;              // EnemyController
+        if (!ctrl?.coinPrefab) return;
+
+        const coin = instantiate(ctrl.coinPrefab);
+
+        find('Canvas')!.addChild(coin);          // 放到 UI 层
+        const pos = new Vec3();
+        this.node.getWorldPosition(pos);         // 取死亡敌人的世界坐标
+        coin.worldPosition = pos;                // 金币就在敌人身上出现
+    }
 } 

+ 9 - 0
assets/scripts/CombatSystem/SkillSelection.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "1.2.0",
+  "importer": "directory",
+  "imported": true,
+  "uuid": "18a63d02-832d-491a-bfd0-3256bd10542d",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 37 - 0
assets/scripts/CombatSystem/SkillSelection/SkillButtonAnimator.ts

@@ -0,0 +1,37 @@
+import { _decorator, Component, Node, Vec3, tween } from 'cc';
+const { ccclass, property } = _decorator;
+
+/**
+ * SkillButtonAnimator
+ * 负责技能按钮的缩放 / 移动动画。
+ * 提供 playShrink 接口供外部调用。
+ */
+@ccclass('SkillButtonAnimator')
+export class SkillButtonAnimator extends Component {
+
+    /**
+     * 播放缩小并(可选)移动到指定目标位置的动画。
+     * @param duration 动画时长(秒)
+     * @param targetPos 目标位置(节点本地坐标系),不传则保持原位置
+     * @param onComplete 完成回调
+     */
+    public playShrink(duration: number = 0.3, targetPos?: Vec3, onComplete?: () => void) {
+        // 若目标位置存在,则先 tween 到该位置;否则只缩放
+        const props: any = { scale: new Vec3(0, 0, 0) };
+        if (targetPos) {
+            props.position = targetPos;
+        }
+
+        tween(this.node)
+            .to(duration, props, { easing: 'quadIn' })
+            .call(() => {
+                this.node.active = false; // 动画结束后隐藏节点
+                if (onComplete) {
+                    onComplete();
+                }
+            })
+            .start();
+    }
+}
+
+export default SkillButtonAnimator; 

+ 9 - 0
assets/scripts/CombatSystem/SkillSelection/SkillButtonAnimator.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "8cc2bb10-888b-48c9-b9ae-a19fe51ea5cf",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 98 - 0
assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts

@@ -0,0 +1,98 @@
+import { _decorator, Component, Node, Button, Vec3, find } from 'cc';
+import SkillButtonAnimator from './SkillButtonAnimator';
+import { GameManager } from '../../LevelSystem/GameManager';
+const { ccclass, property } = _decorator;
+
+/**
+ * SkillSelectionController
+ * 放在 Canvas/SelectSkillUI 节点上。
+ * 负责监听技能按钮点击,并按顺序播放缩小动画,最后关闭整个 SelectSkillUI。
+ */
+@ccclass('SkillSelectionController')
+export class SkillSelectionController extends Component {
+
+    @property({ type: [Node], tooltip: '技能按钮节点列表,留空则自动从 SkillsContainer 获取' })
+    public skillButtons: Node[] = [];
+
+    @property({ tooltip: '缩小动画时长(秒)' })
+    public shrinkDuration: number = 0.25;
+
+    private _clicked = false;
+
+    start() {
+        // 自动收集按钮
+        if (this.skillButtons.length === 0) {
+            const container = this.node.getChildByName('SkillsContainer');
+            if (container) {
+                this.skillButtons = container.children;
+            }
+        }
+
+        // 绑定点击事件
+        this.skillButtons.forEach(btn => {
+            const buttonComp = btn.getComponent(Button);
+            if (buttonComp) {
+                buttonComp.node.on(Button.EventType.CLICK, () => this.onSkillSelected(btn));
+            } else {
+                // 兜底:直接监听触摸
+                btn.on(Node.EventType.TOUCH_END, () => this.onSkillSelected(btn));
+            }
+        });
+    }
+
+    /**
+     * 玩家选择某技能按钮
+     */
+    private onSkillSelected(selectedBtn: Node) {
+        if (this._clicked) return; // 只允许一次
+        this._clicked = true;
+
+        // 禁用所有按钮交互
+        this.skillButtons.forEach(btn => {
+            const b = btn.getComponent(Button);
+            if (b) b.interactable = false;
+        });
+
+        const otherBtns = this.skillButtons.filter(btn => btn !== selectedBtn);
+        let finishedOthers = 0;
+
+        // 所有其他按钮完成动画后,再收缩选中按钮
+        const onOtherFinished = () => {
+            finishedOthers++;
+            if (finishedOthers >= otherBtns.length) {
+                // 收缩选中按钮
+                const selAnim = selectedBtn.getComponent(SkillButtonAnimator);
+                selAnim?.playShrink(this.shrinkDuration, undefined, () => {
+                    // 关闭整个 UI
+                    this.node.active = false;
+
+                    // 恢复游戏并重置能量
+                    const gm = this.getGameManager();
+                    if (gm) {
+                        gm.resetEnergy();
+                        gm.resumeGame();
+                    }
+                });
+            }
+        };
+
+        // 播放其他按钮动画
+        const targetPos = selectedBtn.position.clone();
+        otherBtns.forEach(btn => {
+            const anim = btn.getComponent(SkillButtonAnimator);
+            anim?.playShrink(this.shrinkDuration, targetPos, onOtherFinished);
+        });
+    }
+
+    private _gameManager: GameManager = null;
+    private getGameManager(): GameManager {
+        if (this._gameManager && this._gameManager.isValid) return this._gameManager;
+        const gmNode = find('GameManager');
+        if (gmNode) {
+            this._gameManager = gmNode.getComponent(GameManager);
+        }
+        return this._gameManager;
+    }
+}
+
+export default SkillSelectionController; 

+ 9 - 0
assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts.meta

@@ -0,0 +1,9 @@
+{
+  "ver": "4.0.24",
+  "importer": "typescript",
+  "imported": true,
+  "uuid": "af49d5f6-bfef-4bcf-9fbd-61f2f793da39",
+  "files": [],
+  "subMetas": {},
+  "userData": {}
+}

+ 9 - 0
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -53,6 +53,12 @@ export interface BulletInitData {
 
 @ccclass('WeaponBullet')
 export class WeaponBullet extends Component {
+    /* ========= 全局开关:是否允许生成子弹 ========= */
+    private static shootingEnabled: boolean = true;
+    public static setShootingEnabled(enable: boolean) {
+        WeaponBullet.shootingEnabled = enable;
+    }
+
     // === 子模块组件 ===
     private bulletTrajectory: BulletTrajectory = null;
     private bulletHitEffect: BulletHitEffect = null;
@@ -113,6 +119,9 @@ export class WeaponBullet extends Component {
      * 创建多发子弹
      */
     public static createBullets(initData: BulletInitData, bulletPrefab: Node): Node[] {
+        // 关卡备战或其他情况下可关闭生成
+        if (!WeaponBullet.shootingEnabled) return [];
+
         // 获取武器配置
         const config = initData.weaponConfig || WeaponBullet.getWeaponConfig(initData.weaponId);
         if (!config) {

+ 116 - 19
assets/scripts/LevelSystem/GameManager.ts

@@ -1,4 +1,4 @@
-import { _decorator, Component, Node, Prefab, instantiate, Vec3, find, director, Canvas, UITransform, Button, Label, EPhysics2DDrawFlags, sys } from 'cc';
+import { _decorator, Component, Node, Prefab, instantiate, Vec3, find, director, Canvas, UITransform, Button, Label, ProgressBar, EPhysics2DDrawFlags, sys } from 'cc';
 import { LevelManager } from './LevelManager';
 import { LevelConfigManager } from './LevelConfigManager';
 import { SaveDataManager } from './SaveDataManager';
@@ -74,6 +74,19 @@ export class GameManager extends Component {
     })
     public gameDefeatUI: Node = null;
 
+    // === 能量与技能选择 UI ===
+    @property({
+        type: Node,
+        tooltip: '拖拽 EnergyBar (ProgressBar) 节点到这里'
+    })
+    public energyBarNode: Node = null;
+
+    @property({
+        type: Node,
+        tooltip: '拖拽 SelectSkillUI 节点到这里'
+    })
+    public selectSkillUI: Node = null;
+
     // === 游戏配置属性 ===
     // 墙体基础血量由存档决定,不再通过属性面板设置
     private wallHealth: number = 100;
@@ -121,6 +134,13 @@ export class GameManager extends Component {
         bottom: 0
     };
 
+    private preparingNextWave = false;
+
+    // 能量系统
+    private energyPoints: number = 0;
+    private readonly ENERGY_MAX: number = 5;
+    private energyBar: ProgressBar = null;
+
     start() {
         // 初始化物理系统
         this.initPhysicsSystem();
@@ -275,6 +295,27 @@ export class GameManager extends Component {
         if (this.gameDefeatUI) {
             this.gameDefeatUI.active = false;
         }
+
+        // 查找能量条
+        if (!this.energyBarNode) {
+            this.energyBarNode = find('Canvas/GameLevelUI/EnergyBar');
+        }
+
+        if (this.energyBarNode) {
+            this.energyBar = this.energyBarNode.getComponent(ProgressBar);
+            if (this.energyBar) {
+                this.energyBar.progress = 0;
+            }
+        }
+
+        // 查找技能选择 UI
+        if (!this.selectSkillUI) {
+            this.selectSkillUI = find('Canvas/SelectSkillUI');
+        }
+
+        if (this.selectSkillUI) {
+            this.selectSkillUI.active = false;
+        }
     }
 
     // === 查找敌人控制器 ===
@@ -442,7 +483,7 @@ export class GameManager extends Component {
     }
 
     // === 恢复游戏 ===
-    private resumeGame() {
+    public resumeGame() {
         this.currentState = GameState.PLAYING;
         this.gameStarted = true;
         
@@ -705,17 +746,24 @@ export class GameManager extends Component {
 
     // === 原GameManager方法 ===
     public onConfirmButtonClicked() {
-        if (this.blockSelectionUI) {
-            this.blockSelectionUI.active = false;
-            
-            const gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
-            if (gridContainer) {
-                gridContainer.active = true;
-            }
-            
-            this.preservePlacedBlocks();
+        if (this.blockSelectionUI) this.blockSelectionUI.active = false;
+
+        if (this.preparingNextWave) {
+            // 进入下一波
+            this.preparingNextWave = false;
+            this.enemyController.showStartWavePromptUI();  // 弹 startWaveUI 后自动 startGame()
+            this.nextWave();                               // 更新 wave 计数 & total
+            return;
         }
 
+        // ---------- 首波逻辑(原有代码) ----------
+        const gridContainer = find('Canvas/GameLevelUI/GameArea/GridContainer');
+        if (gridContainer) {
+            gridContainer.active = true;
+        }
+        
+        this.preservePlacedBlocks();
+
         this.startGame();
     }
     
@@ -799,7 +847,16 @@ export class GameManager extends Component {
         this.enemiesKilled = 0;
         this.currentWave = 1;
         this.currentWaveEnemyCount = 0;
-        
+
+        // 重置能量条
+        this.energyPoints = 0;
+        if (this.energyBar) {
+            this.energyBar.progress = 0;
+        }
+        if (this.selectSkillUI) {
+            this.selectSkillUI.active = false;
+        }
+ 
         // 关闭胜利/失败界面,确保重新进入时是正常状态
         if (this.gameSuccessUI) {
             this.gameSuccessUI.active = false;
@@ -969,6 +1026,9 @@ export class GameManager extends Component {
                 const firstWaveEnemies = this.levelWaves.length > 0 && this.levelWaves[0].enemies ?
                     this.levelWaves[0].enemies.reduce((t: number, g: any) => t + (g.count || 0), 0) : 0;
                 this.enemyController.startWave(1, totalWaves, firstWaveEnemies);
+
+                // 同步 GameManager 当前波敌人数,避免剩余敌人数计算出错
+                this.setCurrentWave(1, firstWaveEnemies);
             }
         }
     }
@@ -1063,13 +1123,7 @@ export class GameManager extends Component {
 
     /** 显示下一波提示并在短暂延迟后开始下一波 */
     private showNextWavePrompt() {
-        if (this.enemyController && this.enemyController.showStartWavePromptUI) {
-            this.enemyController.showStartWavePromptUI();
-        }
-        // 2 秒后开始下一波
-        this.scheduleOnce(() => {
-            this.nextWave();
-        }, 2);
+        this.openBlockSelectionUIForNextWave();   // 只打开布阵,不弹 StartWaveUI
     }
 
     /** 敌人被消灭时由 EnemyController 调用 */
@@ -1077,6 +1131,9 @@ export class GameManager extends Component {
         this.enemiesKilled++;
         // 当前波击杀 +1
         this.currentWaveEnemyCount++;
+
+        // 增加能量点
+        this.incrementEnergy();
         
         const remaining = this.currentWaveTotalEnemies - this.currentWaveEnemyCount;
         if (remaining <= 0) {
@@ -1091,6 +1148,38 @@ export class GameManager extends Component {
         }
     }
 
+    /** 每击杀敌人调用,能量 +1,并更新进度条。满值时弹出技能选择界面 */
+    private incrementEnergy() {
+        this.energyPoints = Math.min(this.energyPoints + 1, this.ENERGY_MAX);
+        this.updateEnergyBar();
+
+        if (this.energyPoints >= this.ENERGY_MAX) {
+            this.onEnergyFull();
+        }
+    }
+
+    /** 更新能量条显示 */
+    private updateEnergyBar() {
+        if (this.energyBar) {
+            this.energyBar.progress = this.energyPoints / this.ENERGY_MAX;
+        }
+    }
+
+    /** 能量满时触发 */
+    private onEnergyFull() {
+        if (this.selectSkillUI && !this.selectSkillUI.active) {
+            // 暂停游戏后再弹出 UI
+            this.pauseGame();
+            this.selectSkillUI.active = true;
+        }
+    }
+
+    /** 供外部调用:重置能量值并刷新显示 */
+    public resetEnergy() {
+        this.energyPoints = 0;
+        this.updateEnergyBar();
+    }
+
     /* ========= 墙体血量 / 等级相关 ========= */
 
     private wallHpMap: Record<number, number> = {
@@ -1146,4 +1235,12 @@ export class GameManager extends Component {
             nextHp: this.getWallHealthByLevel(newLvl + 1)
         };
     }
+
+    private openBlockSelectionUIForNextWave() {
+        if (this.blockSelectionUI) this.blockSelectionUI.active = true;
+        const grid = find('Canvas/GameLevelUI/GameArea/GridContainer');
+        if (grid) grid.active = true;
+        this.preparingNextWave = true;
+        this.enemyController.pauseSpawning();   // 保险
+    }
 } 

部分文件因为文件数量过多而无法显示