Browse Source

优化暂停

181404010226 5 months ago
parent
commit
31f82087a3

+ 7 - 0
assets/scripts/CombatSystem/BallController.ts

@@ -2,6 +2,7 @@ import { _decorator, Component, Node, Vec2, Vec3, UITransform, Collider2D, Conta
 import { PhysicsManager } from '../Core/PhysicsManager';
 import { WeaponBullet, BulletInitData, WeaponConfig } from './WeaponBullet';
 import { EnemyController } from './EnemyController';
+import { GamePause } from './GamePause';
 const { ccclass, property } = _decorator;
 
 @ccclass('BallController')
@@ -488,6 +489,12 @@ export class BallController extends Component {
         if (!enemyController || !enemyController.hasActiveEnemies()) {
             return;
         }
+
+        // 检查游戏是否暂停,暂停时不发射子弹
+        const gamePause = GamePause.getInstance();
+        if (gamePause && !gamePause.isBulletFireEnabled()) {
+            return;
+        }
         
         // 判断哪个是小球,哪个是方块
         let ballNode: Node = null;

+ 322 - 0
assets/scripts/CombatSystem/GamePause.ts

@@ -0,0 +1,322 @@
+import { _decorator, Component, Node, RigidBody2D, find } from 'cc';
+import { EnemyController } from './EnemyController';
+import { BallController } from './BallController';
+import EventBus, { GameEvents } from '../Core/EventBus';
+
+const { ccclass, property } = _decorator;
+
+/**
+ * 游戏暂停状态枚举
+ */
+export enum PauseState {
+    PLAYING = 'playing',
+    PAUSED = 'paused'
+}
+
+/**
+ * 游戏暂停管理器
+ * 统一管理游戏的暂停和恢复逻辑
+ */
+@ccclass('GamePause')
+export class GamePause extends Component {
+    public static _instance: GamePause = null;
+
+    // 当前暂停状态
+    private currentState: PauseState = PauseState.PLAYING;
+
+    // 暂停前保存的敌人状态
+    private pausedEnemyStates: Map<Node, {
+        velocity: any,
+        angularVelocity: number,
+        isMoving: boolean,
+        attackTimer: number
+    }> = new Map();
+
+    // 暂停前保存的小球状态
+    private pausedBallStates: Map<Node, {
+        velocity: any,
+        angularVelocity: number
+    }> = new Map();
+
+    // 是否允许小球发射子弹(暂停时禁用)
+    private bulletFireEnabled: boolean = true;
+
+    start() {
+        // 监听游戏事件
+        const eventBus = EventBus.getInstance();
+        eventBus.on(GameEvents.GAME_SUCCESS, this.onGameEnd, this);
+        eventBus.on(GameEvents.GAME_DEFEAT, this.onGameEnd, this);
+    }
+
+    /**
+     * 暂停游戏
+     */
+    public pauseGame(): void {
+        if (this.currentState === PauseState.PAUSED) {
+            return;
+        }
+
+        console.log('GamePause: 暂停游戏');
+        this.currentState = PauseState.PAUSED;
+
+        // 禁用小球发射子弹
+        this.bulletFireEnabled = false;
+
+        // 暂停敌人
+        this.pauseAllEnemies();
+
+        // 暂停敌人生成
+        this.pauseEnemySpawning();
+
+        // 小球继续运动但不发射子弹(根据新需求)
+        // 不调用 pauseBalls() 方法
+
+        // 派发暂停事件
+        EventBus.getInstance().emit(GameEvents.GAME_PAUSE);
+    }
+
+    /**
+     * 恢复游戏
+     */
+    public resumeGame(): void {
+        if (this.currentState === PauseState.PLAYING) {
+            return;
+        }
+
+        console.log('GamePause: 恢复游戏');
+        this.currentState = PauseState.PLAYING;
+
+        // 启用小球发射子弹
+        this.bulletFireEnabled = true;
+
+        // 恢复敌人
+        this.resumeAllEnemies();
+
+        // 恢复敌人生成
+        this.resumeEnemySpawning();
+
+        // 恢复小球(如果之前被暂停的话)
+        this.resumeAllBalls();
+
+        // 派发恢复事件
+        EventBus.getInstance().emit(GameEvents.GAME_RESUME);
+    }
+
+    /**
+     * 暂停所有敌人
+     */
+    private pauseAllEnemies(): void {
+        const enemyController = EnemyController.getInstance();
+        if (!enemyController) return;
+
+        const activeEnemies = enemyController.getActiveEnemies?.() || [];
+        
+        for (const enemy of activeEnemies) {
+            if (!enemy || !enemy.isValid) continue;
+
+            // 保存敌人状态
+            const rigidBody = enemy.getComponent(RigidBody2D);
+            if (rigidBody) {
+                this.pausedEnemyStates.set(enemy, {
+                    velocity: rigidBody.linearVelocity.clone(),
+                    angularVelocity: rigidBody.angularVelocity,
+                    isMoving: true,
+                    attackTimer: 0 // 可以扩展保存攻击计时器
+                });
+
+                // 停止敌人运动
+                rigidBody.linearVelocity.set(0, 0);
+                rigidBody.angularVelocity = 0;
+                rigidBody.sleep();
+            }
+
+            // 暂停敌人AI组件(如果有的话)
+            const enemyAI = enemy.getComponent('EnemyAI');
+            if (enemyAI && typeof (enemyAI as any).pause === 'function') {
+                (enemyAI as any).pause();
+            }
+
+            // 暂停敌人动画(如果有的话)
+            const animation = enemy.getComponent('Animation');
+            if (animation && typeof (animation as any).pause === 'function') {
+                (animation as any).pause();
+            }
+        }
+    }
+
+    /**
+     * 恢复所有敌人
+     */
+    private resumeAllEnemies(): void {
+        const enemyController = EnemyController.getInstance();
+        if (!enemyController) return;
+
+        const activeEnemies = enemyController.getActiveEnemies?.() || [];
+        
+        for (const enemy of activeEnemies) {
+            if (!enemy || !enemy.isValid) continue;
+
+            const savedState = this.pausedEnemyStates.get(enemy);
+            if (savedState) {
+                const rigidBody = enemy.getComponent(RigidBody2D);
+                if (rigidBody) {
+                    // 恢复敌人运动
+                    rigidBody.wakeUp();
+                    rigidBody.linearVelocity = savedState.velocity;
+                    rigidBody.angularVelocity = savedState.angularVelocity;
+                }
+            }
+
+            // 恢复敌人AI组件
+            const enemyAI = enemy.getComponent('EnemyAI');
+            if (enemyAI && typeof (enemyAI as any).resume === 'function') {
+                (enemyAI as any).resume();
+            }
+
+            // 恢复敌人动画
+            const animation = enemy.getComponent('Animation');
+            if (animation && typeof (animation as any).resume === 'function') {
+                (animation as any).resume();
+            }
+        }
+
+        // 清空保存的状态
+        this.pausedEnemyStates.clear();
+    }
+
+    /**
+     * 暂停敌人生成
+     */
+    private pauseEnemySpawning(): void {
+        const enemyController = EnemyController.getInstance();
+        if (enemyController && enemyController.pauseSpawning) {
+            enemyController.pauseSpawning();
+        }
+    }
+
+    /**
+     * 恢复敌人生成
+     */
+    private resumeEnemySpawning(): void {
+        const enemyController = EnemyController.getInstance();
+        if (enemyController && enemyController.resumeSpawning) {
+            enemyController.resumeSpawning();
+        }
+    }
+
+    /**
+     * 暂停所有小球
+     */
+    private pauseAllBalls(): void {
+        // 查找所有小球控制器
+        const ballControllers = this.findAllBallControllers();
+        
+        for (const ballController of ballControllers) {
+            if (ballController && ballController.pauseBall) {
+                ballController.pauseBall();
+            }
+        }
+    }
+
+    /**
+     * 恢复所有小球
+     */
+    private resumeAllBalls(): void {
+        // 查找所有小球控制器
+        const ballControllers = this.findAllBallControllers();
+        
+        for (const ballController of ballControllers) {
+            if (ballController && ballController.resumeBall) {
+                ballController.resumeBall();
+            }
+        }
+    }
+
+    /**
+     * 查找所有小球控制器
+     */
+    private findAllBallControllers(): BallController[] {
+        const controllers: BallController[] = [];
+        
+        // 主要的小球控制器
+        const mainBallControllerNode = find('Canvas/GameLevelUI/BallController');
+        if (mainBallControllerNode) {
+            const controller = mainBallControllerNode.getComponent(BallController);
+            if (controller) {
+                controllers.push(controller);
+            }
+        }
+
+        // 可以扩展查找其他小球控制器
+        // 例如:多球模式下的额外控制器
+
+        return controllers;
+    }
+
+    /**
+     * 游戏结束时的处理
+     */
+    private onGameEnd(): void {
+        this.pauseGame();
+    }
+
+    /**
+     * 检查是否为暂停状态
+     */
+    public isPaused(): boolean {
+        return this.currentState === PauseState.PAUSED;
+    }
+
+    /**
+     * 检查是否允许发射子弹
+     */
+    public isBulletFireEnabled(): boolean {
+        return this.bulletFireEnabled;
+    }
+
+    /**
+     * 获取当前暂停状态
+     */
+    public getCurrentState(): PauseState {
+        return this.currentState;
+    }
+
+    /**
+     * 强制设置暂停状态(谨慎使用)
+     */
+    public forceSetState(state: PauseState): void {
+        this.currentState = state;
+        this.bulletFireEnabled = (state === PauseState.PLAYING);
+    }
+
+    /**
+     * 获取单例实例
+     */
+    public static getInstance(): GamePause {
+        if (!GamePause._instance) {
+            // 创建节点和组件
+            const gamePauseNode = new Node('GamePause');
+            GamePause._instance = gamePauseNode.addComponent(GamePause);
+            
+            // 将节点添加到场景中(不销毁)
+            const scene = find('Canvas');
+            if (scene) {
+                scene.addChild(gamePauseNode);
+            }
+        }
+        return GamePause._instance;
+    }
+
+    onDestroy() {
+        // 清理事件监听
+        const eventBus = EventBus.getInstance();
+        eventBus.off(GameEvents.GAME_SUCCESS, this.onGameEnd, this);
+        eventBus.off(GameEvents.GAME_DEFEAT, this.onGameEnd, this);
+        
+        // 清空状态
+        this.pausedEnemyStates.clear();
+        this.pausedBallStates.clear();
+        
+        GamePause._instance = null;
+    }
+}

+ 35 - 38
assets/scripts/CombatSystem/SkillSelection/SkillSelectionController.ts

@@ -1,6 +1,8 @@
-import { _decorator, Component, Node, Button, Vec3, find} from 'cc';
-import SkillButtonAnimator from './SkillButtonAnimator';
+import { _decorator, Component, Node, Button, find, Label, Sprite, Vec3 } from 'cc';
 import { GameManager } from '../../LevelSystem/GameManager';
+import { SkillButtonAnimator } from './SkillButtonAnimator';
+import { GamePause } from '../GamePause';
+
 const { ccclass, property } = _decorator;
 
 /**
@@ -14,28 +16,28 @@ export class SkillSelectionController extends Component {
     @property({ type: [Node], tooltip: '技能按钮节点列表,留空则自动从 SkillsContainer 获取' })
     public skillButtons: Node[] = [];
 
-    @property({ tooltip: '缩动画时长(秒)' })
-    public shrinkDuration: number = 0.25;
+    @property({ type: Number, tooltip: '缩动画时长' })
+    public shrinkDuration: number = 0.3;
 
+    // 防止重复点击标记
     private _clicked = false;
 
     start() {
-        // 自动收集按钮
+        this.setupSkillButtons();
+    }
+
+    private setupSkillButtons() {
         if (this.skillButtons.length === 0) {
             const container = this.node.getChildByName('SkillsContainer');
             if (container) {
-                this.skillButtons = container.children;
+                this.skillButtons = container.children.slice();
             }
         }
 
-        // 绑定点击事件
-        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));
+        this.skillButtons.forEach(btnNode => {
+            const btn = btnNode.getComponent(Button);
+            if (btn) {
+                btn.node.on(Button.EventType.CLICK, () => this.onSkillSelected(btnNode), this);
             }
         });
     }
@@ -66,12 +68,17 @@ export class SkillSelectionController extends Component {
                     // 关闭整个 UI
                     this.node.active = false;
 
-                    // 恢复游戏并重置能量
+                    // 恢复游戏并重置能量 - 使用GamePause
+                    const gamePause = GamePause.getInstance();
+                    if (gamePause) {
+                        gamePause.resumeGame();
+                    }
+                    
                     const gm = this.getGameManager();
                     if (gm) {
                         gm.resetEnergy();
-                        gm.resumeGame();
                     }
+                    
                     // 关闭后立刻重置UI
                     this.resetUI();
                 });
@@ -86,35 +93,25 @@ export class SkillSelectionController extends Component {
         });
     }
 
+    /**
+     * 重置 UI 状态,供下次开启时使用
+     */
     private resetUI() {
         this._clicked = false;
+
+        // 重新启用所有按钮交互
         this.skillButtons.forEach(btn => {
-            // 恢复交互
             const b = btn.getComponent(Button);
             if (b) b.interactable = true;
-            // 恢复缩放
-            btn.setScale(Vec3.ONE);
-            // 恢复位置(如果有初始位置记录,建议用初始位置)
-            // btn.setPosition(初始位置);
-            // 恢复透明度等
-            // btn.opacity = 255;
-            // 还原动画状态
+
+            // 重置按钮动画状态
             const anim = btn.getComponent(SkillButtonAnimator);
-            anim?.resetState?.();
+            anim?.resetState();
         });
     }
 
-    private _gameManager: GameManager = null;
-    private getGameManager(): GameManager {
-        if (this._gameManager && this._gameManager.isValid) return this._gameManager;
-
-        // 尝试在 Canvas 下递归查找
-        const canvas = find('Canvas');
-        if (canvas) {
-            this._gameManager = canvas.getComponentInChildren(GameManager);
-            if (this._gameManager) return this._gameManager;
-        }
+    private getGameManager(): GameManager | null {
+        const gmNode = find('Canvas/GameLevelUI/GameManager');
+        return gmNode?.getComponent(GameManager) || null;
     }
-}
-
-export default SkillSelectionController; 
+} 

+ 37 - 7
assets/scripts/CombatSystem/WeaponBullet.ts

@@ -4,6 +4,8 @@ import { BulletTrajectory, BulletTrajectoryConfig } from './BulletEffects/Bullet
 import { BulletHitEffect, HitEffectConfig, HitResult } from './BulletEffects/BulletHitEffect';
 import { BulletLifecycle, BulletLifecycleConfig } from './BulletEffects/BulletLifecycle';
 import { ConfigManager } from '../Core/ConfigManager';
+import { EnemyController } from './EnemyController';
+import { GamePause } from './GamePause';
 
 const { ccclass, property } = _decorator;
 
@@ -122,6 +124,12 @@ export class WeaponBullet extends Component {
         // 关卡备战或其他情况下可关闭生成
         if (!WeaponBullet.shootingEnabled) return [];
 
+        // 检查游戏是否暂停,暂停时不创建子弹
+        const gamePause = GamePause.getInstance();
+        if (gamePause && !gamePause.isBulletFireEnabled()) {
+            return [];
+        }
+
         // 获取武器配置
         const config = initData.weaponConfig || WeaponBullet.getWeaponConfig(initData.weaponId);
         if (!config) {
@@ -134,7 +142,7 @@ export class WeaponBullet extends Component {
             direction = initData.direction.clone();
         } else if (initData.autoTarget) {
             // 使用 EnemyController 单例获取最近敌人,避免频繁 find
-            const enemyController = (globalThis as any).EnemyController?.getInstance?.();
+            const enemyController = EnemyController.getInstance();
             if (enemyController) {
                 const nearestEnemy = enemyController.getNearestEnemy(initData.firePosition);
                 if (nearestEnemy) {
@@ -192,20 +200,42 @@ export class WeaponBullet extends Component {
      * 初始化子弹
      */
     public init(initData: BulletInitData) {
+        // 检查游戏是否暂停,暂停时不初始化子弹
+        const gamePause = GamePause.getInstance();
+        if (gamePause && !gamePause.isBulletFireEnabled()) {
+            // 立即销毁自己
+            this.node.destroy();
+            return;
+        }
+
+        // 验证初始化数据
+        if (!WeaponBullet.validateInitData(initData)) {
+            console.error('WeaponBullet.init: 初始化数据无效');
+            this.node.destroy();
+            return;
+        }
+        
         // 获取武器配置
         this.weaponConfig = initData.weaponConfig || WeaponBullet.getWeaponConfig(initData.weaponId);
         if (!this.weaponConfig) {
-            console.error('❌ 无法获取武器配置,初始化失败');
+            console.error(`WeaponBullet.init: 无法找到武器配置 ${initData.weaponId}`);
+            this.node.destroy();
             return;
         }
         
-        // 设置位置
+        // 使用世界坐标设置位置
         this.setPositionInGameArea(initData.firePosition);
         
-        // 延迟初始化物理组件,确保位置设置完成
-        this.scheduleOnce(() => {
-            this.initializeComponents(initData);
-        }, 0.05);
+        // 初始化各个组件
+        this.initializeComponents(initData);
+        
+        // 设置子弹外观
+        this.setupBulletSprite();
+        
+        // 设置碰撞监听
+        this.setupCollisionListener();
+        
+        this.isInitialized = true;
     }
     
     /**

+ 15 - 17
assets/scripts/LevelSystem/GameManager.ts

@@ -12,6 +12,7 @@ import { BlockManager } from '../CombatSystem/BlockManager';
 import { LevelSessionManager } from '../Core/LevelSessionManager';
 import { GameStartMove } from '../Animations/GameStartMove';
 import { GameBlockSelection } from '../CombatSystem/BlockSelection/GameBlockSelection';
+import { GamePause } from '../CombatSystem/GamePause';
 const { ccclass, property } = _decorator;
 
 /**
@@ -489,14 +490,10 @@ export class GameManager extends Component {
         this.currentState = GameState.PAUSED;
         this.gameStarted = false;
         
-        if (this.enemyController && this.enemyController.pauseSpawning) {
-            this.enemyController.pauseSpawning();
-        }
-
-        // 暂停小球
-        if (this.ballController) {
-            const bc = this.ballController.getComponent(BallController);
-            bc?.pauseBall?.();
+        // 使用GamePause统一管理暂停逻辑
+        const gamePause = GamePause.getInstance();
+        if (gamePause) {
+            gamePause.pauseGame();
         }
     }
 
@@ -505,14 +502,10 @@ export class GameManager extends Component {
         this.currentState = GameState.PLAYING;
         this.gameStarted = true;
         
-        if (this.enemyController && this.enemyController.resumeSpawning) {
-            this.enemyController.resumeSpawning();
-        }
-
-        // 恢复小球
-        if (this.ballController) {
-            const bc = this.ballController.getComponent(BallController);
-            bc?.resumeBall?.();
+        // 使用GamePause统一管理恢复逻辑
+        const gamePause = GamePause.getInstance();
+        if (gamePause) {
+            gamePause.resumeGame();
         }
 
         if (this.gameSuccessUI) {
@@ -1135,7 +1128,12 @@ export class GameManager extends Component {
         if (this.blockSelectionComponent) {
             this.blockSelectionComponent.showBlockSelection(true);
             this.preparingNextWave = true;
-            this.enemyController.pauseSpawning();   // 保险
+            
+            // 使用GamePause来暂停游戏
+            const gamePause = GamePause.getInstance();
+            if (gamePause) {
+                gamePause.pauseGame();
+            }
         }
     }