Skip to main content

Input

The input.h header provides input handling capabilities for Atlas Engine, including keyboard and mouse input processing. All input events are processed through the Interactive base class.

Key Enum

Enumeration of all keyboard keys. Each key is a valid keyboard input.
enum class Key : int {
    // Alphanumeric
    Key0, Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9,
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
    
    // Special characters
    Space, Apostrophe, Comma, Minus, Period, Slash,
    Semicolon, Equal, LeftBracket, Backslash, RightBracket, GraveAccent,
    
    // Control keys
    Escape, Enter, Tab, Backspace, Insert, Delete,
    Right, Left, Down, Up,
    PageUp, PageDown, Home, End,
    
    // Lock keys
    CapsLock, ScrollLock, NumLock, PrintScreen, Pause,
    
    // Function keys
    F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
    F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, F25,
    
    // Keypad
    KP0, KP1, KP2, KP3, KP4, KP5, KP6, KP7, KP8, KP9,
    KPDecimal, KPDivide, KPMultiply, KPSubtract, KPAdd, KPEnter, KPEqual,
    
    // Modifiers
    LeftShift, LeftControl, LeftAlt, LeftSuper,
    RightShift, RightControl, RightAlt, RightSuper,
    Menu,
    
    Unknown
};

Usage Example

class MainScene : public Scene {
public:
    void update(Window &window) override {
        // Check if key is currently pressed
        if (window.isKeyPressed(Key::Escape)) {
            window.releaseMouse();
        }
        
        // Check if key was clicked (single frame)
        if (window.isKeyClicked(Key::Q)) {
            toggleFeature();
        }
        
        // Check for specific input combinations
        if (window.isKeyPressed(Key::W)) {
            moveForward();
        }
    }
};

MouseButton Enum

Enumeration of mouse buttons.
enum class MouseButton : int {
    Button1,  // Generic button 1
    Button2,  // Generic button 2
    Button3,  // Generic button 3
    Button4,  // Generic button 4
    Button5,  // Generic button 5
    Button6,  // Generic button 6
    Button7,  // Generic button 7
    Button8,  // Generic button 8
    Last,     // Last valid button
    
    // Named aliases
    Left,     // Primary button (Button1)
    Right,    // Secondary button (Button2)
    Middle    // Scroll wheel button (Button3)
};

Usage Example

class Interactive {
public:
    void onMouseButtonPress(MouseButton button, float deltaTime) override {
        if (button == MouseButton::Left) {
            // Handle left click
            fireWeapon();
        } else if (button == MouseButton::Right) {
            // Handle right click
            aim();
        }
    }
};

MousePacket

Structure representing mouse movement data. Automatically handled and updated by the Window class.
struct MousePacket {
    float xpos = 0.f;           // Current x position
    float ypos = 0.f;           // Current y position
    float xoffset = 0.f;        // Change in x since last update
    float yoffset = 0.f;        // Change in y since last update
    bool constrainPitch = true; // Prevent camera flipping
    bool firstMouse = true;     // First mouse movement flag
};

Usage Example

class PlayerController : public Interactive {
public:
    void onMouseMove(MousePacket data, float deltaTime) override {
        // Use offset for camera rotation
        float sensitivity = 0.1f;
        yaw += data.xoffset * sensitivity;
        pitch += data.yoffset * sensitivity;
        
        // Constrain pitch if enabled
        if (data.constrainPitch) {
            pitch = std::clamp(pitch, -89.0f, 89.0f);
        }
    }
};

MouseScrollPacket

Structure representing mouse scroll data. Automatically handled by the Window class.
struct MouseScrollPacket {
    float xoffset;  // Scroll offset in x direction
    float yoffset;  // Scroll offset in y direction
};

Usage Example

class Interactive {
public:
    void onMouseScroll(MouseScrollPacket data, float deltaTime) override {
        // Zoom camera with scroll wheel
        float zoomSpeed = 2.0f;
        fieldOfView -= data.yoffset * zoomSpeed;
        fieldOfView = std::clamp(fieldOfView, 1.0f, 120.0f);
    }
};

Interactive Class

Abstract base class for objects that receive and react to input events.
class Interactive {
public:
    virtual ~Interactive() = default;
    
    // Keyboard events
    virtual void onKeyPress(Key key, float deltaTime) {}
    virtual void onKeyRelease(Key key, float deltaTime) {}
    
    // Mouse events
    virtual void onMouseMove(MousePacket data, float deltaTime) {}
    virtual void onMouseButtonPress(MouseButton button, float deltaTime) {}
    virtual void onMouseScroll(MouseScrollPacket data, float deltaTime) {}
    
    // Frame update
    virtual void atEachFrame(float deltaTime) {}
    
    // Last received mouse data
    MousePacket lastMouseData;
};

Usage Example

class PlayerController : public Interactive {
public:
    void onKeyPress(Key key, float deltaTime) override {
        float moveSpeed = 5.0f;
        
        switch (key) {
            case Key::W:
                position += forward * moveSpeed * deltaTime;
                break;
            case Key::S:
                position -= forward * moveSpeed * deltaTime;
                break;
            case Key::A:
                position -= right * moveSpeed * deltaTime;
                break;
            case Key::D:
                position += right * moveSpeed * deltaTime;
                break;
            case Key::Space:
                jump();
                break;
        }
    }
    
    void onKeyRelease(Key key, float deltaTime) override {
        if (key == Key::LeftShift) {
            stopSprinting();
        }
    }
    
    void onMouseMove(MousePacket data, float deltaTime) override {
        // Look around based on mouse movement
        float sensitivity = 0.1f;
        yaw += data.xoffset * sensitivity;
        pitch -= data.yoffset * sensitivity;
        
        // Update direction vectors
        updateVectors();
    }
    
    void onMouseButtonPress(MouseButton button, float deltaTime) override {
        if (button == MouseButton::Left) {
            fire();
        }
    }
    
    void atEachFrame(float deltaTime) override {
        // Continuous updates every frame
        applyGravity(deltaTime);
        updateAnimation(deltaTime);
    }
    
private:
    Position3d position;
    Position3d forward, right, up;
    float yaw = 0.0f;
    float pitch = 0.0f;
    
    void updateVectors() {
        // Calculate forward vector from yaw and pitch
        forward.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
        forward.y = sin(glm::radians(pitch));
        forward.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
        forward = forward.normalized();
        
        // Calculate right and up vectors
        right = Position3d::fromGlm(
            glm::normalize(glm::cross(forward.toGlm(), glm::vec3(0, 1, 0))));
        up = Position3d::fromGlm(
            glm::normalize(glm::cross(right.toGlm(), forward.toGlm())));
    }
};

Complete Example: Scene with Input

class MainScene : public Scene {
    Camera camera;
    bool isPaused = false;
    
public:
    void initialize(Window &window) override {
        camera = Camera();
        camera.setPosition({-5.0f, 1.0f, 0.0f});
        camera.lookAt({0.0f, 1.0f, 0.0f});
        window.setCamera(&camera);
    }
    
    void update(Window &window) override {
        if (isPaused) {
            return;
        }
        
        // Camera updates
        camera.update(window);
        
        // Handle pause
        if (window.isKeyPressed(Key::Escape)) {
            window.releaseMouse();
            isPaused = true;
        }
        
        // Toggle features
        if (window.isKeyClicked(Key::F)) {
            window.toggleFullscreen();
        }
        
        if (window.isKeyClicked(Key::V)) {
            window.toggleVSync();
        }
    }
    
    void onMouseMove(Window &window, Movement2d movement) override {
        if (!isPaused) {
            camera.updateLook(window, movement);
        }
    }
};

Component Example: Breaking Joints

class BallBehavior : public Component {
public:
    void init() override {
        if (object && object->rigidbody) {
            object->rigidbody->applyImpulse({0.0f, 0.0f, 20.0f});
        }
    }
    
    void update(float deltaTime) override {
        // Check for key input in component
        if (Window::mainWindow->isKeyClicked(Key::N)) {
            auto hinge = object->getComponent<HingeJoint>();
            if (hinge) {
                std::cout << "Breaking hinge joint\n";
                hinge->breakJoint();
            }
        }
    }
};

Best Practices

  1. Use isKeyPressed() for continuous input: Movement, aiming, etc.
  2. Use isKeyClicked() for single-action events: Jumping, toggling features, menu actions
  3. Override only needed methods: Leave unused event handlers unimplemented
  4. Store mouse sensitivity as a configurable value: Allow users to adjust
  5. Clamp pitch values: Prevent camera gimbal lock by constraining pitch to [-89, 89] degrees
  6. Release mouse on pause: Call window.releaseMouse() when pausing
  7. Check for null before accessing components: Always verify objects exist before using them
  8. Use deltaTime for movement: Multiply movement by deltaTime for frame-rate independent motion

Build docs developers (and LLMs) love