397 lines
9.0 KiB
Markdown
397 lines
9.0 KiB
Markdown
|
|
# 应用状态机 (App/AppStatus)
|
||
|
|
|
||
|
|
## 📋 模块概述
|
||
|
|
管理应用的整体状态流转,包括启动、登录、游戏等状态,基于 Framework/FSM 实现。
|
||
|
|
|
||
|
|
## 🎯 核心特性
|
||
|
|
- ✅ 应用状态流转管理
|
||
|
|
- ✅ 网络初始化
|
||
|
|
- ✅ 登录流程
|
||
|
|
- ✅ 游戏状态管理
|
||
|
|
- ✅ 状态生命周期
|
||
|
|
- ✅ 服务器消息监听
|
||
|
|
|
||
|
|
## 🗂️ 文件结构
|
||
|
|
|
||
|
|
```
|
||
|
|
App/AppStatus/
|
||
|
|
├── AppStatusManager.ts # 应用状态管理器
|
||
|
|
├── AppStatusBoot.ts # 启动状态
|
||
|
|
├── AppStatusLogin.ts # 登录状态
|
||
|
|
└── AppStatusGame.ts # 游戏状态
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🔄 状态流转图
|
||
|
|
|
||
|
|
```
|
||
|
|
[启动 Boot] → [登录 Login] → [游戏 Game]
|
||
|
|
↑ ↓ ↓
|
||
|
|
└────────────┴──────────────┘
|
||
|
|
(退出/死亡返回登录)
|
||
|
|
```
|
||
|
|
|
||
|
|
## 📘 核心类详解
|
||
|
|
|
||
|
|
### AppStatusManager - 应用状态管理器
|
||
|
|
|
||
|
|
**职责**: 管理应用的整体状态流转
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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 - 启动状态
|
||
|
|
|
||
|
|
**职责**: 初始化网络管理器并连接服务器
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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 - 登录状态
|
||
|
|
|
||
|
|
**职责**: 显示登录界面,处理登录逻辑
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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 - 游戏状态
|
||
|
|
|
||
|
|
**职责**: 游戏主循环,处理游戏逻辑和服务器消息
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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. 启动应用
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
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. 切换状态
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { AppStatusManager } from './App/AppStatus/AppStatusManager';
|
||
|
|
|
||
|
|
// 从登录状态切换到游戏状态
|
||
|
|
AppStatusManager.getInstance().changeState('Game', {
|
||
|
|
player: playerInfo,
|
||
|
|
isNewPlayer: false
|
||
|
|
});
|
||
|
|
|
||
|
|
// 从游戏状态返回登录状态
|
||
|
|
AppStatusManager.getInstance().changeState('Login');
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 获取当前状态
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 获取当前状态名称
|
||
|
|
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 状态
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 配置网络
|
||
|
|
const config: NetConfig = {
|
||
|
|
serverUrl: 'http://localhost:3000',
|
||
|
|
timeout: 30000,
|
||
|
|
autoReconnect: true,
|
||
|
|
reconnectInterval: 3000,
|
||
|
|
maxReconnectTimes: 5
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### Login 状态
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 由 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 状态
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 监听服务器广播
|
||
|
|
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
|
||
|
|
|
||
|
|
## 🔍 调试技巧
|
||
|
|
|
||
|
|
### 状态切换日志
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// BaseState 会自动输出状态切换日志
|
||
|
|
// [FSM] Enter state: Boot
|
||
|
|
// [FSM] Exit state: Boot
|
||
|
|
// [FSM] Enter state: Login
|
||
|
|
```
|
||
|
|
|
||
|
|
### 网络状态日志
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// NetManager 会输出网络状态日志
|
||
|
|
// [NetManager] 网络已连接
|
||
|
|
// [NetManager] 网络已断开
|
||
|
|
// [NetManager] 正在重连...
|
||
|
|
```
|
||
|
|
|
||
|
|
### 游戏状态信息
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 在 Game 状态中查看玩家信息
|
||
|
|
const gameState = AppStatusManager.getInstance()
|
||
|
|
.getCurrentState() as AppStatusGame;
|
||
|
|
console.log('玩家信息:', gameState._player);
|
||
|
|
console.log('是否新玩家:', gameState._isNewPlayer);
|
||
|
|
```
|
||
|
|
|
||
|
|
## 💡 最佳实践
|
||
|
|
|
||
|
|
1. **单一入口**: 通过 AppStatusManager 统一管理所有状态切换
|
||
|
|
2. **参数传递**: 使用 params 在状态间传递必要的数据
|
||
|
|
3. **资源管理**: 每个状态负责自己的资源加载和释放
|
||
|
|
4. **错误处理**: 网络错误时提供友好的提示并返回合适的状态
|
||
|
|
5. **状态独立**: 每个状态保持独立,避免直接依赖其他状态
|
||
|
|
|
||
|
|
## 🎯 扩展状态
|
||
|
|
|
||
|
|
### 添加新状态
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 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
|
||
|
|
```
|
||
|
|
|
||
|
|
## 📚 相关文档
|
||
|
|
|
||
|
|
- [Framework/FSM README](../../Framework/FSM/README.md) - 状态机框架
|
||
|
|
- [Framework/Net README](../../Framework/Net/README.md) - 网络通信
|
||
|
|
- [App/Login README](../Login/README.md) - 登录模块
|