Input System
The Raftel Engine Input System provides a simple and consistent way to handle keyboard and mouse interactions in your application. The system abstracts the complexities of the underlying GLFW input handling and provides a clean interface for your game to respond to user actions.
Important Note
The Input object is always owned and managed by the Window object. You should never create an Input object directly. Access the input system through your Window instance.
Features
Keyboard Input
Detect key presses, releases, and held states with simple function calls. Supports all standard keyboard keys.
Mouse Input
Track mouse buttons, position, movement deltas, and scroll wheel activity with precision.
Frame-Based Detection
Distinguish between keys pressed this frame versus keys being held down, allowing for precise input timing.
Scroll Wheel Support
Capture scroll wheel movement in both x and y axes for advanced navigation or zooming functionality.
Accessing Input
Since the Input system is managed by the Window, you'll need to access it through your Window instance:
// Assuming you have a Window instance
auto windowOpt = Raftel::Window::make("My Application", *windowSystemOpt);
// Check if a key is being pressed
if (windowOpt->input->isKeyPressed(Raftel::Input::Keys::Key_Space)) {
// The space key was just pressed this frame
}
// Check if a key is held down
if (windowOpt->input->isKeyDown(Raftel::Input::Keys::Key_W)) {
// The W key is being held down
}
// Access mouse movement
glm::vec2 mouseDelta = windowOpt->input->getMouseDelta();
// Use mouseDelta.x and mouseDelta.y for camera rotation or other controls
Keyboard Input
The Input system provides several methods to check the state of keys:
- isKeyPressed(Keys key): Returns true if the key was pressed this frame (transition from up to down).
- isKeyRelease(Keys key): Returns true if the key was released this frame (transition from down to up).
- isKeyDown(Keys key): Returns true if the key is currently being held down.
- isKeyUp(Keys key): Returns true if the key is currently up (not pressed).
// In your update or game loop function
void update(float deltaTime) {
// Jump only on the frame when space is pressed
if (window->input->isKeyPressed(Raftel::Input::Keys::Key_Space)) {
player.jump();
}
// Continuously move forward while W is held down
if (window->input->isKeyDown(Raftel::Input::Keys::Key_W)) {
player.moveForward(deltaTime);
}
// Reload weapon when R is released
if (window->input->isKeyRelease(Raftel::Input::Keys::Key_R)) {
player.reloadWeapon();
}
}
Mouse Input
The Input system also provides methods to handle mouse interaction:
- isMouseButtonPressed(Buttons button): Returns true if the mouse button was pressed this frame.
- isMouseButtonRelease(Buttons button): Returns true if the mouse button was released this frame.
- isMouseButtonDown(Buttons button): Returns true if the mouse button is currently being held down.
- isMouseButtonUp(Buttons button): Returns true if the mouse button is currently up.
- getMousePosition(): Returns the current mouse position as a glm::vec2.
- getMouseDelta(): Returns the mouse movement since the last frame as a glm::vec2.
- getScrollDelta(): Returns the scroll wheel movement since the last frame as a glm::vec2.
- resetMouseTracking(): Resets mouse movement tracking (useful when implementing camera controls).
// Handle camera rotation with mouse movement
void updateCamera(float deltaTime) {
// Get mouse movement
glm::vec2 mouseDelta = window->input->getMouseDelta();
// Apply rotation to camera
camera.yaw += mouseDelta.x * sensitivity;
camera.pitch -= mouseDelta.y * sensitivity; // Inverted Y for intuitive control
// Clamp pitch to avoid gimbal lock
camera.pitch = glm::clamp(camera.pitch, -89.0f, 89.0f);
// Update camera vectors
camera.updateVectors();
// Handle zoom with scroll wheel
glm::vec2 scrollDelta = window->input->getScrollDelta();
camera.zoom -= scrollDelta.y * zoomSensitivity;
camera.zoom = glm::clamp(camera.zoom, 1.0f, 45.0f);
}
Complete Example: First Person Camera
Here's a complete example showing how to implement a first-person camera using the Input system:
#include "raftel/window.hpp"
#include "raftel/camera.hpp"
class FirstPersonController {
public:
FirstPersonController(Raftel::Window* window, Raftel::Camera* camera)
: window(window), camera(camera) {
// Initially reset mouse tracking to avoid jumps
window->input->resetMouseTracking();
}
void update(float deltaTime) {
// Process keyboard input for movement
processKeyboardInput(deltaTime);
// Process mouse input for rotation
processMouseInput();
// Process scroll wheel for zoom
processScrollInput();
}
private:
Raftel::Window* window;
Raftel::Camera* camera;
// Movement settings
float movementSpeed = 5.0f;
float mouseSensitivity = 0.1f;
float zoomSensitivity = 1.0f;
void processKeyboardInput(float deltaTime) {
float velocity = movementSpeed * deltaTime;
// Forward/backward
if (window->input->isKeyDown(Raftel::Input::Keys::Key_W))
camera->position += camera->forward * velocity;
if (window->input->isKeyDown(Raftel::Input::Keys::Key_S))
camera->position -= camera->forward * velocity;
// Left/right
if (window->input->isKeyDown(Raftel::Input::Keys::Key_A))
camera->position -= camera->right * velocity;
if (window->input->isKeyDown(Raftel::Input::Keys::Key_D))
camera->position += camera->right * velocity;
// Up/down
if (window->input->isKeyDown(Raftel::Input::Keys::Key_Space))
camera->position += camera->up * velocity;
if (window->input->isKeyDown(Raftel::Input::Keys::Key_Control))
camera->position -= camera->up * velocity;
}
void processMouseInput() {
// Only rotate when right mouse button is held down
if (window->input->isMouseButtonDown(Raftel::Input::Buttons::Mouse_Right)) {
glm::vec2 mouseDelta = window->input->getMouseDelta();
camera->yaw += mouseDelta.x * mouseSensitivity;
camera->pitch -= mouseDelta.y * mouseSensitivity;
// Clamp pitch to avoid gimbal lock
camera->pitch = glm::clamp(camera->pitch, -89.0f, 89.0f);
// Update camera vectors
camera->updateVectors();
}
}
void processScrollInput() {
glm::vec2 scrollDelta = window->input->getScrollDelta();
camera->fov -= scrollDelta.y * zoomSensitivity;
camera->fov = glm::clamp(camera->fov, 1.0f, 90.0f);
}
};
// In main application
int main() {
// Initialize window and camera
auto windowSystemOpt = Raftel::WindowSystem::make();
auto windowOpt = Raftel::Window::make("First Person Demo", *windowSystemOpt);
auto camera = std::make_unique();
auto controller = std::make_unique(windowOpt.get(), camera.get());
// Main loop
float lastFrame = 0.0f;
while (!windowOpt->ShouldClose()) {
float currentFrame = glfwGetTime();
float deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// Update controller
controller->update(deltaTime);
// Clear screen and render scene...
// Important: update keys at the end of each frame
windowOpt->input->updateKeys();
windowOpt->swapBuffers();
}
return 0;
}
Best Practices
- Always call
updateKeys()
at the end of each frame to ensure proper key state tracking. - For camera controls, use
resetMouseTracking()
when initializing or toggling camera modes to avoid sudden jumps. - Separate input handling from game logic for better maintainability.
- For smooth movement, always multiply your velocities by deltaTime.
Keyboard Keys and Mouse Buttons
The Input system defines enumerations for supported keyboard keys and mouse buttons. These enumerations are used as parameters in the various input detection methods.
Keyboard Keys Enumeration
// All supported keyboard keys
enum class Keys {
// Alphabet
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, Key_H, Key_I, Key_J, Key_K, Key_L, Key_M,
Key_N, Key_O, Key_P, Key_Q, Key_R, Key_S, Key_T, Key_U, Key_V, Key_W, Key_X, Key_Y, Key_Z,
// Numbers (top row)
Key_0, Key_1, Key_2, Key_3, Key_4, Key_5, Key_6, Key_7, Key_8, Key_9,
// Function keys
Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11, Key_F12,
Key_F13, Key_F14, Key_F15, Key_F16, Key_F17, Key_F18, Key_F19, Key_F20, Key_F21, Key_F22, Key_F23, Key_F24, Key_F25,
// Numpad
Key_KP_0, Key_KP_1, Key_KP_2, Key_KP_3, Key_KP_4, Key_KP_5, Key_KP_6, Key_KP_7, Key_KP_8, Key_KP_9,
Key_KP_Decimal, Key_KP_Divide, Key_KP_Multiply, Key_KP_Subtract, Key_KP_Add, Key_KP_Enter, Key_KP_Equal,
// Arrow keys
Key_Left, Key_Right, Key_Up, Key_Down,
// Special keys
Key_Space, Key_Escape, Key_Enter, Key_Tab, Key_Backspace, Key_Insert, Key_Delete,
Key_Page_Up, Key_Page_Down, Key_Home, Key_End,
Key_Caps_Lock, Key_Scroll_Lock, Key_Num_Lock, Key_Print_Screen, Key_Pause,
// Modifier keys
Key_Left_Shift, Key_Left_Control, Key_Left_Alt, Key_Left_Super,
Key_Right_Shift, Key_Right_Control, Key_Right_Alt, Key_Right_Super,
// Punctuation and symbols
Key_Apostrophe, // '
Key_Comma, // ,
Key_Minus, // -
Key_Period, // .
Key_Slash, // /
Key_Semicolon, // ;
Key_Equal, // =
Key_Left_Bracket, // [
Key_Backslash, // \
Key_Right_Bracket, // ]
Key_Grave_Accent, // `
Key_Count // Number of keys
};
Mouse Buttons Enumeration
// All supported mouse buttons
enum class Buttons {
Mouse_Left, // Left mouse button
Mouse_Right, // Right mouse button
Mouse_Middle, // Middle mouse button (scroll wheel click)
Button_Count // Number of buttons
};
Key Categories
The keyboard keys are organized into several logical categories:
- Alphabet: All letter keys (A-Z)
- Numbers: Number keys in the top row (0-9)
- Function Keys: All function keys (F1-F25)
- Numpad: Number pad keys and operators
- Arrow Keys: Directional arrow keys
- Special Keys: Common special keys like Space, Enter, Tab, etc.
- Modifier Keys: Shift, Control, Alt, Super (Windows/Command)
- Punctuation and Symbols: All available symbols and punctuation marks
- Non-US Keys: Additional keys found on non-US keyboards
Usage Example
Use these enumerations with the input methods:
// Check if the W key is pressed
if (window->input->isKeyPressed(Raftel::Input::Keys::Key_W)) {
// W key action
}
// Check if the left mouse button is down
if (window->input->isMouseButtonDown(Raftel::Input::Buttons::Mouse_Left)) {
// Left mouse button action
}
// Check for more complex key combinations
if (window->input->isKeyDown(Raftel::Input::Keys::Key_Left_Control) &&
window->input->isKeyPressed(Raftel::Input::Keys::Key_S)) {
// Ctrl+S save action
}
// Use numpad for camera movements
if (window->input->isKeyDown(Raftel::Input::Keys::Key_KP_8)) {
camera.moveForward(deltaTime);
} else if (window->input->isKeyDown(Raftel::Input::Keys::Key_KP_2)) {
camera.moveBackward(deltaTime);
}
API Reference
For a complete reference of the Input class API, check out the Doxygen Documentation.