116 lines
3.5 KiB
Markdown
116 lines
3.5 KiB
Markdown
|
|
# Input Flow And Frame Semantics
|
|||
|
|
|
|||
|
|
## 这套输入系统怎么理解
|
|||
|
|
|
|||
|
|
当前 XCEngine 输入系统更接近“轻量级、同步、帧驱动的输入管理器”,而不是完整设备抽象框架。
|
|||
|
|
|
|||
|
|
它有两个核心层:
|
|||
|
|
|
|||
|
|
- `InputModule` 负责接平台消息。
|
|||
|
|
- `InputManager` 负责保存状态并提供事件与查询接口。
|
|||
|
|
|
|||
|
|
这意味着真正的输入流向是:
|
|||
|
|
|
|||
|
|
1. 平台窗口系统产生命令或消息。
|
|||
|
|
2. 平台后端把消息翻译成 `ProcessKeyDown`、`ProcessMouseMove` 一类调用。
|
|||
|
|
3. `InputManager` 更新状态并同步广播事件。
|
|||
|
|
4. 游戏或工具层在当前帧读取轮询状态。
|
|||
|
|
5. `Update()` 在帧边界清理瞬时状态。
|
|||
|
|
|
|||
|
|
## 为什么同时保留事件和轮询
|
|||
|
|
|
|||
|
|
事件和轮询各有适合的地方:
|
|||
|
|
|
|||
|
|
- UI、输入法、编辑器面板更适合订阅事件。
|
|||
|
|
- 游戏逻辑、相机控制、角色移动更适合每帧轮询。
|
|||
|
|
|
|||
|
|
`InputManager` 同时提供这两种方式,是为了避免你在上层自己重复维护一套输入缓存。
|
|||
|
|
|
|||
|
|
## 帧边界怎么理解
|
|||
|
|
|
|||
|
|
当前实现里,`Update()` 不是“采集输入”,而是“结束上一帧的瞬时状态”。
|
|||
|
|
|
|||
|
|
它会清理:
|
|||
|
|
|
|||
|
|
- `IsKeyPressed` 依赖的本帧按下标记
|
|||
|
|
- `IsMouseButtonClicked` 依赖的本帧点击标记
|
|||
|
|
- `GetMouseDelta`
|
|||
|
|
- `GetMouseScrollDelta`
|
|||
|
|
|
|||
|
|
所以最重要的实践是:
|
|||
|
|
|
|||
|
|
- 每帧只调用一次 `Update()`。
|
|||
|
|
- 保持调用时机一致。
|
|||
|
|
- 在清理之前,先消费掉当前帧的瞬时输入。
|
|||
|
|
|
|||
|
|
## 和 Unity 的对照
|
|||
|
|
|
|||
|
|
从使用方式上看,`InputManager` 很像 Unity 旧版 Input Manager:
|
|||
|
|
|
|||
|
|
- 有具名轴:`Horizontal`、`Vertical`
|
|||
|
|
- 有具名按钮:`Jump`、`Fire1`、`Fire2`、`Fire3`
|
|||
|
|
- 查询接口偏向 `GetAxis` / `GetButton`
|
|||
|
|
|
|||
|
|
但当前实现并不等同于 Unity:
|
|||
|
|
|
|||
|
|
- 没有平滑、重力、灵敏度、死区等高级参数。
|
|||
|
|
- `GetAxisRaw` 当前只在“按下这一帧”返回非零,不等同于 Unity 中“原始持续值”的语义。
|
|||
|
|
- `Mouse X` 和 `Mouse Y` 默认虽然被注册了,但当前实现并不会通过鼠标移动更新它们。
|
|||
|
|
|
|||
|
|
## 平台桥接是怎么接进来的
|
|||
|
|
|
|||
|
|
`InputModule` 的意义是把平台消息层隔离掉。当前 Windows 路径里:
|
|||
|
|
|
|||
|
|
- `WindowsInputModule::HandleMessage` 负责处理 `WM_KEYDOWN`、`WM_MOUSEMOVE`、`WM_MOUSEWHEEL` 等消息。
|
|||
|
|
- 它再调用 `InputManager::Process*` 统一更新状态。
|
|||
|
|
|
|||
|
|
这样做的好处是:
|
|||
|
|
|
|||
|
|
- 游戏层不用知道 Win32 消息细节。
|
|||
|
|
- 将来如果换 GLFW、SDL 或其它平台层,`InputManager` 可以不变。
|
|||
|
|
|
|||
|
|
## 当前版本最该知道的限制
|
|||
|
|
|
|||
|
|
- `KeyCode` 底层值当前有重复,部分键会共享状态槽。
|
|||
|
|
- Windows 路径当前不能准确区分左右 Ctrl / Alt / Shift。
|
|||
|
|
- 触摸与摇杆接口基本是预留位。
|
|||
|
|
- 事件回调是同步执行的,不是异步消息队列。
|
|||
|
|
- `Shutdown()` 当前不会清空事件监听器;如果单例跨生命周期反复初始化,要自己管理订阅者。
|
|||
|
|
|
|||
|
|
## 推荐使用方式
|
|||
|
|
|
|||
|
|
初始化阶段:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
using namespace XCEngine::Input;
|
|||
|
|
|
|||
|
|
InputManager::Get().Initialize(windowHandle);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
平台消息阶段:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
windowsInputModule.HandleMessage(hwnd, msg, wParam, lParam);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
游戏逻辑阶段:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
if (InputManager::Get().GetButton("Jump")) {
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
帧边界阶段:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
InputManager::Get().Update(deltaTime);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 相关 API
|
|||
|
|
|
|||
|
|
- [Input](../../XCEngine/Input/Input.md)
|
|||
|
|
- [InputManager](../../XCEngine/Input/InputManager/InputManager.md)
|
|||
|
|
- [InputModule](../../XCEngine/Input/InputModule/InputModule.md)
|
|||
|
|
- [InputTypes](../../XCEngine/Input/InputTypes/InputTypes.md)
|