Bevy All-in-One Documentation

A powerful 3D/2.5D game controller plugin for Bevy Engine.


Project maintained by yaskhan Hosted on GitHub Pages — Theme by mattgraham

Bevy Input System Documentation

Documentation Contents

The Input System documentation provides comprehensive coverage of a flexible, production-ready input handling framework built for the Bevy game engine. This system delivers robust action mapping, input buffering, runtime rebinding capabilities, and seamless integration between global input state and entity-specific components.

Table of Contents


1. System Overview

1.1 Design Philosophy

The Bevy Input System implements a robust action-based input architecture that decouples physical input devices from logical game actions. This separation enables:

1.2 Key Capabilities

The input system delivers production-ready features essential for modern game development:

1.3 System Boundaries

The input system operates within well-defined boundaries to maintain separation of concerns:

This layered architecture ensures the input system remains focused on input processing without encroaching on gameplay logic responsibilities.


2. Architecture Deep Dive

2.1 Resource/Component Relationship Model

The input system employs a dual-state model combining global resources with entity-specific components:

Global Resources (Engine-Level State)

Entity Components (Gameplay-Level State)

This dual-state model provides critical advantages:

2.2 Data Flow Architecture

Input processing follows a strict unidirectional data flow through discrete processing stages:

Physical Input Devices
         ↓
[Bevy ButtonInput Resources]
         ↓
[update_input_state System]
         ├──→ Binding Translation (InputMap lookup)
         ├──→ Action Buffering (critical actions)
         ├──→ Axis Normalization (movement vectors)
         └──→ State Population (InputState resource)
         ↓
[cleanup_input_buffer System]
         ↓
[player_input_sync_system]
         ↓
[Gameplay Systems Consuming InputState]

Each stage performs a single responsibility with explicit boundaries:

  1. Binding Translation Stage: Raw device inputs (KeyCode::Space) mapped to logical actions (InputAction::Jump) through InputMap configuration. Multiple bindings per action evaluated with OR semantics (any binding triggers the action).

  2. Buffering Stage: Time-critical actions (Jump, Interact, LockOn) captured on just_pressed events and stored in InputBuffer with timestamps. Enables “input forgiveness” for actions occurring between animation frames.

  3. State Population Stage: Translated actions populate InputState fields with appropriate temporal semantics:
    • Continuous actions (MoveForward) set boolean flags for duration of press
    • Momentary actions (Jump) set just_pressed flags only on initial press frame
    • Axis inputs (movement, look) normalized to consistent magnitude
  4. Buffer Cleanup Stage: Expired buffered actions automatically removed based on configurable TTL (buffer_ttl in InputConfig).

  5. Entity Synchronization Stage: Global InputState resource cloned to all entities with InputState components and Player marker component (excluding AI-controlled entities).

This staged architecture ensures deterministic processing order and prevents race conditions between input collection and gameplay consumption.

2.3 Update Schedule Integration

The input system integrates with Bevy’s scheduling system through carefully ordered system execution:

.add_systems(Update, (
    update_input_state,
    handle_rebinding,
    cleanup_input_buffer,
    player_input_sync_system,
).chain())
.add_systems(Update, (
    process_movement_input,
    process_action_input,
));

Critical sequencing considerations:

This scheduling guarantees that gameplay systems always observe a consistent, fully processed input state snapshot for the current frame.


3. Input Action Taxonomy

3.1 Movement Actions

Movement actions form the foundation of player locomotion with orthogonal directional controls:

3.2 Combat Actions

Combat actions support diverse engagement mechanics with weapon handling and defensive capabilities:

3.3 Interaction & Utility Actions

Interaction actions bridge player intent with environmental engagement:

3.4 Advanced Movement & Stealth Actions

Specialized actions supporting advanced locomotion and stealth gameplay:

3.5 Camera Control Actions

Camera manipulation actions (primarily handled through axis inputs rather than discrete actions):

Note: Continuous camera rotation typically handled through raw mouse delta processing rather than discrete actions to enable smooth, analog control. The input system provides the look vector field in InputState for this purpose.


4. Binding System Mechanics

4.1 Binding Types

The system supports two primary physical input types through the InputBinding enumeration:

Keyboard Bindings (InputBinding::Key(KeyCode))

Mouse Bindings (InputBinding::Mouse(MouseButton))

4.2 Multi-Binding Semantics

Each logical action supports multiple physical bindings evaluated with OR semantics:

4.3 Default Binding Configuration

The InputMap::default() implementation provides comprehensive out-of-the-box configuration:

Default bindings represent a balanced starting point optimized for PC keyboard/mouse configurations. Console or alternative input schemes require custom InputMap initialization.


5. Input State Management

5.1 State Representation Structure

The InputState structure maintains comprehensive input snapshot with field categories:

Movement Vectors

Continuous Press States

Boolean flags indicating sustained press duration:

These fields remain true for entire duration of physical input press, enabling analog-like behavior for actions with variable duration effects (e.g., partial crouch depth based on press duration).

Momentary Press States

Boolean flags indicating frame-specific press events:

These fields set true only on the exact frame when physical input transitions from released to pressed. Automatically reset to false on subsequent frames regardless of continued press duration. Critical for actions triggering discrete state transitions (jump initiation, weapon firing).

Specialized State Fields

5.2 Temporal Semantics

Critical distinction between continuous and momentary state fields:

Field Type Physical Input Duration Field Value Duration Typical Use Cases
Continuous (*_pressed) Held for N frames Remains true for all N frames Crouching, sprinting, aiming
Momentary (*_just_pressed) Pressed on frame T true only on frame T Jumping, firing, interacting
Vector (movement, look) Continuous analog input Updated every frame with current values Locomotion, camera rotation

This temporal distinction enables nuanced input handling:

5.3 Input Enable/Disable System

The set_input_enabled() method provides comprehensive input suppression:

pub fn set_input_enabled(&mut self, enabled: bool)

When disabling input (enabled = false):

  1. Sets enabled flag to false
  2. Resets all movement vectors to Vec2::ZERO
  3. Resets all boolean press states to false
  4. Clears weapon selection field (None)
  5. Subsequent input processing skipped until re-enabled

Critical use cases:

Re-enabling input (enabled = true) does not restore previous state—player must provide fresh inputs. This prevents unintended actions from inputs buffered during disabled period.


6. Input Buffering System

6.1 Buffering Rationale

Input buffering addresses critical temporal disconnects between player intent and game state readiness:

6.2 Buffered Action Structure

The BufferedAction structure captures temporal input data:

pub struct BufferedAction {
    pub action: InputAction,
    pub timestamp: f32,
}

Timestamp enables precise expiration management independent of frame rate. Buffer cleanup evaluates (current_time - timestamp) <= buffer_ttl rather than frame-counting.

6.3 Buffer Configuration

Buffer behavior controlled through InputConfig resource:

pub struct InputConfig {
    pub mouse_sensitivity: f32,
    pub gamepad_sensitivity: f32,
    pub invert_y_axis: bool,
    pub buffer_ttl: f32,  // Critical parameter
}

Buffer TTL applies uniformly to all buffered actions. Advanced implementations might support action-specific TTLs through extended configuration.

6.4 Buffered Action Consumption

Two consumption patterns supported through InputBuffer methods:

Consume-and-Remove Pattern

pub fn consume(&mut self, action: InputAction) -> bool

Ensures each buffered action triggers at most one state transition, preventing duplicate processing.

Peek-without-Consumption Pattern

pub fn is_buffered(&self, action: InputAction) -> bool

Supports advanced UX patterns like visual feedback for buffered inputs before state transition occurs.

6.5 Default Buffered Actions

System buffers three critical actions by default:

Gameplay systems may extend buffering to additional actions through custom buffer management outside core input system.

6.6 Buffer Lifecycle Management

Automatic buffer management through dedicated cleanup system:

fn cleanup_input_buffer(
    time: Res<Time>,
    config: Res<InputConfig>,
    mut input_buffer: ResMut<InputBuffer>,
)

This automatic cleanup prevents buffer accumulation and memory growth during extended play sessions.


7. Runtime Rebinding System

7.1 Rebinding Workflow

The rebinding system enables dynamic control remapping through intuitive three-step process:

  1. Initiation: Gameplay system sets RebindState.action = Some(target_action) when player enters rebinding UI for specific action.

  2. Capture: System monitors all physical inputs (keyboard keys, mouse buttons) on subsequent frames. First detected input becomes new binding.

  3. Assignment: Detected input automatically assigned as sole binding for target action in InputMap, replacing previous bindings. RebindState.action reset to None.

This workflow requires zero gameplay system involvement beyond initiation—capture and assignment handled entirely by handle_rebinding system.

7.2 Rebind State Resource

pub struct RebindState {
    pub action: Option<InputAction>,
}

State resource enables rebinding initiation from any system with mutable access to RebindState. Typical initiation pattern:

// In UI interaction system when player selects "Jump" for rebinding
rebind_state.action = Some(InputAction::Jump);
// Next physical input automatically becomes new jump binding

7.3 Input Capture Priority

During rebinding mode (RebindState.action.is_some()):

This priority ordering reflects typical rebinding expectations—keyboard rebinding more common than mouse button rebinding.

7.4 Binding Replacement Semantics

New bindings completely replace existing bindings for target action:

This simplification ensures rebinding predictability—players always know exactly which physical input triggers an action after rebinding.

7.5 Rebinding Safety Considerations

Critical safety mechanisms prevent problematic rebinding scenarios:


8. System Execution Details

8.1 Primary Input Processing System

fn update_input_state(
    time: Res<Time>,
    keyboard: Res<ButtonInput<KeyCode>>,
    mouse_buttons: Res<ButtonInput<MouseButton>>,
    input_map: Res<InputMap>,
    mut input_state: ResMut<InputState>,
    mut input_buffer: ResMut<InputBuffer>,
)

Execution Preconditions

Binding Translation Implementation

Closure-based binding evaluation provides efficient action checking:

let check_action = |action: InputAction| -> bool {
    if let Some(bindings) = input_map.bindings.get(&action) {
        bindings.iter().any(|binding| match binding {
            InputBinding::Key(code) => keyboard.pressed(code.clone()),
            InputBinding::Mouse(button) => mouse_buttons.pressed(button.clone()),
        })
    } else {
        false
    }
};

Separate closure for just_pressed evaluation follows identical pattern with just_pressed() instead of pressed().

Buffer Population Logic

Buffered actions captured on exact frame of press:

for action in actions_to_buffer {
    if check_action_just_pressed(action) {
        input_buffer.actions.push(BufferedAction {
            action,
            timestamp: time.elapsed_seconds(),
        });
    }
}

Movement Vector Construction

Orthogonal axis combination with normalization:

let mut movement = Vec2::ZERO;
if check_action(InputAction::MoveForward) { movement.y += 1.0; }
if check_action(InputAction::MoveBackward) { movement.y -= 1.0; }
if check_action(InputAction::MoveLeft) { movement.x -= 1.0; }
if check_action(InputAction::MoveRight) { movement.x += 1.0; }
input_state.movement = movement.normalize_or_zero();

Weapon Selection Implementation

Mutually exclusive numeric selection with priority ordering:

input_state.select_weapon = None;
if check_action_just_pressed(InputAction::SelectWeapon1) { input_state.select_weapon = Some(0); }
else if check_action_just_pressed(InputAction::SelectWeapon2) { input_state.select_weapon = Some(1); }
// ... continues through SelectWeapon0

8.2 Buffer Cleanup System

fn cleanup_input_buffer(
    time: Res<Time>,
    config: Res<InputConfig>,
    mut input_buffer: ResMut<InputBuffer>,
)

8.3 Player Input Synchronization System

fn player_input_sync_system(
    input_state: Res<InputState>,
    mut query: Query<&mut InputState, (With<Player>, Without<AiController>)>,
)

Critical query constraints:

Synchronization mechanism:

8.4 Stub Systems: Movement and Action Processing

fn process_movement_input(_input: Res<InputState>) {}
fn process_action_input(_input: Res<InputState>) {}

These placeholder systems represent integration points for gameplay logic:

Current stub implementation enables:

Production implementations would replace stubs with systems containing actual character movement, weapon firing, and interaction logic.


9. Configuration System

9.1 InputConfig Resource Structure

pub struct InputConfig {
    pub mouse_sensitivity: f32,
    pub gamepad_sensitivity: f32,
    pub invert_y_axis: bool,
    pub buffer_ttl: f32,
}

Sensitivity Parameters

Axis Inversion

Buffer Configuration

9.2 Configuration Initialization

Default implementation provides balanced starting point:

impl Default for InputConfig {
    fn default() -> Self {
        Self {
            mouse_sensitivity: 0.15,
            gamepad_sensitivity: 1.0,
            invert_y_axis: false,
            buffer_ttl: 0.15,
        }
    }
}

Game-specific tuning recommended before production release:

9.3 Runtime Configuration Updates

InputConfig is a standard Bevy resource enabling runtime modification:

// In settings UI system when player adjusts slider
config.mouse_sensitivity = new_value;

Changes take effect immediately on next frame:

Persistence requires explicit serialization/deserialization as with InputMap.


10. Integration Patterns

10.1 Player Controller Integration

Recommended integration pattern for character controllers:

// In character movement system
fn character_movement(
    input: Res<InputState>,
    mut query: Query<(&mut Transform, &mut CharacterController), With<Player>>,
) {
    if !input.enabled {
        return; // Respect global input disable state
    }
    
    for (mut transform, mut controller) in query.iter_mut() {
        // Apply movement vector with sprint modifier
        let speed = if input.sprint_pressed { 
            controller.sprint_speed 
        } else { 
            controller.walk_speed 
        };
        
        let movement = input.movement * speed * time.delta_seconds();
        // ... apply movement to transform
    }
}

Critical integration considerations:

10.2 Jump Buffering Implementation

Leveraging buffered jump input in character controller:

fn jump_handling(
    mut input_buffer: ResMut<InputBuffer>,
    mut query: Query<&mut CharacterController>,
) {
    for mut controller in query.iter_mut() {
        // Check both direct input and buffered input
        let wants_to_jump = input.jump_pressed || input_buffer.consume(InputAction::Jump);
        
        if wants_to_jump && controller.can_jump() {
            controller.initiate_jump();
        }
    }
}

Buffer consumption pattern ensures:

10.3 Weapon System Integration

Weapon selection and firing integration pattern:

fn weapon_system(
    input: Res<InputState>,
    mut weapons: ResMut<WeaponInventory>,
) {
    // Handle weapon selection
    if let Some(slot) = input.select_weapon {
        weapons.equip_slot(slot);
    }
    
    // Handle firing with buffering consideration
    if input.fire_just_pressed || input_buffer.consume(InputAction::Fire) {
        weapons.active_weapon.fire();
    }
    
    // Handle reload
    if input.reload_pressed {
        weapons.active_weapon.reload();
    }
}

Selection handling considerations:

10.4 Menu System Integration

Input disable pattern for pause menus:

fn pause_menu_system(
    mut input_state: ResMut<InputState>,
    keyboard: Res<ButtonInput<KeyCode>>,
    mut menu_state: ResMut<MenuState>,
) {
    // Toggle pause on ESC press
    if keyboard.just_pressed(KeyCode::Escape) {
        menu_state.paused = !menu_state.paused;
        
        // Disable player input when paused
        input_state.set_input_enabled(!menu_state.paused);
    }
    
    // UI navigation uses separate input context not affected by disable
    if menu_state.paused {
        // Process UI navigation inputs here
        // These would use direct ButtonInput checks rather than InputState
    }
}

Critical separation:

10.5 Camera System Integration

Camera control integration leveraging look vector:

fn camera_control(
    input: Res<InputState>,
    config: Res<InputConfig>,
    mut query: Query<&mut CameraController>,
) {
    for mut controller in query.iter_mut() {
        // Apply sensitivity multiplier to raw look input
        let look_delta = input.look * config.mouse_sensitivity;
        
        // Apply Y-axis inversion if configured
        let look_delta = Vec2::new(look_delta.x, 
            if config.invert_y_axis { -look_delta.y } else { look_delta.y }
        );
        
        // Apply rotation to camera
        controller.yaw += look_delta.x;
        controller.pitch += look_delta.y;
        
        // Clamp pitch to prevent gimbal lock
        controller.pitch = controller.pitch.clamp(-1.5, 1.5);
    }
}

Look vector processing responsibilities:


11. Best Practices & Design Patterns

11.1 Input Action Design Principles

Semantic Action Naming

Action Granularity

Buffering Strategy

11.2 Binding Configuration Guidelines

Conflict Avoidance

Accessibility Considerations

Platform Adaptation

11.3 Buffer TTL Tuning Methodology

Systematic approach to buffer configuration:

  1. Measure Animation Durations: Profile critical animation sequences:
    • Jump landing recovery: typically 150-300ms
    • Weapon reload completion: varies by weapon type
    • Interaction approach window: distance-dependent
  2. Set Initial TTL: Configure buffer_ttl to 75% of shortest critical animation:
    • Landing recovery 200ms → initial TTL 150ms
    • Ensures inputs captured during majority of lockout period
  3. Playtest with Edge Cases:
    • Rapid jump sequences (jump immediately after landing)
    • Interaction attempts while running at full speed
    • Weapon switching during reload animations
  4. Adjust Based on Feedback:
    • “Missed jumps” complaints → increase TTL by 25ms increments
    • “Unintended double jumps” complaints → decrease TTL by 25ms increments
    • Target 95%+ success rate on intentional rapid inputs
  5. Document Rationale: Record final TTL value with justification:
    • “175ms TTL balances jump forgiveness with prevention of accidental double-jumps during sprinting”

11.4 Input Disable Patterns

Granular input disabling strategies:

Full Disable

input_state.set_input_enabled(false);

Use cases:

Partial Disable (Custom Implementation)

Core system doesn’t support partial disable—implement through wrapper:

// Custom resource tracking disabled action groups
#[derive(Resource)]
struct DisabledInputGroups {
    movement: bool,
    combat: bool,
    camera: bool,
}

// System filtering actions based on disabled groups
fn filtered_input_update(
    disabled: Res<DisabledInputGroups>,
    raw_input: Res<InputState>,
    mut filtered: ResMut<FilteredInputState>,
) {
    filtered.movement = if disabled.movement { Vec2::ZERO } else { raw_input.movement };
    filtered.jump_pressed = if disabled.movement { false } else { raw_input.jump_pressed };
    // ... etc
}

Use cases:

11.5 Testing Methodologies

Comprehensive input testing strategy:

Automated Binding Validation

#[test]
fn test_no_duplicate_bindings() {
    let map = InputMap::default();
    let mut binding_usage = HashMap::new();
    
    for (action, bindings) in &map.bindings {
        for binding in bindings {
            let count = binding_usage.entry(binding.clone()).or_insert(0);
            *count += 1;
        }
    }
    
    // Allow up to 2 uses per binding (intentional conflicts)
    for (binding, count) in &binding_usage {
        assert!(count <= &2, "Binding {:?} used {} times", binding, count);
    }
}

Buffer Timing Tests

#[test]
fn test_jump_buffer_timing() {
    let mut buffer = InputBuffer::default();
    let config = InputConfig { buffer_ttl: 0.15, ..default() };
    
    // Add jump at t=0.0
    buffer.actions.push(BufferedAction { 
        action: InputAction::Jump, 
        timestamp: 0.0 
    });
    
    // Should be present at t=0.14
    buffer.actions.retain(|ba| 0.14 - ba.timestamp <= config.buffer_ttl);
    assert_eq!(buffer.actions.len(), 1);
    
    // Should be expired at t=0.16
    buffer.actions.retain(|ba| 0.16 - ba.timestamp <= config.buffer_ttl);
    assert_eq!(buffer.actions.len(), 0);
}

Edge Case Simulation

Manual testing scenarios:


12. Troubleshooting Guide

12.1 Common Issues & Solutions

Issue: Inputs Not Registering

Symptoms: Player presses keys but character doesn’t move/act.

Diagnosis Steps:

  1. Verify input_state.enabled == true (not disabled by menu/cutscene)
  2. Check InputMap contains bindings for expected actions
  3. Confirm no binding conflicts consuming inputs unexpectedly
  4. Validate systems execute in correct order (input update before gameplay)

Solutions:

Issue: Double Actions from Single Press

Symptoms: Single key press triggers action twice (double jump, double fire).

Diagnosis Steps:

  1. Verify gameplay system consumes buffered actions with consume() not is_buffered()
  2. Check for duplicate system registration processing same inputs
  3. Confirm no rebinding state active causing input capture duplication

Solutions:

Issue: Diagonal Movement Faster Than Cardinal

Symptoms: Character moves faster when pressing two movement keys simultaneously.

Diagnosis Steps:

  1. Verify movement.normalize_or_zero() called in input processing
  2. Check gameplay systems not re-normalizing already normalized vectors
  3. Confirm no speed multipliers applied differentially to diagonal movement

Solutions:

Issue: Rebinding Not Capturing Inputs

Symptoms: Rebinding UI active but inputs not captured as new bindings.

Diagnosis Steps:

  1. Verify RebindState.action properly set to Some(action)
  2. Check no other systems consuming inputs before rebinding system
  3. Confirm rebinding system executes after input state update but before gameplay systems

Solutions:

Issue: Buffer Overflow / Memory Growth

Symptoms: Memory usage increases steadily during extended play sessions.

Diagnosis Steps:

  1. Profile InputBuffer.actions vector size over time
  2. Verify cleanup_input_buffer system executes every frame
  3. Check for manual buffer additions without corresponding cleanup

Solutions:

12.2 Diagnostic Tools

Input State Visualizer

Debug overlay displaying current input state:

INPUT STATE (enabled: true)
Movement: (0.00, 1.00)  [Forward]
Look: (2.34, -1.87)
Jump: ■ (buffered)
Sprint: □
Crouch: □
Fire: ■ (just pressed)
Weapon: None

Implementation approach:

Binding Conflict Detector

System identifying problematic binding configurations:

fn detect_binding_conflicts(map: Res<InputMap>) {
    let mut binding_to_actions: HashMap<InputBinding, Vec<InputAction>> = HashMap::new();
    
    for (action, bindings) in &map.bindings {
        for binding in bindings {
            binding_to_actions.entry(binding.clone())
                .or_insert_with(Vec::new)
                .push(*action);
        }
    }
    
    for (binding, actions) in &binding_to_actions {
        if actions.len() > 1 {
            warn!("Binding conflict: {:?} mapped to {:?}", binding, actions);
        }
    }
}

Run during development builds to catch configuration issues early.


13. Performance Characteristics

13.1 Computational Complexity

Per-Frame Processing Cost

Memory Footprint

Total typical footprint: <3KB—negligible for modern systems.

13.2 Optimization Opportunities

Binding Lookup Caching

Current implementation performs hashmap lookup per action per frame:

if let Some(bindings) = input_map.bindings.get(&action) { ... }

Optimization potential:

Buffer Structure Optimization

Current Vec<BufferedAction> sufficient for typical use:

Movement Vector Precomputation

Current implementation reconstructs movement vector every frame:

Conclusion: System already highly optimized for typical workloads. Micro-optimizations unlikely to yield measurable gains in real-world scenarios.

13.3 Scalability Considerations

Multiplayer Scaling

System scales linearly with player count:

Action Set Expansion

Adding actions scales linearly:

Input Device Expansion

Adding gamepad support requires:


14. Extension Points & Future Enhancements

14.1 Gamepad Support Integration

Current architecture supports straightforward gamepad extension:

Required Modifications

  1. Extend InputBinding enumeration:
    pub enum InputBinding {
        Key(KeyCode),
        Mouse(MouseButton),
        Gamepad(GamepadButton),
    }
    
  2. Add gamepad resource access to update_input_state:
    gamepad_buttons: Res<ButtonInput<GamepadButton>>,
    
  3. Extend binding evaluation closures:
    InputBinding::Gamepad(button) => gamepad_buttons.pressed(button.clone()),
    
  4. Add gamepad axis processing for analog movement:
    // In movement construction
    let gamepad_axis = Vec2::new(
        gamepad_axes.axis(GamepadAxis::LEFT_STICK_X),
        gamepad_axes.axis(GamepadAxis::LEFT_STICK_Y),
    );
    movement += gamepad_axis;
    

Design Considerations

14.2 Advanced Buffering Features

Action-Specific TTL Configuration

Current uniform TTL may not suit all actions:

Extension approach:

pub struct BufferConfig {
    pub default_ttl: f32,
    pub overrides: HashMap<InputAction, f32>,
}

// In buffer population
let ttl = config.overrides.get(&action).copied().unwrap_or(config.default_ttl);

Input Chording Support

Buffer combinations of simultaneous inputs:

Implementation strategy:

14.3 Input Recording & Playback

Replay system for:

Core requirements:

Extension architecture:

#[derive(Resource)]
pub struct InputRecorder {
    pub recording: bool,
    pub playback: bool,
    pub events: Vec<InputEvent>,
}

pub struct InputEvent {
    pub timestamp: f32,
    pub action: InputAction,
    pub pressed: bool,
}

Challenges:

14.4 Accessibility Enhancements

Input Remapping Profiles

Predefined control schemes for accessibility needs:

Implementation:

pub enum ControlProfile {
    Standard,
    OneHandedLeft,
    OneHandedRight,
    MouseOnly,
    Simplified,
}

impl ControlProfile {
    pub fn apply_to_map(&self, map: &mut InputMap) {
        match self {
            ControlProfile::OneHandedLeft => {
                // Remap all actions to left-hand accessible keys
                map.bindings.insert(InputAction::Jump, vec![InputBinding::Key(KeyCode::Space)]);
                // ... etc
            }
            // ... other profiles
        }
    }
}

Input Assistance Features


15.1 Character Controller System

Tight integration required for responsive movement:

Critical interface:

// CharacterController component provides state query methods
impl CharacterController {
    pub fn can_jump(&self) -> bool {
        self.is_grounded && !self.jump_cooldown.active()
    }
}

15.2 Weapon System

Action consumption patterns:

State synchronization:

15.3 Camera System

Look vector processing responsibilities:

Separation of concerns:

15.4 UI / Menu System

Critical separation patterns:

Menu state machine:

GameState::Playing 
  → ESC pressed 
  → GameState::Paused (disable gameplay input, enable UI navigation)
  → Resume selected 
  → GameState::Playing (enable gameplay input, disable UI navigation)

15.5 Animation System

Input-driven animation selection:

Animation-state feedback:


16. Conclusion & Recommendations

16.1 System Maturity Assessment

The input system demonstrates production-ready characteristics:

Recommended for production use with minor configuration tuning for target game genre.

16.2 Implementation Recommendations

Pre-Production Phase

  1. Audit default bindings for conflicts specific to your game:
    • Resolve LeanRight/Interact conflict (both default to E)
    • Resolve CornerLean/SwitchCameraMode conflict (both default to C)
    • Document final binding decisions in design documentation
  2. Determine buffer TTL based on animation durations:
    • Profile jump landing recovery animations
    • Set initial buffer_ttl to 75% of shortest critical animation
    • Plan playtesting session specifically for buffer tuning
  3. Design rebinding UI with conflict detection:
    • Implement visual warnings for binding conflicts
    • Provide one-click resolution options (reassign conflicting action)
    • Include “Restore Defaults” functionality

Production Phase

  1. Implement input state visualizer for QA:
    • Overlay showing current input state during gameplay
    • Highlight buffered actions with distinct visual treatment
    • Enable via debug key combination (e.g., F12)
  2. Conduct accessibility review:
    • Verify all critical actions have keyboard alternatives
    • Test with input delay simulators to validate buffer effectiveness
    • Gather feedback from players with motor impairments
  3. Performance profiling:
    • Measure input system cost across target hardware spectrum
    • Verify no frame drops during input mashing scenarios
    • Confirm memory footprint stability during extended sessions

Post-Launch Considerations

  1. Monitor rebinding analytics:
    • Track most frequently rebound actions
    • Identify problematic default bindings for future titles
    • Correlate rebinding patterns with player retention metrics
  2. Plan input system evolution:
    • Prioritize gamepad support based on player platform distribution
    • Evaluate demand for advanced accessibility features
    • Prepare architecture for potential VR/AR input modalities

16.3 Final Assessment

This input system provides a robust foundation for diverse game genres with minimal modification. Its action-based architecture, temporal buffering capabilities, and runtime rebinding support address the majority of input handling requirements for modern games. The clean separation between input processing and gameplay logic ensures long-term maintainability as game complexity grows.

Recommended adoption path:

  1. Integrate core system with default bindings (resolving known conflicts)
  2. Tune buffer TTL through targeted playtesting
  3. Implement rebinding UI with conflict detection
  4. Extend with gamepad support when platform requirements demand
  5. Enhance with accessibility features based on player feedback

The system’s thoughtful architecture provides solid footing for both immediate production needs and future evolution as player expectations and input technologies advance.