ADD: Update Docs
This commit is contained in:
292
docs/InputSystem.md
Normal file
292
docs/InputSystem.md
Normal file
@@ -0,0 +1,292 @@
|
||||
## Input System: Cross-Platform Input Handling
|
||||
|
||||
Unified input abstraction layer that wraps SDL2 events into a clean, game-friendly API. Provides both polled state queries and event-based access.
|
||||
|
||||
### Components
|
||||
|
||||
- `InputSystem` (src/core/input/input_system.h/.cpp)
|
||||
- Main entry point for all input handling.
|
||||
- Pumps SDL2 events, tracks window state (quit, minimize, resize).
|
||||
- Maintains per-frame `InputState` and `InputEvent` list.
|
||||
|
||||
- `InputState`
|
||||
- Polled snapshot of keyboard and mouse state.
|
||||
- Distinguishes between "down" (held), "pressed" (just pressed this frame), and "released" (just released this frame).
|
||||
|
||||
- `InputEvent`
|
||||
- Discrete input event with timestamp and modifiers.
|
||||
- Types: `KeyDown`, `KeyUp`, `MouseButtonDown`, `MouseButtonUp`, `MouseMove`, `MouseWheel`.
|
||||
|
||||
### Key Codes
|
||||
|
||||
Cross-platform key codes based on USB HID usage IDs (compatible with SDL scancodes):
|
||||
|
||||
```cpp
|
||||
enum class Key : uint16_t
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
// Letters (A-Z): 4-29
|
||||
A = 4, B = 5, C = 6, D = 7, E = 8, F = 9, G = 10, H = 11, I = 12,
|
||||
J = 13, K = 14, L = 15, M = 16, N = 17, O = 18, P = 19, Q = 20,
|
||||
R = 21, S = 22, T = 23, U = 24, V = 25, W = 26, X = 27, Y = 28, Z = 29,
|
||||
|
||||
// Numbers (0-9): 30-39
|
||||
Num1 = 30, Num2 = 31, Num3 = 32, Num4 = 33, Num5 = 34,
|
||||
Num6 = 35, Num7 = 36, Num8 = 37, Num9 = 38, Num0 = 39,
|
||||
|
||||
// Special keys
|
||||
Enter = 40,
|
||||
Escape = 41,
|
||||
Backspace = 42,
|
||||
Tab = 43,
|
||||
Space = 44,
|
||||
|
||||
// Modifiers
|
||||
LeftCtrl = 224,
|
||||
LeftShift = 225,
|
||||
LeftAlt = 226,
|
||||
LeftSuper = 227,
|
||||
RightCtrl = 228,
|
||||
RightShift = 229,
|
||||
RightAlt = 230,
|
||||
RightSuper = 231,
|
||||
};
|
||||
```
|
||||
|
||||
### Mouse Buttons
|
||||
|
||||
```cpp
|
||||
enum class MouseButton : uint8_t
|
||||
{
|
||||
Left = 0,
|
||||
Middle = 1,
|
||||
Right = 2,
|
||||
X1 = 3, // Extra button 1
|
||||
X2 = 4, // Extra button 2
|
||||
};
|
||||
```
|
||||
|
||||
### Cursor Modes
|
||||
|
||||
```cpp
|
||||
enum class CursorMode : uint8_t
|
||||
{
|
||||
Normal = 0, // Default cursor, visible
|
||||
Hidden = 1, // Cursor hidden but not captured
|
||||
Relative = 2, // FPS-style: cursor hidden, motion is relative delta only
|
||||
};
|
||||
```
|
||||
|
||||
### InputState API
|
||||
|
||||
Polled state for keyboard and mouse. Updated each frame before game logic runs.
|
||||
|
||||
**Keyboard:**
|
||||
|
||||
```cpp
|
||||
bool key_down(Key key) const; // True if key is currently held
|
||||
bool key_pressed(Key key) const; // True only on the frame key was pressed
|
||||
bool key_released(Key key) const; // True only on the frame key was released
|
||||
```
|
||||
|
||||
**Mouse:**
|
||||
|
||||
```cpp
|
||||
bool mouse_down(MouseButton button) const; // True if button is held
|
||||
bool mouse_pressed(MouseButton button) const; // True only on frame pressed
|
||||
bool mouse_released(MouseButton button) const; // True only on frame released
|
||||
|
||||
glm::vec2 mouse_position() const; // Current cursor position (pixels)
|
||||
glm::vec2 mouse_delta() const; // Motion delta this frame (pixels)
|
||||
glm::vec2 wheel_delta() const; // Scroll wheel delta this frame
|
||||
```
|
||||
|
||||
**Modifiers:**
|
||||
|
||||
```cpp
|
||||
InputModifiers modifiers() const;
|
||||
|
||||
struct InputModifiers
|
||||
{
|
||||
bool shift = false;
|
||||
bool ctrl = false;
|
||||
bool alt = false;
|
||||
bool super = false; // Windows key / Command key
|
||||
};
|
||||
```
|
||||
|
||||
### InputEvent Structure
|
||||
|
||||
For event-driven input handling:
|
||||
|
||||
```cpp
|
||||
struct InputEvent
|
||||
{
|
||||
enum class Type : uint8_t
|
||||
{
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
MouseButtonDown,
|
||||
MouseButtonUp,
|
||||
MouseMove,
|
||||
MouseWheel,
|
||||
};
|
||||
|
||||
Type type;
|
||||
uint32_t timestamp_ms; // SDL timestamp
|
||||
InputModifiers mods; // Active modifiers
|
||||
|
||||
Key key; // Valid for KeyDown/KeyUp
|
||||
MouseButton mouse_button; // Valid for MouseButtonDown/Up
|
||||
|
||||
glm::vec2 mouse_pos; // Valid for mouse events
|
||||
glm::vec2 mouse_delta; // Valid for MouseMove
|
||||
glm::vec2 wheel_delta; // Valid for MouseWheel
|
||||
};
|
||||
```
|
||||
|
||||
### InputSystem API
|
||||
|
||||
**Frame Lifecycle:**
|
||||
|
||||
```cpp
|
||||
void begin_frame(); // Clear per-frame state (pressed/released, deltas)
|
||||
void pump_events(); // Poll and process all pending SDL events
|
||||
```
|
||||
|
||||
**State Access:**
|
||||
|
||||
```cpp
|
||||
const InputState& state() const; // Get current polled state
|
||||
std::span<const InputEvent> events() const; // Get all events this frame
|
||||
```
|
||||
|
||||
**Window State:**
|
||||
|
||||
```cpp
|
||||
bool quit_requested() const; // True if window close requested
|
||||
bool window_minimized() const; // True if window is minimized
|
||||
|
||||
bool resize_requested() const; // True if resize/move occurred
|
||||
uint32_t last_resize_event_ms() const;
|
||||
void clear_resize_request(); // Clear after handling resize
|
||||
```
|
||||
|
||||
**Cursor Control:**
|
||||
|
||||
```cpp
|
||||
CursorMode cursor_mode() const;
|
||||
void set_cursor_mode(CursorMode mode);
|
||||
```
|
||||
|
||||
**Native Event Access (Engine Internal):**
|
||||
|
||||
```cpp
|
||||
// For ImGui or other systems that need raw SDL events
|
||||
void for_each_native_event(NativeEventCallback callback, void* user) const;
|
||||
```
|
||||
|
||||
### Usage Example
|
||||
|
||||
**Polled State (Most Common):**
|
||||
|
||||
```cpp
|
||||
void Game::update(InputSystem& input)
|
||||
{
|
||||
const InputState& state = input.state();
|
||||
|
||||
// Movement
|
||||
glm::vec3 move{0.0f};
|
||||
if (state.key_down(Key::W)) move.z -= 1.0f;
|
||||
if (state.key_down(Key::S)) move.z += 1.0f;
|
||||
if (state.key_down(Key::A)) move.x -= 1.0f;
|
||||
if (state.key_down(Key::D)) move.x += 1.0f;
|
||||
|
||||
// Sprint (shift held)
|
||||
float speed = 5.0f;
|
||||
if (state.modifiers().shift) speed = 10.0f;
|
||||
|
||||
// Camera look (relative mode)
|
||||
if (state.mouse_down(MouseButton::Right))
|
||||
{
|
||||
input.set_cursor_mode(CursorMode::Relative);
|
||||
glm::vec2 delta = state.mouse_delta();
|
||||
camera.rotate(delta.x * 0.1f, delta.y * 0.1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
input.set_cursor_mode(CursorMode::Normal);
|
||||
}
|
||||
|
||||
// Fire on click
|
||||
if (state.mouse_pressed(MouseButton::Left))
|
||||
{
|
||||
player.fire();
|
||||
}
|
||||
|
||||
// Toggle inventory on I press
|
||||
if (state.key_pressed(Key::I))
|
||||
{
|
||||
ui.toggle_inventory();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Event-Driven (For UI / Text Input):**
|
||||
|
||||
```cpp
|
||||
void TextBox::process_input(InputSystem& input)
|
||||
{
|
||||
for (const InputEvent& ev : input.events())
|
||||
{
|
||||
if (ev.type == InputEvent::Type::KeyDown)
|
||||
{
|
||||
if (ev.key == Key::Backspace && !text.empty())
|
||||
{
|
||||
text.pop_back();
|
||||
}
|
||||
else if (ev.key == Key::Enter)
|
||||
{
|
||||
submit();
|
||||
}
|
||||
}
|
||||
else if (ev.type == InputEvent::Type::MouseWheel)
|
||||
{
|
||||
scroll_offset -= ev.wheel_delta.y * 20.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Frame Flow
|
||||
|
||||
1. Engine calls `InputSystem::begin_frame()` at frame start.
|
||||
2. Engine calls `InputSystem::pump_events()` to process SDL events.
|
||||
3. Game code queries `state()` for polled input or iterates `events()` for event-driven logic.
|
||||
4. Render loop proceeds.
|
||||
|
||||
### Integration with ImGui
|
||||
|
||||
The engine provides native event access for ImGui integration:
|
||||
|
||||
```cpp
|
||||
input.for_each_native_event(
|
||||
[](void* user, InputSystem::NativeEventView event)
|
||||
{
|
||||
if (event.backend == InputSystem::NativeBackend::SDL2)
|
||||
{
|
||||
ImGui_ImplSDL2_ProcessEvent(
|
||||
static_cast<const SDL_Event*>(event.data));
|
||||
}
|
||||
},
|
||||
nullptr);
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
- Prefer polled state (`key_down`, `mouse_down`) for continuous actions like movement.
|
||||
- Use pressed/released (`key_pressed`, `mouse_pressed`) for one-shot actions like firing or toggling.
|
||||
- Use `CursorMode::Relative` for FPS-style camera control; mouse position becomes meaningless, only delta matters.
|
||||
- Check `quit_requested()` each frame to handle window close gracefully.
|
||||
- The `resize_requested()` flag includes window move events to trigger swapchain recreation on multi-monitor setups.
|
||||
Reference in New Issue
Block a user