Skip to content

Core.Input

Dennis Steffen edited this page Jan 4, 2026 · 1 revision

The input system in your project is a multi-layered, decoupled architecture designed to handle various input sources (Keyboard, Gamepads, etc.) and map them to player-specific game actions.

Here is an explanation of the core system components and how they interact.

1. The Atomic Layer: IInput and BaseInput

The foundation of the system is the IInput interface, which represents the state of a single button or axis.

  • BaseInput: This is an abstract class that implements common logic for any input. It handles:
    • Normalization: Clamping values between -1 and 1.
    • Edge Detection: Using an internal EdgeDetector to determine IsPressed (rising edge), Down (current state), and Up.
    • Animations: It supports an IButtonAnimation (like the BlinkAnimation seen in MainGameObject) to provide visual feedback based on input state.

2. The Hardware Layer: SDLControllerDevice and ControllerInput

This layer interfaces with physical hardware.

  • SDLControllerDevice: Wraps the SDL2 controller API. it creates a dictionary of ControllerInput objects for every button and axis on a gamepad.
  • ControllerInput: A concrete implementation of BaseInput that polls the hardware state via a delegate (e.g., device.GetButton(...)).

3. The Mapping Layer: GenericMapper<T>

This class maps a generic Enum T (representing game actions like Jump or Action) to specific IInput instances.

  • Action Mapping: You use AddInput to bind an enum value to a specific hardware function or a lambda.
  • Axis Aggregation: It uses GenericAxisInput<T> to combine four discrete directional inputs (Up, Down, Left, Right) into a single Vector2 (e.g., for movement).
  • Persistence: It supports IGenericInputMapSaver to load and save player-specific keybindings.

4. The Routing Layer: PlayerInputRouter<T>

This is the central "brain" of the input system. It implements IInputMapper<T> itself and acts as a dispatcher.

  • Multi-Player Support: It maintains a dictionary of IInputMapper instances assigned to different player IDs.
  • Abstraction: The game logic asks the PlayerInputRouter for the state of "Player 1, Action", and the router decides whether that comes from a Keyboard mapper or a Gamepad mapper.
  • Auto-Assignment: It can automatically switch the active mapper if AnyInputPressed is detected on a specific device.

5. The Management Layer: InputManager<T>

This service handles the high-level lifecycle of players.

  • Player Joining: It manages the AssignMode, where it waits for a player to press a button and then tells the PlayerInputRouter to link that physical device profile to a player slot.
  • Status Tracking: It tracks PlayerInputStatus (ID, Online status, and PlayerInputType).

Examples of Usage

Defining and Querying Input

In your game logic (like in MainGameObject), you interact with the system via the router using your game-specific enum:

// Inside MainGameObject.cs
private PlayerInputRouter<GameInput> _controller;

protected override void OnInitialize()
{
    // Retrieve the router from the Object Manager
    _controller = GlobalObjectManager.ObjectManager.Get<PlayerInputRouter<GameInput>>()!;
}

protected override void OnPreUpdate(float deltaTime)
{
    // Check if Player 1 pressed the "Action" button this frame
    bool isPressed = _controller.GetState(1, GameInput.Action).IsPressed;
    
    // Get movement axis for Player 1
    Vector2 moveDir = _controller.GetAxis(1);
}

Configuring a Mapper

When setting up the engine, you might configure a GenericMapper for the keyboard:

// Example setup for Keyboard
var keyboardMapper = new GenericMapper<GameInput>();

// Map GameInput.Action to a specific function (e.g., Space key check)
keyboardMapper.AddInput(0, GameInput.Action, "Jump", () => MyKeyboardHardware.IsKeyDown(Space));

// Map an axis using four directions
keyboardMapper.AddAxis(0, 1, GameInput.Left, GameInput.Right, GameInput.Up, GameInput.Down);

// Register the mapper to the router
router.AddMapper(keyboardMapper);

Applying Animations to Inputs

Because BaseInput supports animations, you can trigger visual logic directly on the input object:

// Inside MainGameObject.cs OnLateUpdate
if (InternalState == GameInternalState.PlayerIsApplyingForce)
{
    // Make the physical button on the arcade cabinet/UI blink rapidly
    _controller.GetState(1, GameInput.Action).Animation = new BlinkAnimation(100);
}

Summary of Component Flow

  1. Hardware (SDLControllerDevice) produces raw data.
  2. Input Objects (ControllerInput) wrap that data into a standardized IInput.
  3. Mappers (GenericMapper) associate that IInput with a semantic action (e.g., GameInput.Action).
  4. Router (PlayerInputRouter) directs that action to a specific player index.
  5. Game (MainGameObject) queries the Router to decide what happens on screen.

Clone this wiki locally