Skip to main content

Input System

Source: client/engine/systems/systems_functions/InputSystem.cpp

Purpose: Read logical action state from the InputManager and populate the Inputs component for player-controlled entities.


Overview

The Input System bridges the gap between the Input Abstraction Layer and the ECS. It queries the InputManager for action states and updates Inputs components accordingly.

This system uses logical actions (e.g., MoveLeft, Shoot) instead of physical keys, making the game code independent of the underlying input backend (SFML, SDL, etc.).


Components Used

ComponentAccessDescription
InputsWriteStores movement direction and shoot state

Dependencies

DependencyTypeDescription
InputManager<Game::Action>ReferenceProvides action state queries

Behavior

  1. Focus Check: Returns immediately if the window doesn't have focus
  2. Action Queries: For each entity with Inputs:
    • Uses GetAxis() for smooth movement values (-1.0 to +1.0)
    • Uses IsActionActive() for discrete actions (shoot)
  3. State Tracking: Preserves last_shoot_state for edge detection

Action to Input Mapping

Component FieldNegative ActionPositive Action
horizontalMoveLeftMoveRight
verticalMoveUpMoveDown
shoot-Shoot

Function Signature

void InputSystem(
Engine::registry &reg,
Engine::Input::InputManager<Game::Action> &input_manager,
Engine::sparse_array<Component::Inputs> &inputs);

Parameters

ParameterDescription
regEngine registry (unused but required for system signature)
input_managerThe input manager configured with game bindings
inputsSparse array of Inputs components to update

Implementation

void InputSystem(Engine::registry &reg,
Engine::Input::InputManager<Game::Action> &input_manager,
Engine::sparse_array<Component::Inputs> &inputs) {

// Ignore input when window loses focus
if (!input_manager.HasFocus())
return;

for (auto &&[i, input] : make_indexed_zipper(inputs)) {
// Track previous shoot state for edge detection
input.last_shoot_state = input.shoot;

// Use logical actions via GetAxis helper
// Returns -1.0, 0.0, or +1.0 based on action state
input.horizontal = input_manager.GetAxis(
Game::Action::MoveLeft,
Game::Action::MoveRight);

input.vertical = input_manager.GetAxis(
Game::Action::MoveUp,
Game::Action::MoveDown);

// Discrete action for shooting
input.shoot = input_manager.IsActionActive(Game::Action::Shoot);
}
}

Default Key Bindings

The InputManager is configured with these default bindings (set in GameInputBindings.hpp):

Movement

ActionPrimarySecondaryTertiary
MoveUpZW Arrow
MoveDownS- Arrow
MoveLeftQA Arrow
MoveRightD- Arrow

Combat

ActionPrimarySecondary
ShootSpaceLeft Mouse Button

Network Integration

The Inputs component is converted to a bitfield for network transmission:

uint8_t InputToBitfield(const Component::Inputs &input) {
uint8_t bitfield = 0;

if (input.vertical < 0.0f) bitfield |= (1 << 0); // Up
if (input.vertical > 0.0f) bitfield |= (1 << 1); // Down
if (input.horizontal < 0.0f) bitfield |= (1 << 2); // Left
if (input.horizontal > 0.0f) bitfield |= (1 << 3); // Right
if (input.shoot) bitfield |= (1 << 4); // Shoot

return bitfield;
}

This follows the RFC Section 6.1 (PLAYER_INPUT) specification.


Notes

  • No Physical Keys: This system never checks sf::Keyboard directly
  • Focus Handling: Prevents unintended input when alt-tabbed
  • Backend Agnostic: Works with any IInputBackend implementation
  • Key Remapping: Change bindings at runtime via InputManager::BindKey()