Files
rougelike-demo/client/.github/instructions/AppStatus-Guide.md
2025-12-14 22:37:49 +08:00

7.6 KiB

AppStatus 应用状态机使用指南

📚 概述

AppStatus 是基于 FSM 状态机实现的应用级状态管理系统,负责管理整个游戏的状态流转。

简化后的流程: Boot(连接) → Login(登录) → Game(游戏世界)

🏗️ 项目结构

assets/scripts/
├── Boot/
│   └── Boot.ts                    # 启动组件(挂载到场景)
├── App/
│   └── AppStatus/
│       ├── AppStatusBoot.ts       # 启动状态(连接服务器)
│       ├── AppStatusLogin.ts      # 登录状态(调用Login API)
│       ├── AppStatusGame.ts       # 游戏状态(监听广播)
│       └── AppStatusManager.ts    # 状态管理器
├── Framework/
│   ├── FSM/                       # 状态机框架
│   └── Net/                       # 网络通信框架
└── Shared/
    └── protocols/                 # TSRPC协议定义

🚀 快速开始

1. 在场景中使用

  1. 打开 Cocos Creator
  2. 打开主场景 assets/scenes/main.scene
  3. 创建一个空节点,命名为 "Boot"
  4. assets/scripts/Boot/Boot.ts 拖拽到 Boot 节点上
  5. 运行游戏

2. 状态流转

应用启动后会自动按以下顺序流转:

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

## 📋 各状态详解

### Boot 启动状态

**触发时机**: 应用启动时
**主要工作**:
- 初始化网络管理器
- 连接服务器

**完成后**: 自动切换到 Login 状态

### Login 登录状态

**触发时机**: Boot 状态完成后
**主要工作**:
- 显示登录界面
- 等待用户输入玩家ID
- 调用 Login API 登录

**如何触发登录**:
```typescript
// 在登录UI中调用
const loginState = AppStatusManager.getInstance()
    .getCurrentState() as AppStatusLogin;
await loginState.login("player123", "玩家昵称");

协议:

  • API: Login
  • 请求: { playerId: string, playerName?: string }
  • 响应: { success: boolean, player: PlayerInfo, isNewPlayer: boolean }

完成后: 登录成功后直接进入 Game 状态

Game 游戏状态

触发时机: 登录成功后 主要工作:

  • 接收登录返回的玩家信息(PlayerInfo)
  • 加载游戏场景
  • 创建玩家角色
  • 监听服务器广播消息
  • 运行游戏主循环

监听的服务器广播:

// PlayerJoin - 其他玩家加入
NetManager.getInstance().listenMsg("PlayerJoin", (msg) => {
    console.log(`${msg.playerName} 加入了游戏`);
    // 在场景中创建其他玩家
});

// PlayerMove - 其他玩家移动
NetManager.getInstance().listenMsg("PlayerMove", (msg) => {
    console.log(`${msg.playerName} 移动到 (${msg.position.x}, ${msg.position.y})`);
    // 更新其他玩家位置
});

// Chat - 聊天消息
NetManager.getInstance().listenMsg("Chat", (msg) => {
    console.log(`${msg.playerName}: ${msg.content}`);
    // 显示聊天内容
});

可用操作:

const gameState = AppStatusManager.getInstance()
    .getCurrentState() as AppStatusGame;

// 移动(调用Move API)
await NetManager.getInstance().callApi("Move", { x: 10, y: 5 });

// 发送聊天(调用Send API)
await NetManager.getInstance().callApi("Send", { content: "Hello!" });

// 暂停/恢复游戏
gameState.pauseGame();
gameState.resumeGame();

// 玩家死亡
gameState.onPlayerDeath();

// 退出游戏(返回登录)
gameState.quitGame();

🎮 AppStatusManager API

获取管理器实例

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

const appManager = AppStatusManager.getInstance();

主要方法

start()

启动应用,从 Boot 状态开始

appManager.start();

changeState(stateName, params?)

手动切换状态

// 切换到登录状态
appManager.changeState("Login");

// 切换到大厅状态并传递用户信息
appManager.changeState("Lobby", { 
    userInfo: { userId: "123", username: "玩家" }
});

// 切换到游戏状态
appManager.changeState("Game", { 
    roomId: "room123",
    userInfo: currentUser 
});

getCurrentStateName()

获取当前状态名称

const stateName = appManager.getCurrentStateName();
console.log(`当前状态: ${stateName}`); // 输出: "Login"

getCurrentState()

获取当前状态实例

const currentState = appManager.getCurrentState();
if (currentState) {
    console.log(`当前状态: ${currentState.name}`);
}

update(dt)

更新状态机(在游戏主循环中调用)

// Boot.ts 中已自动处理
update(deltaTime: number) {
    this._appStatusManager.update(deltaTime);
}

🔧 扩展新状态

如果需要添加新的应用状态:

1. 创建状态类

import { BaseState } from "../../Framework/FSM/BaseState";

export class AppStatusNewState extends BaseState {
    constructor(fsm: any) {
        super(fsm, "NewState");
    }
    
    onEnter(params?: any): void {
        super.onEnter(params);
        // 进入状态的逻辑
    }
    
    onExit(): void {
        super.onExit();
        // 退出状态的逻辑
    }
}

2. 在 AppStatusManager 中注册

// AppStatusManager.ts
import { AppStatusNewState } from "./AppStatusNewState";

private initStates(): void {
    // ... 其他状态
    this._fsm.addState(new AppStatusNewState(this._fsm));
}

3. 从其他状态切换

this._fsm.changeState("NewState", { /* 参数 */ });

💡 最佳实践

1. 状态间传递数据

使用 changeState 的第二个参数传递数据:

// 传递数据
this._fsm.changeState("Lobby", { 
    userInfo: { userId: "123", username: "玩家" }
});

// 接收数据
onEnter(params?: any): void {
    if (params && params.userInfo) {
        this._userInfo = params.userInfo;
    }
}

2. 保持状态独立

每个状态应该是独立的,不要在状态间共享可变数据,而是通过参数传递。

3. 异步操作处理

在状态的 onEnter 中处理异步操作:

async onEnter(params?: any): Promise<void> {
    super.onEnter(params);
    
    try {
        await this.loadSomething();
        this._fsm.changeState("NextState");
    } catch (error) {
        console.error("加载失败:", error);
        // 处理错误
    }
}

4. 状态清理

始终在 onExit 中清理资源:

onExit(): void {
    super.onExit();
    // 清理UI
    // 取消网络监听
    // 释放资源
}

🐛 调试技巧

1. 查看状态流转日志

所有状态切换都会在控制台输出日志:

[FSM] 进入状态: Boot
[AppStatusBoot] 开始初始化应用...
[FSM] 退出状态: Boot
[FSM] 进入状态: Loading

2. 查看当前状态

console.log("当前状态:", AppStatusManager.getInstance().getCurrentStateName());

3. 直接切换到指定状态(测试用)

// 跳过登录直接进入大厅
AppStatusManager.getInstance().changeState("Lobby", { 
    userInfo: { userId: "test", username: "测试用户" }
});

📝 TODO 清单

当前 AppStatus 模块已完成基础框架,但以下功能需要后续实现:

  • 集成 UI 管理器
  • 实现实际的服务器连接
  • 实现资源预加载
  • 实现房间系统
  • 实现匹配系统
  • 实现游戏场景加载
  • 添加状态转换动画
  • 添加错误处理和重试机制

🎯 下一步

  1. 实现 UI 系统
  2. 创建登录界面
  3. 连接服务器
  4. 实现房间和匹配功能
  5. 开发游戏主逻辑

更新时间: 2025-12-14