Skip to content

Input

Input provides key, mouse, gamepad, and action/axis bindings. It exposes both raw device state and higher-level actions.

Key States

  • keyDown(key) returns true while held.
  • keyPressed(key) returns true for a single frame on press.
  • keyReleased(key) returns true for a single frame on release.

Actions and Axes

  • bindAction(name, key) maps exactly one key to an action. Binding the same name again replaces its previous key.
  • actionDown(name) reports whether that key is held.
  • actionPressed(name) reports whether that key was pressed this frame.
  • bindAxis(name, negative, positive) maps a pair of keys to a float axis.
  • Binding an axis name again replaces its previous key pair.
  • axis(name) returns positive-state minus negative-state: -1, 0, or 1. Opposing held keys cancel to 0.

On desktop, addActionBinding() and addAxisBinding() append keyboard or mouse-button alternatives, while addGamepadActionBinding() and addGamepadAxisBinding() add standard gamepad sources. Aggregated axes are clamped to [-1, 1]. Action edges also describe the aggregate: pressing a second source while an action is held does not emit another press, and a release is emitted only after every bound source is up. A complete tap within one frame still reports both edges. See Desktop Production for raw queries, deadzones, connection events, and rebinding methods.

Mouse

  • mousePosition() and mouseDelta()
  • scrollDelta()
  • mouseScale(x, y) to scale coordinates to logical space
  • Mouse buttons use KeyCode::MouseLeft, MouseRight, and MouseMiddle with the same keyDown, keyPressed, and keyReleased queries as keyboard keys.

Hosts normally set mouseScale() from the surface-to-logical-size ratio. Changing it resets the mouse baseline and delta; non-positive scale components fall back to 1.0f.

On web builds, a mouse drag that begins on the game canvas continues to update mousePosition() until its button is released, even while the pointer is over another page element or outside the canvas. Outside-canvas positions are not clamped and may be negative or greater than the logical viewport. A press that begins outside the canvas is not forwarded to the game. Losing browser focus or hiding the page releases held mouse buttons so they cannot remain stuck.

Touch (iOS and Android)

The iOS and Android hosts feed stable platform pointer IDs through these methods:

  • onTouchBegan(id, x, y)
  • onTouchMoved(id, x, y)
  • onTouchEnded(id, x, y)
  • onTouchCanceled(id)

Per-frame multi-touch state is queryable via Input::touches(), which returns a std::vector<Touch>. Each Touch carries:

  • id — stable for the lifetime of the touch
  • position — already scaled to logical space (same convention as mousePosition())
  • isDown, isPressed, isReleased

The first active touch is treated as the primary pointer: its position is mirrored onto mousePosition() and its press/release drives KeyCode::MouseLeft. Scenes and UI written against mouse input therefore work unchanged on touch platforms. Secondary touches do not change mouse state.

When the primary touch ends or is canceled, MouseLeft is released even if another touch remains. The next active touch becomes primary for subsequent position updates, but the engine does not synthesize a replacement MouseLeft press. An ended or canceled touch remains in touches() for its release frame with isReleased=true, then is removed at the end of the frame. Use touches() for true multi-touch and gesture continuity.

auto* input = context()->input();
for (const auto& touch : input->touches()) {
    if (touch.isPressed) {
        // Begin tracking this pointer.
    } else if (touch.isReleased) {
        // Finish tracking this pointer.
    } else if (touch.isDown) {
        // Update this pointer's gesture.
    }
}

Platform Notes

Platform Input source
Windows/macOS GLFW keyboard, mouse, scroll, and standard-layout gamepad input
Web Browser keyboard, mouse/pointer, and wheel events through Emscripten/GLFW
iOS UIKit touch events; the primary touch is mirrored as the left mouse button
Android GameActivity multi-touch and standard hardware keys; primary touch mirrors MouseLeft; Back uses the app Back contract; volume/mute/camera buttons remain system-owned

The web canvas, iOS view, and Android surface scale pointer coordinates into the engine's logical coordinate space. Use the scene/UI conversion helpers in Runtime when moving positions between world and UI space.

Sample Usage

auto* input = context()->input();
input->bindAction("activate", snowpulse::KeyCode::Space);
input->bindAxis("horizontal", snowpulse::KeyCode::A, snowpulse::KeyCode::D);
#if defined(SNOW_PLATFORM_DESKTOP)
input->addGamepadActionBinding(
    "activate", snowpulse::Input::kAnyGamepad, snowpulse::GamepadButton::A);
input->addGamepadAxisBinding(
    "horizontal", snowpulse::Input::kAnyGamepad, snowpulse::GamepadAxis::LeftX);
#endif

if (input->actionPressed("activate")) {
    // Perform the bound action once.
}

const float horizontal = input->axis("horizontal");

if (input->keyPressed(snowpulse::KeyCode::MouseLeft)) {
    const glm::vec2 pointer = input->mousePosition();
    const glm::vec2 motion = input->mouseDelta();
}