docs: add editor runtime flow docs

This commit is contained in:
2026-04-04 00:45:13 +08:00
parent 8abca3dec5
commit 2620b5914b
41 changed files with 2367 additions and 126 deletions

View File

@@ -0,0 +1,40 @@
# GameViewPanel::GameViewPanel
**命名空间**: `XCEngine::Editor`
**类型**: `constructor`
**源文件**: `editor/src/panels/GameViewPanel.h`
## 签名
```cpp
GameViewPanel();
```
## 作用
创建标题固定为 `"Game"` 的 Game 视图面板。
## 当前实现行为
当前构造函数非常薄:
```cpp
GameViewPanel::GameViewPanel() : Panel("Game") {}
```
它只完成一件事:
- 通过基类 `Panel` 把面板标题初始化为 `"Game"`
也就是说,`GameViewPanel` 的真实行为几乎都不在 constructor而集中在 [Render](Render.md) 里:
- 请求 `Game` 视口内容
- 采集 ImGui 键鼠状态
- 发布 `GameViewInputFrameEvent`
## 相关文档
- [GameViewPanel](GameViewPanel.md)
- [Render](Render.md)

View File

@@ -6,23 +6,151 @@
**源文件**: `editor/src/panels/GameViewPanel.h`
**描述**: Game 视图面板,占位承载 Game 窗口并标记动作路由焦点
**描述**: Game 视图面板,负责在 editor 窗口中承载 `EditorViewportKind::Game` 视口内容,并把 ImGui 键鼠状态逐帧发布为 `GameViewInputFrameEvent`
## 概述
当前 `GameViewPanel` 和 [SceneViewPanel](../SceneViewPanel/SceneViewPanel.md) 一样,都属于轻量占位面板。
`GameViewPanel` 当前已经不是单纯的“薄视口壳层”。按 `GameViewPanel.cpp` 的真实实现,它承担两条并行职责:
它目前主要做两件事:
1. 调用 [ViewportPanelContent](../ViewportPanelContent/ViewportPanelContent.md) 承载 `EditorViewportKind::Game` 对应的视口纹理。
2. 从 ImGui 读取当前 Game View 的键鼠状态,打包成 [GameViewInputFrameEvent](../../Core/EditorEvents/EditorEvents.md),经 [EventBus](../../Core/EventBus/EventBus.md) 逐帧发布给 [PlaySessionController](../../Core/PlaySessionController/PlaySessionController.md)。
-`"Game"` 为名字打开一个面板窗口
- 在该窗口激活时通知 `Actions` 层观察焦点路由
因此它已经是当前 Editor 里“运行时输入桥”的起点,而不是只负责显示画面。
如果你想顺着一条连续链路理解“Game View 采样到的输入最终怎样进入运行时 `InputManager`”,可以继续看:
- [Game View Runtime Input Bridge](../../../../_guides/Editor/Game-View-Runtime-Input-Bridge.md)
## 生命周期与公开入口
- [Constructor](Constructor.md)
创建标题固定为 `"Game"` 的 Game 视图面板。
- [Render](Render.md)
渲染 Game 视口,并在每帧末尾发布 `GameViewInputFrameEvent`
## 当前执行链路
`Render()` 当前的真实顺序是:
1. 把窗口边框宽度压到 `0.0f`
2. 打开 `PanelWindowScope("Game")`
3. 如果面板未打开,立即发布一个空的 `GameViewInputFrameEvent{}`,然后返回。
4. 如果面板打开,调用:
```cpp
RenderViewportPanelContent(*m_context, EditorViewportKind::Game);
```
5. 基于返回的 `ViewportPanelContentResult` 构建 `GameViewInputFrameEvent`
6. 通过 `context->GetEventBus().Publish(event)` 发布这一帧输入快照。
7. 调用 `Actions::ObserveInactiveActionRoute(*m_context)`
这意味着 `GameViewPanel` 每一帧都会发事件,不是“只有输入变化时才发”。关闭面板时发布空事件,也是一种有意设计,用来通知下游桥接层释放之前维持的运行时输入状态。
## `GameViewInputFrameEvent` 是什么
`GameViewPanel` 发布的不是离散键盘事件,而是逐帧快照。当前快照包含:
- `hovered`
- `focused`
- `mousePosition`
- `mouseDelta`
- `mouseWheel`
- `keyDown[256]`
- `mouseButtonDown[5]`
它的含义是:
- `mousePosition`
- 以 Game View 视口左上角为原点的局部坐标。
- `mouseDelta`
- 当前帧 ImGui 观察到的鼠标位移。
- `mouseWheel`
- 只有在 `content.hovered == true` 时才会写入 `io.MouseWheel`,否则强制为 `0.0f`
- `keyDown` / `mouseButtonDown`
- 表示“这一帧视图认为当前哪些键 / 鼠标按钮处于按住态”。
它是状态快照,不是 down/up 事件流。
## 键鼠采集规则
### 键盘
`FillGameViewKeyboardState(...)` 当前维护一张固定映射表,把 `ImGuiKey` 映射到 `XCEngine::Input::KeyCode`,包括:
- `A-Z`
- `0-9`
- `Space` / `Tab` / `Enter` / `Escape`
- 左右 `Shift` / `Ctrl` / `Alt`
- 方向键、`Home/End/PageUp/PageDown`
- `Delete` / `Backspace`
- `F1-F12`
- 若干标点键
所以 `GameViewPanel` 当前不是把所有 ImGui 按键原样透传,而是只透传这张映射表覆盖到的子集。
### 鼠标
`FillGameViewMouseState(...)` 当前只采集:
- Left
- Right
- Middle
并把它们写进 `mouseButtonDown` 数组。
### hovered / focused 门控
只有在:
- `event.hovered == true`
-
- `event.focused == true`
时,`GameViewPanel` 才会填充键盘和鼠标按钮状态。
这条规则很关键,因为它决定了:
- 视口失焦时不会继续把旧键盘状态送进运行时
- 只要视口仍有焦点,即使鼠标暂时不在上面,键盘状态仍然可以继续驱动 play mode
## 和 `PlaySessionController` 的关系
`GameViewPanel` 自己不会直接调用运行时 [InputManager](../../../Input/InputManager/InputManager.md)。它只负责:
- 采集 ImGui 输入快照
-`EventBus` 发布 `GameViewInputFrameEvent`
真正的桥接发生在 [PlaySessionController](../../Core/PlaySessionController/PlaySessionController.md)
`GameViewPanel` -> `EventBus.Publish(GameViewInputFrameEvent)` -> `PlaySessionController::ApplyGameViewInputFrame()` -> `InputManager::Process*`
这条拆分让 `GameViewPanel` 仍然保持在面板层,而不会直接知道 play mode 状态机和运行时输入细节。
## 与 SceneView 的关系
当前 `GameViewPanel` 与 [SceneViewPanel](../SceneViewPanel/SceneViewPanel.md) 的关系是:
- 两者都复用 [ViewportPanelContent](../ViewportPanelContent/ViewportPanelContent.md) 作为视口承载 helper。
- `SceneViewPanel` 把输入主要送给 editor camera / gizmo 交互。
- `GameViewPanel` 把输入主要送进 `GameViewInputFrameEvent`,再桥接到运行时 `InputManager`
所以两者虽然共用 viewport 承载层,但输入去向完全不同。
## 当前实现边界
- 当前没有真正的 runtime frame 嵌入或 play mode 画面输出
- 它现在更像未来 Game View 能力的承载容器
- 当前页本身不提供 play / pause / step 控件;这些仍属于菜单栏、动作路由和 `PlaySessionController`
- 当前没有额外的 Game View toolbar、缩放菜单或 aspect 预设逻辑
- 当前发布的是状态快照,不是字符输入或完整原始平台消息。
## 相关文档
- [panels](../panels.md)
- [Actions](../../Actions/Actions.md)
- [Constructor](Constructor.md)
- [Render](Render.md)
- [ViewportPanelContent](../ViewportPanelContent/ViewportPanelContent.md)
- [EditorEvents](../../Core/EditorEvents/EditorEvents.md)
- [EventBus](../../Core/EventBus/EventBus.md)
- [PlaySessionController](../../Core/PlaySessionController/PlaySessionController.md)
- [Game View Runtime Input Bridge](../../../../_guides/Editor/Game-View-Runtime-Input-Bridge.md)
- [SceneViewPanel](../SceneViewPanel/SceneViewPanel.md)

View File

@@ -0,0 +1,93 @@
# GameViewPanel::Render
**命名空间**: `XCEngine::Editor`
**类型**: `method`
**源文件**: `editor/src/panels/GameViewPanel.h`
## 签名
```cpp
void Render() override;
```
## 作用
绘制 `Game` 视口面板,并把当前帧 Game View 可见输入状态发布为 `GameViewInputFrameEvent`
## 当前实现行为
### 1. 建立面板外壳
- 先把 `ImGuiStyleVar_WindowBorderSize` 压成 `0.0f`
- 打开 `UI::PanelWindowScope("Game")`
这一步只负责建立 Game 面板窗口本身,不包含工具栏或额外控制区。
### 2. 面板关闭时主动发布空事件
如果 `panel.IsOpen() == false`,当前实现不会静默返回,而是先执行:
```cpp
PublishGameViewInputFrame(m_context, GameViewInputFrameEvent{});
```
然后再返回。
这条路径的意义是:
- 明确告诉下游“这一帧没有有效 Game View 输入”
- 让 [PlaySessionController](../../Core/PlaySessionController/PlaySessionController.md) 在下一帧桥接时释放之前保持的运行时按键和鼠标按钮状态
### 3. 请求 Game 视口内容
当面板打开时,会调用:
```cpp
RenderViewportPanelContent(*m_context, EditorViewportKind::Game);
```
得到 `ViewportPanelContentResult`,其中包含:
- 视口区域是否存在
- `hovered`
- `focused`
- 视口内容矩形范围
这些信息随后会被用来构造 `GameViewInputFrameEvent`
### 4. 构造输入快照
`BuildGameViewInputFrame(content)` 当前会按真实源码执行以下规则:
- `content.hasViewportArea == false` 时,直接返回空事件
- `mousePosition` 使用 `io.MousePos - content.itemMin`,也就是 Game View 局部坐标
- `mouseDelta` 直接取 `io.MouseDelta`
- `mouseWheel` 只有在 `content.hovered == true` 时才保留,否则强制写 `0.0f`
- 只有在 `hovered || focused` 时,才填充:
- 键盘映射表覆盖到的 `keyDown`
- Left / Right / Middle 三个鼠标按钮的 `mouseButtonDown`
### 5. 发布事件并继续动作观察
构造完成后会立即:
1. `context->GetEventBus().Publish(event)`
2. `Actions::ObserveInactiveActionRoute(*m_context)`
这说明 `Render()` 不是只负责画图,它同时还是当前 Game View 输入桥的事件源头。
## 当前实现边界
- 当前不绘制 play / pause / step 工具条。
- 当前发布的是状态快照,不是字符输入或原始平台消息。
- 当前键盘采样只覆盖 `GameViewPanel.cpp` 里维护的那张固定映射表,不是任意 `ImGuiKey` 都会桥接。
## 相关文档
- [GameViewPanel](GameViewPanel.md)
- [Constructor](Constructor.md)
- [EditorEvents](../../Core/EditorEvents/EditorEvents.md)
- [PlaySessionController](../../Core/PlaySessionController/PlaySessionController.md)
- [Game View Runtime Input Bridge](../../../../_guides/Editor/Game-View-Runtime-Input-Bridge.md)