# 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)