Files
2025-12-14 22:41:10 +08:00

9.0 KiB

应用状态机 (App/AppStatus)

📋 模块概述

管理应用的整体状态流转,包括启动、登录、游戏等状态,基于 Framework/FSM 实现。

🎯 核心特性

  • 应用状态流转管理
  • 网络初始化
  • 登录流程
  • 游戏状态管理
  • 状态生命周期
  • 服务器消息监听

🗂️ 文件结构

App/AppStatus/
├── AppStatusManager.ts   # 应用状态管理器
├── AppStatusBoot.ts      # 启动状态
├── AppStatusLogin.ts     # 登录状态
└── AppStatusGame.ts      # 游戏状态

🔄 状态流转图

[启动 Boot] → [登录 Login] → [游戏 Game]
     ↑            ↓              ↓
     └────────────┴──────────────┘
         (退出/死亡返回登录)

📘 核心类详解

AppStatusManager - 应用状态管理器

职责: 管理应用的整体状态流转

class AppStatusManager {
    private _fsm: FSM;    // 底层状态机
    
    // 获取单例
    static getInstance(): AppStatusManager;
    
    // 启动应用(从 Boot 状态开始)
    start(): void;
    
    // 切换状态
    changeState(stateName: string, params?: any): void;
    
    // 获取当前状态名称
    getCurrentStateName(): string | null;
    
    // 获取当前状态实例
    getCurrentState(): any;
    
    // 更新状态机(在主循环中调用)
    update(dt: number): void;
    
    // 获取底层 FSM 实例
    getFSM(): FSM;
    
    // 销毁管理器
    destroy(): void;
}

AppStatusBoot - 启动状态

职责: 初始化网络管理器并连接服务器

class AppStatusBoot extends BaseState {
    constructor(fsm: FSM);
    
    // 进入启动状态
    async onEnter(params?: any): Promise<void>;
    
    // 初始化并连接网络
    private async initAndConnectNet(): Promise<void>;
    
    // 退出启动状态
    onExit(): void;
}

执行流程:

  1. 设置服务协议
  2. 初始化网络配置
  3. 监听网络事件
  4. 连接服务器
  5. 连接成功后切换到登录状态

AppStatusLogin - 登录状态

职责: 显示登录界面,处理登录逻辑

class AppStatusLogin extends BaseState {
    constructor(fsm: FSM);
    
    // 进入登录状态
    async onEnter(params?: any): Promise<void>;
    
    // 显示登录 UI
    private async showLoginUI(): Promise<void>;
    
    // 退出登录状态
    onExit(): void;
}

执行流程:

  1. 通过 UIMgr 加载并显示登录 UI
  2. 等待用户输入账号并点击登录
  3. UILogin 调用 NetManager 发送登录请求
  4. 登录成功后切换到游戏状态

AppStatusGame - 游戏状态

职责: 游戏主循环,处理游戏逻辑和服务器消息

class AppStatusGame extends BaseState {
    private _player: any;              // 玩家信息
    private _isNewPlayer: boolean;     // 是否新玩家
    private _isPaused: boolean;        // 是否暂停
    
    constructor(fsm: FSM);
    
    // 进入游戏状态
    async onEnter(params?: any): Promise<void>;
    
    // 加载游戏场景
    private async loadGameScene(): Promise<void>;
    
    // 初始化游戏
    private async initGame(): Promise<void>;
    
    // 监听服务器广播消息
    private listenServerMessages(): void;
    
    // 开始游戏
    private startGame(): void;
    
    // 游戏主循环
    onUpdate(dt: number): void;
    
    // 暂停游戏
    pauseGame(): void;
    
    // 恢复游戏
    resumeGame(): void;
    
    // 玩家死亡
    private onPlayerDeath(): void;
    
    // 退出游戏(返回登录)
    quitGame(): void;
    
    // 退出游戏状态
    onExit(): void;
}

执行流程:

  1. 接收玩家信息
  2. 加载游戏场景
  3. 初始化玩家角色
  4. 监听服务器广播消息
  5. 开始游戏主循环
  6. 处理游戏逻辑和网络消息

📝 使用指南

1. 启动应用

import { AppStatusManager } from './App/AppStatus/AppStatusManager';

// 在 Boot 组件中启动
@ccclass('Boot')
export class Boot extends Component {
    start() {
        // 获取管理器实例
        const appManager = AppStatusManager.getInstance();
        
        // 启动应用
        appManager.start();
    }
    
    update(deltaTime: number) {
        // 在主循环中更新状态机
        AppStatusManager.getInstance().update(deltaTime);
    }
}

2. 切换状态

import { AppStatusManager } from './App/AppStatus/AppStatusManager';

// 从登录状态切换到游戏状态
AppStatusManager.getInstance().changeState('Game', {
    player: playerInfo,
    isNewPlayer: false
});

// 从游戏状态返回登录状态
AppStatusManager.getInstance().changeState('Login');

3. 获取当前状态

// 获取当前状态名称
const stateName = AppStatusManager.getInstance().getCurrentStateName();
console.log(`当前状态: ${stateName}`);

// 获取当前状态实例
const currentState = AppStatusManager.getInstance().getCurrentState();

🔄 完整流程示例

启动到游戏的完整流程

1. Boot 组件启动
    ↓
2. AppStatusManager.start()
    ↓
3. 切换到 Boot 状态
    ↓
4. 初始化网络并连接服务器
    ↓
5. 连接成功,切换到 Login 状态
    ↓
6. 加载并显示登录 UI
    ↓
7. 用户输入账号并点击登录
    ↓
8. 调用 Login API
    ↓
9. 登录成功,切换到 Game 状态
    ↓
10. 加载游戏场景
    ↓
11. 初始化玩家角色
    ↓
12. 监听服务器广播消息
    ↓
13. 开始游戏主循环

📡 网络协议使用

Boot 状态

// 配置网络
const config: NetConfig = {
    serverUrl: 'http://localhost:3000',
    timeout: 30000,
    autoReconnect: true,
    reconnectInterval: 3000,
    maxReconnectTimes: 5
};

Login 状态

// 由 UILogin 调用登录 API
const result = await netManager.callApi<ReqLogin, ResLogin>('Login', {
    account: account
});

// 登录成功后切换状态
if (result && result.success) {
    AppStatusManager.getInstance().changeState('Game', {
        player: result.player,
        isNewPlayer: result.isNewPlayer
    });
}

Game 状态

// 监听服务器广播
netManager.listenMsg<MsgPlayerJoin>('PlayerJoin', (msg) => {
    console.log(`玩家 ${msg.playerName} 加入游戏`);
    // 在场景中创建其他玩家
});

netManager.listenMsg<MsgPlayerMove>('PlayerMove', (msg) => {
    console.log(`玩家 ${msg.playerName} 移动`);
    // 更新其他玩家位置
});

// 发送 API
const result = await netManager.callApi<ReqMove, ResMove>('Move', {
    x: targetX,
    y: targetY
});

⚠️ 注意事项

  1. 状态切换参数: 切换到 Game 状态时必须传递 player 参数
  2. 网络初始化: Boot 状态必须在网络连接成功后才能切换状态
  3. 资源清理: 切换状态时注意清理前一个状态的资源
  4. 消息监听: Game 状态退出时要取消所有服务器消息监听
  5. 异步处理: 状态的 onEnter 方法是异步的,注意使用 await

🔍 调试技巧

状态切换日志

// BaseState 会自动输出状态切换日志
// [FSM] Enter state: Boot
// [FSM] Exit state: Boot
// [FSM] Enter state: Login

网络状态日志

// NetManager 会输出网络状态日志
// [NetManager] 网络已连接
// [NetManager] 网络已断开
// [NetManager] 正在重连...

游戏状态信息

// 在 Game 状态中查看玩家信息
const gameState = AppStatusManager.getInstance()
    .getCurrentState() as AppStatusGame;
console.log('玩家信息:', gameState._player);
console.log('是否新玩家:', gameState._isNewPlayer);

💡 最佳实践

  1. 单一入口: 通过 AppStatusManager 统一管理所有状态切换
  2. 参数传递: 使用 params 在状态间传递必要的数据
  3. 资源管理: 每个状态负责自己的资源加载和释放
  4. 错误处理: 网络错误时提供友好的提示并返回合适的状态
  5. 状态独立: 每个状态保持独立,避免直接依赖其他状态

🎯 扩展状态

添加新状态

// 1. 创建新状态类
class AppStatusLobby extends BaseState {
    constructor(fsm: FSM) {
        super(fsm, "Lobby");
    }
    
    onEnter(params?: any): void {
        super.onEnter(params);
        console.log("[AppStatusLobby] 进入大厅");
        // 显示大厅 UI
    }
    
    onExit(): void {
        super.onExit();
        // 清理大厅资源
    }
}

// 2. 在 AppStatusManager 中注册
private initStates(): void {
    this._fsm.addState(new AppStatusBoot(this._fsm));
    this._fsm.addState(new AppStatusLogin(this._fsm));
    this._fsm.addState(new AppStatusLobby(this._fsm));  // 新增
    this._fsm.addState(new AppStatusGame(this._fsm));
}

// 3. 修改状态流转
// Login -> Lobby -> Game

📚 相关文档