feat: isolate lobby during minigames
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
31afce6882
commit
6a5e534d00
@@ -0,0 +1,194 @@
|
||||
# MiniGame Lobby Stage Isolation Design
|
||||
|
||||
## 目标
|
||||
|
||||
进入小游戏后,大厅必须进入暂停并隐藏状态:大厅操作界面关闭或不可交互,大厅场景和单位不再渲染,大厅输入和大厅 Tick 停止,直到小游戏完全退出后再恢复大厅。这样大厅和小游戏成为两个互斥阶段,避免大厅 UI、输入、场景渲染和小游戏同时存在导致遮挡、误操作或性能浪费。
|
||||
|
||||
本设计采用“暂停并隐藏大厅”,不销毁大厅对象。原因是当前大厅/小游戏链路复用同一 `XWebSocket`,小游戏结束后要快速回到大厅;销毁重建大厅会引入额外状态恢复和网络重连复杂度。
|
||||
|
||||
## 范围
|
||||
|
||||
本设计覆盖:
|
||||
|
||||
- 进入小游戏前暂停大厅 UI、输入、Tick 和渲染。
|
||||
- 小游戏运行期间保持大厅不可见、不可操作。
|
||||
- 小游戏退出或进入失败时恢复大厅。
|
||||
- 保持 `MiniGameHost` 只负责小游戏生命周期,不让它直接依赖大厅对象。
|
||||
- 先支持 `PcLobbySmokeLauncher` 链路,后续正式大厅入口复用同一隔离组件。
|
||||
|
||||
不覆盖:
|
||||
|
||||
- 销毁并重新加载完整大厅场景。
|
||||
- 大厅业务状态持久化重建。
|
||||
- 小游戏内部 UI 设计。
|
||||
|
||||
## 架构
|
||||
|
||||
新增通用组件 `MiniGameStageIsolation`,放在 `Client/Assets/Script/xmain/MiniGame/`。它是大厅阶段隔离控制器,只负责保存大厅对象原始状态、暂停大厅、恢复大厅,不包含具体小游戏逻辑。
|
||||
|
||||
`MiniGameHost` 继续只处理:下载、校验、加载 DLL、创建 `IGameClient`、网络 Pump、结算、退出清理。它不直接关闭大厅 UI 或大厅场景。
|
||||
|
||||
调用方负责把隔离控制器串到流程里。当前 `PcLobbySmokeLauncher` 在收到 `MatchAssigned` 后调用:
|
||||
|
||||
```text
|
||||
EnterAssignedGame(gameId, version)
|
||||
↓
|
||||
MiniGameStageIsolation.SuspendLobbyForMiniGame()
|
||||
↓
|
||||
MiniGameHost.EnterGame(...)
|
||||
↓
|
||||
MiniGameHost.OnGameExited
|
||||
↓
|
||||
MiniGameStageIsolation.ResumeLobbyAfterMiniGame()
|
||||
↓
|
||||
恢复大厅网络 Pump 和大厅操作界面
|
||||
```
|
||||
|
||||
## `MiniGameStageIsolation` 职责
|
||||
|
||||
建议接口:
|
||||
|
||||
```csharp
|
||||
public sealed class MiniGameStageIsolation : MonoBehaviour
|
||||
{
|
||||
public GameObject[] LobbyUiRoots;
|
||||
public GameObject[] LobbySceneRoots;
|
||||
public Behaviour[] LobbyInputBehaviours;
|
||||
public Behaviour[] LobbyTickBehaviours;
|
||||
public CanvasGroup[] LobbyCanvasGroups;
|
||||
|
||||
public bool IsSuspended { get; }
|
||||
|
||||
public void SuspendLobbyForMiniGame();
|
||||
public void ResumeLobbyAfterMiniGame();
|
||||
}
|
||||
```
|
||||
|
||||
字段语义:
|
||||
|
||||
- `LobbyUiRoots`:大厅普通操作界面根节点。Suspend 时 `SetActive(false)`,Resume 时恢复原始 active 状态。
|
||||
- `LobbySceneRoots`:大厅 3D 场景、单位、特效、地图等渲染根节点。Suspend 时 `SetActive(false)`,Resume 时恢复原始 active 状态。
|
||||
- `LobbyInputBehaviours`:大厅输入、摇杆、点击、快捷键等脚本。Suspend 时 `enabled = false`,Resume 时恢复原始 enabled 状态。
|
||||
- `LobbyTickBehaviours`:大厅逻辑 Tick、AI 展示、场景表现控制等脚本。Suspend 时 `enabled = false`,Resume 时恢复原始 enabled 状态。
|
||||
- `LobbyCanvasGroups`:需要保留 active 但禁止交互的 UI 根。Suspend 时 `interactable=false`、`blocksRaycasts=false`,Resume 时恢复原状态。
|
||||
|
||||
实现要求:
|
||||
|
||||
1. 防重入:重复调用 `SuspendLobbyForMiniGame()` 不重复覆盖原始状态;重复调用 `ResumeLobbyAfterMiniGame()` 不报错。
|
||||
2. 恢复原状:Resume 恢复的是 Suspend 前的原始状态,不是简单全部打开。
|
||||
3. 空引用安全:数组里存在 null 时跳过并打印 warning 或静默跳过。
|
||||
4. 不销毁对象:只隐藏或禁用,避免破坏大厅状态。
|
||||
5. 不管理小游戏对象:小游戏 UI/资源由 `MiniGameHost` 和热更客户端清理。
|
||||
|
||||
## 大厅链路接入
|
||||
|
||||
`PcLobbySmokeLauncher` 当前已经有 `_lobbyActive` 控制大厅网络 Pump,有 `_matching` 控制匹配状态,有 `OnGameExited` 恢复大厅网络和刷新游戏列表。
|
||||
|
||||
接入后:
|
||||
|
||||
- `EnterAssignedGame()` 中,在 `MiniGameHost.EnterGame()` 前调用隔离器 Suspend。
|
||||
- `OnGameExited` 中,先 Resume 大厅,再恢复 `_lobbyNet`、`_lobbyActive`、`_matching`,最后 `RequestGameList()`。
|
||||
- 如果 `MiniGameHost.EnterGame()` 下载失败或加载失败,也必须触发恢复,避免大厅被隐藏后卡死。
|
||||
|
||||
建议 `MiniGameHost` 在进入失败路径调用一个统一失败退出函数,确保 `OnGameExited` 被调用。例如:
|
||||
|
||||
```text
|
||||
CoEnter 下载失败 / DLL 加载失败 / CreateClient 失败
|
||||
↓
|
||||
FailEnterAndExit(reason)
|
||||
↓
|
||||
清理已创建资源
|
||||
↓
|
||||
OnGameExited?.Invoke()
|
||||
```
|
||||
|
||||
这样大厅隔离恢复不需要知道失败原因,只要监听 `OnGameExited`。
|
||||
|
||||
## 生命周期
|
||||
|
||||
正常进入:
|
||||
|
||||
```text
|
||||
大厅收到 MatchAssigned
|
||||
↓
|
||||
暂停大厅网络 Pump(_lobbyActive=false)
|
||||
↓
|
||||
MiniGameStageIsolation.SuspendLobbyForMiniGame()
|
||||
↓
|
||||
MiniGameHost.EnterGame(...)
|
||||
↓
|
||||
小游戏下载、加载、OnEnter
|
||||
↓
|
||||
小游戏运行,期间大厅不可见不可操作
|
||||
```
|
||||
|
||||
正常退出:
|
||||
|
||||
```text
|
||||
小游戏 RoomEnd / 玩家确认结算 / 主动退出
|
||||
↓
|
||||
MiniGameHost.ExitGame()
|
||||
↓
|
||||
热更客户端 OnExit + 资源卸载
|
||||
↓
|
||||
OnGameExited
|
||||
↓
|
||||
MiniGameStageIsolation.ResumeLobbyAfterMiniGame()
|
||||
↓
|
||||
恢复大厅网络 Pump 和大厅 UI
|
||||
↓
|
||||
刷新大厅列表或大厅状态
|
||||
```
|
||||
|
||||
失败恢复:
|
||||
|
||||
```text
|
||||
小游戏下载失败 / 校验失败 / DLL 加载失败 / 入口类型错误
|
||||
↓
|
||||
MiniGameHost 清理部分初始化状态
|
||||
↓
|
||||
OnGameExited
|
||||
↓
|
||||
MiniGameStageIsolation.ResumeLobbyAfterMiniGame()
|
||||
↓
|
||||
大厅恢复可见和可操作,并显示错误日志或状态
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
- Suspend 后如果 EnterGame 失败,必须恢复大厅。
|
||||
- Resume 前如果部分对象已被外部销毁,跳过该对象,不抛异常。
|
||||
- 小游戏结算弹窗显示期间大厅仍保持隐藏,避免结算窗和大厅 UI 叠加。
|
||||
- 玩家重复点击进入小游戏时,如果 `IsSuspended == true`,不再次进入或不再次保存状态。
|
||||
- 如果大厅本身某些 UI 在进入前就是关闭状态,退出小游戏后仍保持关闭。
|
||||
|
||||
## 测试策略
|
||||
|
||||
编辑器/纯 C# 可验证:
|
||||
|
||||
1. `MiniGameStageIsolation` Suspend 后:
|
||||
- 配置的 UI root inactive。
|
||||
- 配置的 scene root inactive。
|
||||
- 输入 Behaviour disabled。
|
||||
- Tick Behaviour disabled。
|
||||
- CanvasGroup 不可交互且不挡射线。
|
||||
2. Resume 后恢复 Suspend 前状态。
|
||||
3. 重复 Suspend/Resume 不破坏状态。
|
||||
4. null 配置不抛异常。
|
||||
|
||||
运行时 smoke 验证:
|
||||
|
||||
1. 进入小游戏后,大厅操作 GUI 不再显示或不可操作。
|
||||
2. 大厅 3D/单位根节点不可见。
|
||||
3. 小游戏结算弹窗期间大厅仍隐藏。
|
||||
4. 点击结算确认退出小游戏后,大厅恢复可见可操作。
|
||||
5. CDN 缺包或 DLL 加载失败时,大厅也能恢复。
|
||||
|
||||
## 文档同步
|
||||
|
||||
`Client/Assets/Doc/MiniGameDevelopmentDesign.md` 应补充“大厅与小游戏阶段隔离”章节,说明:
|
||||
|
||||
- 小游戏期间大厅必须暂停并隐藏。
|
||||
- 隔离职责属于调用方/隔离组件,不属于具体小游戏。
|
||||
- `MiniGameHost.OnGameExited` 是恢复大厅的统一时机。
|
||||
- 新小游戏不得直接操作大厅对象。
|
||||
Reference in New Issue
Block a user