2026-03-26 16:45:24 +08:00
|
|
|
# InputManager
|
|
|
|
|
|
|
|
|
|
**命名空间**: `XCEngine::Input`
|
|
|
|
|
|
|
|
|
|
**类型**: `class (singleton)`
|
|
|
|
|
|
|
|
|
|
**头文件**: `XCEngine/Input/InputManager.h`
|
|
|
|
|
|
2026-03-26 17:39:53 +08:00
|
|
|
**描述**: 保存当前输入状态、提供轮询查询接口,并同步广播输入事件。
|
2026-03-26 16:45:24 +08:00
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
2026-04-08 16:07:03 +08:00
|
|
|
`InputManager` 是 XCEngine 当前输入系统的核心入口。平台层通过 `ProcessKeyDown`、`ProcessMouseMove` 等方法把原始输入送进来;游戏代码、编辑器和测试代码则通过 `IsKeyDown`、`GetAxis`、`GetButtonDown`、`OnKeyEvent` 等 API 查询或订阅这些状态。
|
2026-03-26 16:45:24 +08:00
|
|
|
|
2026-03-26 17:39:53 +08:00
|
|
|
它同时承担两种角色:
|
2026-03-26 16:45:24 +08:00
|
|
|
|
2026-04-08 16:07:03 +08:00
|
|
|
- 轮询中心
|
|
|
|
|
- 保存“当前是否按住”“本帧是否刚按下”“本帧是否刚释放”“本帧鼠标位移 / 滚轮”等状态
|
|
|
|
|
- 事件中心
|
|
|
|
|
- 把输入变化同步广播给订阅者
|
|
|
|
|
|
|
|
|
|
## 当前帧状态模型
|
|
|
|
|
|
|
|
|
|
当前实现内部至少维护四组和键鼠查询直接相关的状态:
|
|
|
|
|
|
|
|
|
|
| 状态层 | 键盘 | 鼠标 | 作用 |
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
| 按住态 | `m_keyDown` | `m_mouseButtonDown` | 回答 `IsKeyDown()` / `IsMouseButtonDown()` / `GetAxis()` / `GetButton()` / `IsAnyKeyDown()` |
|
|
|
|
|
| 按下边沿 | `m_keyDownThisFrame` | `m_mouseButtonDownThisFrame` | 回答 `IsKeyPressed()` / `IsMouseButtonClicked()` / `GetButtonDown()` / `IsAnyKeyPressed()` |
|
|
|
|
|
| 上一帧按下边沿 | `m_keyDownLastFrame` | `m_mouseButtonDownLastFrame` | 帮助判断“这一帧是否第一次按下” |
|
|
|
|
|
| 释放边沿 | `m_keyUpThisFrame` | `m_mouseButtonUpThisFrame` | 回答 `IsKeyReleased()` / `IsMouseButtonReleased()` / `GetButtonUp()` |
|
|
|
|
|
|
|
|
|
|
这里要特别区分两组很容易混淆的接口:
|
|
|
|
|
|
|
|
|
|
- `IsKeyUp()` / `IsMouseButtonUp()`
|
|
|
|
|
- 只是“当前没有按住”
|
|
|
|
|
- 等价于 `!IsKeyDown()` / `!IsMouseButtonDown()`
|
|
|
|
|
- `IsKeyReleased()` / `IsMouseButtonReleased()`
|
|
|
|
|
- 是“本帧刚释放”
|
|
|
|
|
- 依赖 `ProcessKeyUp()` / `ProcessMouseButton(..., false, ...)` 写入的释放边沿缓存
|
2026-03-26 16:45:24 +08:00
|
|
|
|
2026-03-26 17:39:53 +08:00
|
|
|
## 生命周期
|
2026-03-26 16:45:24 +08:00
|
|
|
|
2026-03-26 17:39:53 +08:00
|
|
|
- [Get](Get.md) 返回进程级单例。
|
|
|
|
|
- [Initialize](Initialize.md) 分配内部状态数组、保存窗口句柄并注册默认轴/按钮。
|
2026-04-08 16:07:03 +08:00
|
|
|
- [Shutdown](Shutdown.md) 会清空状态和映射,但当前不会清空事件监听器。
|
2026-03-26 17:39:53 +08:00
|
|
|
- [Update](Update.md) 用于推进帧边界并清理瞬时状态。
|
|
|
|
|
|
|
|
|
|
## 默认映射
|
|
|
|
|
|
2026-04-08 16:07:03 +08:00
|
|
|
当前 `Initialize()` 会注册以下默认逻辑项:
|
2026-03-26 17:39:53 +08:00
|
|
|
|
|
|
|
|
- 轴:`Horizontal` (`D` / `A`)、`Vertical` (`W` / `S`)
|
2026-04-08 16:07:03 +08:00
|
|
|
- 轴:`Mouse X`、`Mouse Y`
|
|
|
|
|
- 但两者当前都绑定为 `KeyCode::None`
|
|
|
|
|
- 因此 `GetAxis()` / `GetAxisRaw()` 结果始终为 `0.0f`
|
2026-03-26 17:39:53 +08:00
|
|
|
- 按钮:`Jump` (`Space`)、`Fire1` (`LeftCtrl`)、`Fire2` (`LeftAlt`)、`Fire3` (`LeftShift`)
|
|
|
|
|
|
2026-04-08 16:07:03 +08:00
|
|
|
## 当前实现行为
|
|
|
|
|
|
|
|
|
|
- [GetAxis](GetAxis.md) 和 [GetAxisRaw](GetAxisRaw.md) 当前使用同一套实现,都会根据持续按住状态返回 `-1.0f / 0.0f / 1.0f`。
|
|
|
|
|
- 逻辑按钮当前只是“名字到 `KeyCode`”的映射;`GetButtonDown()` / `GetButtonUp()` 仍然分别转发到键盘的按下边沿 / 释放边沿查询。
|
|
|
|
|
- [IsAnyKeyDown](IsAnyKeyDown.md) 和 [IsAnyKeyPressed](IsAnyKeyPressed.md) 会同时统计键盘和鼠标按键,不只是键盘。
|
|
|
|
|
- [ProcessKeyDown](ProcessKeyDown.md) 不会因为 `repeat == true` 而跳过按下缓存写入,所以 `IsKeyPressed()` / `GetButtonDown()` / `IsAnyKeyPressed()` 反映的是“这一帧是否收到按下消息”,不保证只对应首次物理按下。
|
|
|
|
|
- `m_buttonDownThisFrame` / `m_buttonDownLastFrame` 当前只在 [Update](Update.md) 中维护,还没有成为逻辑按钮查询的主来源。
|
|
|
|
|
|
2026-03-26 17:39:53 +08:00
|
|
|
## 线程语义
|
|
|
|
|
|
|
|
|
|
- 当前实现没有锁,也不是为多线程输入注入设计的。
|
2026-04-08 16:07:03 +08:00
|
|
|
- `Process*`、`Update()`、注册映射和查询最好都在主线程或同一逻辑线程完成。
|
2026-03-26 17:39:53 +08:00
|
|
|
- 事件回调同步执行在调用 `Process*` 的线程上。
|
|
|
|
|
|
|
|
|
|
## 当前实现限制
|
|
|
|
|
|
|
|
|
|
- `platformWindowHandle` 会被保存,但 `InputManager` 当前并不直接使用它。
|
|
|
|
|
- `KeyCode` 数值存在重复项,而 `InputManager` 用原始枚举值索引固定数组,因此部分按键状态会别名冲突。
|
2026-04-08 16:07:03 +08:00
|
|
|
- `GetAxisRaw()` 当前和 `GetAxis()` 没有行为差异。
|
|
|
|
|
- 键盘按下边沿接口不屏蔽平台重复按键消息。
|
|
|
|
|
- `ClearAxes()` 会同时清空 `m_axes` 和 `m_buttons`。
|
2026-03-26 17:39:53 +08:00
|
|
|
- 触摸接口已暴露,但当前实现没有任何公开路径去填充 `m_touches`。
|
2026-04-08 16:07:03 +08:00
|
|
|
- `Shutdown()` 不会清空事件监听器;如果单例跨生命周期反复初始化,需要调用方自己解除订阅。
|
2026-03-26 17:39:53 +08:00
|
|
|
|
|
|
|
|
## 公开方法
|
|
|
|
|
|
|
|
|
|
| 方法 | 说明 |
|
2026-03-26 16:45:24 +08:00
|
|
|
|------|------|
|
2026-03-26 17:39:53 +08:00
|
|
|
| [Get](Get.md) | 获取全局 `InputManager` 实例。 |
|
|
|
|
|
| [Initialize](Initialize.md) | 初始化输入状态和默认映射。 |
|
|
|
|
|
| [Shutdown](Shutdown.md) | 清空输入状态与映射。 |
|
|
|
|
|
| [Update](Update.md) | 推进帧边界并清理瞬时状态。 |
|
|
|
|
|
| [IsKeyDown](IsKeyDown.md) | 查询键是否处于按下状态。 |
|
|
|
|
|
| [IsKeyUp](IsKeyUp.md) | 查询键是否处于抬起状态。 |
|
|
|
|
|
| [IsKeyPressed](IsKeyPressed.md) | 查询键是否在本帧刚被按下。 |
|
2026-04-08 16:07:03 +08:00
|
|
|
| [IsKeyReleased](IsKeyReleased.md) | 查询键是否在本帧刚被释放。 |
|
2026-03-26 17:39:53 +08:00
|
|
|
| [GetMousePosition](GetMousePosition.md) | 读取当前鼠标位置。 |
|
|
|
|
|
| [GetMouseDelta](GetMouseDelta.md) | 读取本帧鼠标位移。 |
|
|
|
|
|
| [GetMouseScrollDelta](GetMouseScrollDelta.md) | 读取本帧滚轮增量。 |
|
|
|
|
|
| [IsMouseButtonDown](IsMouseButtonDown.md) | 查询鼠标按键是否按下。 |
|
|
|
|
|
| [IsMouseButtonUp](IsMouseButtonUp.md) | 查询鼠标按键是否抬起。 |
|
|
|
|
|
| [IsMouseButtonClicked](IsMouseButtonClicked.md) | 查询鼠标按键是否在本帧刚被按下。 |
|
2026-04-08 16:07:03 +08:00
|
|
|
| [IsMouseButtonReleased](IsMouseButtonReleased.md) | 查询鼠标按键是否在本帧刚被释放。 |
|
2026-03-26 17:39:53 +08:00
|
|
|
| [GetTouchCount](GetTouchCount.md) | 读取触点数量。 |
|
|
|
|
|
| [GetTouch](GetTouch.md) | 读取指定触点状态。 |
|
|
|
|
|
| [GetAxis](GetAxis.md) | 读取具名逻辑轴。 |
|
|
|
|
|
| [GetAxisRaw](GetAxisRaw.md) | 读取具名逻辑轴的当前原始值。 |
|
|
|
|
|
| [GetButton](GetButton.md) | 查询具名按钮当前是否按下。 |
|
|
|
|
|
| [GetButtonDown](GetButtonDown.md) | 查询具名按钮是否在本帧刚按下。 |
|
2026-04-08 16:07:03 +08:00
|
|
|
| [GetButtonUp](GetButtonUp.md) | 查询具名按钮是否在本帧刚释放。 |
|
|
|
|
|
| [IsAnyKeyDown](IsAnyKeyDown.md) | 查询是否存在任意键盘键或鼠标键处于按住状态。 |
|
|
|
|
|
| [IsAnyKeyPressed](IsAnyKeyPressed.md) | 查询是否存在任意键盘键或鼠标键在本帧刚被按下。 |
|
2026-03-26 17:39:53 +08:00
|
|
|
| [RegisterAxis](RegisterAxis.md) | 注册或覆盖一个具名轴。 |
|
|
|
|
|
| [RegisterButton](RegisterButton.md) | 注册或覆盖一个具名按钮。 |
|
|
|
|
|
| [ClearAxes](ClearAxes.md) | 清空轴和按钮映射。 |
|
|
|
|
|
| [OnKeyEvent](OnKeyEvent.md) | 获取键盘事件通道。 |
|
|
|
|
|
| [OnMouseButton](OnMouseButton.md) | 获取鼠标按键事件通道。 |
|
|
|
|
|
| [OnMouseMove](OnMouseMove.md) | 获取鼠标移动事件通道。 |
|
|
|
|
|
| [OnMouseWheel](OnMouseWheel.md) | 获取鼠标滚轮事件通道。 |
|
|
|
|
|
| [OnTextInput](OnTextInput.md) | 获取文本输入事件通道。 |
|
|
|
|
|
| [ProcessKeyDown](ProcessKeyDown.md) | 注入按键按下事件。 |
|
|
|
|
|
| [ProcessKeyUp](ProcessKeyUp.md) | 注入按键抬起事件。 |
|
|
|
|
|
| [ProcessMouseMove](ProcessMouseMove.md) | 注入鼠标移动事件。 |
|
|
|
|
|
| [ProcessMouseButton](ProcessMouseButton.md) | 注入鼠标按键事件。 |
|
|
|
|
|
| [ProcessMouseWheel](ProcessMouseWheel.md) | 注入鼠标滚轮事件。 |
|
|
|
|
|
| [ProcessTextInput](ProcessTextInput.md) | 注入文本输入事件。 |
|
2026-03-26 16:45:24 +08:00
|
|
|
|
2026-04-08 16:07:03 +08:00
|
|
|
## 真实行为依据
|
|
|
|
|
|
|
|
|
|
- `engine/src/Input/InputManager.cpp`
|
|
|
|
|
- `tests/Input/test_input_manager.cpp`
|
|
|
|
|
|
2026-03-26 16:45:24 +08:00
|
|
|
## 相关文档
|
|
|
|
|
|
2026-03-26 17:39:53 +08:00
|
|
|
- [当前模块](../Input.md)
|
|
|
|
|
- [InputAxis](../InputAxis/InputAxis.md)
|
|
|
|
|
- [InputEvent](../InputEvent/InputEvent.md)
|
|
|
|
|
- [Input Flow And Frame Semantics](../../../_guides/Input/Input-Flow-and-Frame-Semantics.md)
|