reaction 0.2.0

Universal low-latency input handling for game engines
Documentation

Reaction

A lightweight input handling library that might actually respond to you.

What This Is (Probably)

Reaction is a zero-cost, action-based input library for Rust game engines. It follows the Unix philosophy (do one thing, hopefully well) and decouples your raw hardware events from your high-level game logic.

Fair Warning: This is a library component, not a game engine. It doesn't render graphics, play sound, or make your coffee. Think of it as the steering wheel and pedals of your car - useless without the rest of the car, but hard to drive without them.

Installation

[dependencies]

reaction = "0.2.0"

Core Concepts

What It Does

  • Action Mapping: Maps physical keys (Space, A) to semantic actions (Jump, Fire).
  • Summative Resolution: Opposing inputs (like A and D keys) correctly cancel each other out.
  • Additive Mouse Deltas: No more lost movement data during high-speed frames.
  • State-Driven: Query the state of any key, button, or axis at any time.
  • ECS Integration: Works with archetype_ecs (should work with others too).

What It Doesn't Do

  • Window Management (use winit or sdl2)
  • Rendering (that's your engine's job)
  • Network Replication (maybe someday?)
  • Mind Reading (still working on the API)

Quick Start

1. Basic Input State (The "I just want it to work" approach)

Directly query the state of devices. Useful for prototyping or debugging.

use reaction::prelude::*;

fn update(input: &AdvancedInputState) {
    // Check if Space is held down
    if input.keyboard.is_key_down(KeyCode::Space) {
        println!("We are floating!");
    }
    
    // Check if Left Mouse Button was just pressed this frame
    if input.mouse.is_button_just_pressed(MouseButton::Left) {
        println!("Pew pew!");
    }
}

2. Action Mapping (The "Professional" approach)

Decouple logic from hardware using semantic actions. This is what you should be using for a real game.

use reaction::prelude::*;

// 1. Define Actions
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Action {
    Jump,
    MoveX,
}

fn main() -> Result<()> {
    // 2. Bind Inputs
    let mut map = InputMap::new();
    
    // Jump when Space is pressed
    map.bind(Action::Jump, BindingConfig::new(InputBinding::Key(KeyCode::Space)))?;
    
    // Move on X axis with Gamepad Stick
    map.bind(Action::MoveX, BindingConfig::new(InputBinding::GamepadAxis(GamepadAxis::LeftStickX)))?;

    // 3. Query Actions
    // (Assume 'input' is populated from your window events)
    let input = AdvancedInputState::new(); 

    // Check digital action (boolean)
    if map.is_active(Action::Jump, &input, None, 0.5) {
        println!("Jumping!");
    }

    // Check analog action (float [-1.0, 1.0])
    let move_speed = map.resolve(Action::MoveX, &input, None);
    println!("Moving speed: {}", move_speed);
    
    Ok(())
}

Performance Notes

What We've Tested:

  • State Querying: ~12 ns/iter (Local Benchmark) - It's fast. Very fast.
  • Zero Allocations: The core resolution loop is allocation-free.
  • Config Loading: Uses ron because we believe in types.

What We Haven't Tested:

  • ❌ Controlling a nuclear power plant (please don't).
  • ❌ Input devices older than 1995.

Troubleshooting

"My move inputs aren't canceling out!"

Check:

  • Are you using InputMap::resolve?
  • Did you bind both Left (scale -1.0) and Right (scale 1.0) to the same Action?
  • Is your keyboard actually working?

"Mouse movement feels jittery!"

Check:

  • Are you initializing the mouse state correctly every frame?
  • Are you calling input_state.end_frame(dt)? This is critical for clearing deltas.

"It crashed."

  • Did you unwrap a Result? I told you not to do that in production.
  • Open an issue. I'll take a look. Eventually.

Contributing

Contributions are welcome, implies you follow the [CODE_OF_CONDUCT.md].

Please ensure:

  • cargo test passes
  • cargo clippy is clean
  • You haven't introduced any new "features" that break existing ones.

License

Licensed under the Apache License, Version 2.0.

Acknowledgments

This library wouldn't exist without:

  • gilrs (for making gamepad support bearable)
  • winit (for the raw events)
  • Caffeine (for the late nights)