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:

Accessing Input
// 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:

Keyboard Input Example
// 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:

Mouse Input Example
// 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:

First Person Camera Control
#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

Input::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

Input::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.