Input, Keybindings & Pause Flow

A Unity New Input System layer that centralizes player input, exposes clean gameplay properties, supports saved keybinding overrides, and safely disables movement, combat, interaction, and weapon switching during menus.

System Overview

This system is the control room for player intent: movement, aiming, firing, reloading, interacting, pausing, cycling weapons, and rebinding controls.

What It Does

The Input, Keybindings & Pause Flow system wraps Unity's generated input actions inside a focused PlayerInputReader. Gameplay scripts do not need to know the details of the Input Action Asset. They ask for simple values such as MoveInput, LookInput, FirePressed, ReloadPressed, InteractPressed, JumpPressed, and PausePressed.

The input reader also supports gameplay input locking. When the pause menu opens, gameplay input is disabled while pause input remains available. The player can no longer move, fire, reload, interact, or cycle weapons while browsing menus, but they can still unpause.

Keybinding support is handled through saved binding overrides. The keybindings UI can start a rebind operation, update a row display, save overrides to PlayerPrefs, and reset all bindings back to defaults. This gives the project a real settings workflow instead of a static controls screen.

Primary Role

Centralize player input, support rebinding, and protect gameplay systems from receiving input while menus are open.

Core Loop

InputActionAsset → PlayerInputReader → Gameplay scripts → Pause lock → Rebind/save overrides.

Portfolio Value

Demonstrates New Input System usage, UI tooling, persistent settings, and clean separation between input and gameplay.

Input System Snapshots

Screenshot references for keybinding UI, pause flow, the Input Actions asset, gameplay input locking, and the keybinding row prefab.

Gameplay screenshot of the Keybindings menu showing action names, current bindings, rebind buttons, reset options, and back navigation

Keybindings Menu

The keybindings menu gives players a readable list of controls with rebind and reset options.

  • Rebind UI
  • Controls Menu
  • Settings
Unity Input Actions asset screenshot showing Player actions such as Move, Look, Jump, Attack, Reload, Interact, Drop, Pause, Next, Previous, BandolierHold, and BandolierScroll

Input Actions Asset

Unity's Input Action Asset defines gameplay actions, while PlayerInputReader exposes them through clean properties.

  • New Input System
  • Actions
  • Bindings
Gameplay screenshot of the pause menu showing resume, save, load, settings, keybindings, credits, main menu, and quit buttons

Pause Menu Flow

The pause controller disables gameplay input, unlocks the cursor, pauses time, and opens menu panels.

  • Pause
  • Cursor Lock
  • Input Lock
Unity Inspector screenshot of a KeybindingRowUI prefab showing action label text, binding display text, rebind button, and reset button references

Keybinding Row Prefab

Rows are generated from binding entries so the menu can scale as new actions are added.

  • Prefab UI
  • Generated Rows
  • Bindings
Gameplay screenshot showing the pause menu open while the player weapon, movement, interaction, and loadout controls are disabled behind the menu

Gameplay Input Locking

Menus can stop movement, weapons, interaction, and weapon cycling without disabling pause input.

  • Safe Menus
  • Player Lock
  • Control Gating
Screenshot or diagram showing keybinding overrides saved through PlayerPrefs and restored when the game starts

Saved Overrides

Binding overrides are saved as JSON in PlayerPrefs, then restored on startup.

  • PlayerPrefs
  • JSON
  • Persistence

Input Flow

The system turns low-level input actions into readable gameplay signals.

1. Input Actions Define Controls

The generated InputSystem_Actions asset contains Player and UI action maps for movement, look, attack, reload, interact, pause, weapon cycling, and future controls.

2. PlayerInputReader Wraps Actions

The input reader exposes readable properties instead of forcing gameplay scripts to call InputAction methods directly.

3. Gameplay Scripts Read Intent

Movement, weapons, interaction, loadout cycling, and pause flow all read from the same centralized input source.

4. Pause Disables Gameplay

When paused, gameplay input is disabled while the pause action itself remains available. This prevents the menu from becoming a haunted control sandwich.

5. Rebinding Updates Actions

The keybindings UI starts interactive rebind operations and updates visible binding labels.

6. Overrides Are Saved

Binding overrides are saved as JSON in PlayerPrefs and loaded back into the Input Action Asset on startup.

Key Components

Each piece has a clear responsibility in the input and menu-control pipeline.

PlayerInputReader

Creates the generated input actions, exposes readable input properties, gates gameplay input, saves binding overrides, loads binding overrides, and resets bindings.

  • Input Wrapper
  • Gameplay Gate
  • Binding Saves

KeybindingsMenuUI

Builds the keybindings menu from binding entries, connects rows to input actions, starts rebind flows, saves changes, resets individual bindings, and resets all bindings.

  • Rebinding
  • Menu UI
  • Generated Rows

KeybindingRowUI

Displays one action row with the action name, current binding text, a rebind button, and a reset button.

  • Row Prefab
  • Binding Text
  • Buttons

PauseMenuController

Opens and closes the pause menu, changes Time.timeScale, locks and unlocks the cursor, disables gameplay input, and routes to settings, keybindings, credits, save, load, main menu, and quit.

  • Pause Flow
  • Menu Routing
  • Input Locking

SimpleFirstPersonController

Reads movement, look, and jump input through PlayerInputReader rather than calling the input asset directly.

  • Movement
  • Look
  • Jump

Weapon & Interaction Controllers

Weapon firing, reload, interaction, dropping, and loadout cycling all use the same input reader, keeping action access consistent across gameplay systems.

  • Fire
  • Reload
  • Interact

Pause Flow & Gameplay Locking

Pausing is more than showing a panel. It changes how the whole player stack receives input.

Menu-Safe Player Control

When the pause menu opens, the controller sets the pause root active, pauses time, disables gameplay input, disables player movement, disables interaction, disables weapon use, disables loadout cycling, and unlocks the cursor.

This creates a clean boundary between gameplay and UI. The player cannot accidentally fire, reload, interact with a terminal, or cycle weapons while clicking through the pause menu.

When the player resumes, the same systems are restored. Time returns to normal, gameplay input is enabled, the cursor is locked, and the player regains control.

Paused State

  • Pause menu visible
  • Time.timeScale set to 0
  • Gameplay input disabled
  • Cursor unlocked
  • Movement disabled
  • Weapons disabled

Resume State

  • Pause menu hidden
  • Time.timeScale set to 1
  • Gameplay input enabled
  • Cursor locked
  • Movement enabled
  • Weapons enabled

Design Benefit

Menus can safely exist inside gameplay scenes without each gameplay script needing custom pause logic.

Keybinding Workflow

Binding overrides make the controls menu persistent instead of cosmetic.

Binding Entries

The menu defines entries with a display name, action path, and binding name, such as Move Forward using the Player/Move action and its up binding.

Generated Rows

Each entry creates or configures a row prefab that displays the action name and current binding.

Interactive Rebind

When the player chooses rebind, the selected action can listen for the next valid input and apply it as a binding override.

Saved Overrides

Binding overrides are serialized with SaveBindingOverridesAsJson and stored in PlayerPrefs.

Loaded On Startup

PlayerInputReader loads saved overrides during Awake so custom controls are active immediately.

Reset Support

The player can remove binding overrides and return to the default control layout.

Source Code Snapshot

PlayerInputReader is the best code sample for this page because it shows the bridge between generated Input Actions, gameplay-safe properties, and saved rebinding overrides.

PlayerInputReader.cs Central input wrapper, gameplay input gate, binding override save/load, and reset helpers
//-----PlayerInputReader.cs START-----
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerInputReader : MonoBehaviour
{
    private InputSystem_Actions inputActions;

    private bool gameplayInputEnabled = true;

    private const string BindingOverridesKey = "EchoSystemsLab_BindingOverrides";

    public bool BandolierHeld =>
        gameplayInputEnabled &&
        inputActions.Player.BandolierHold.IsPressed();

    public float BandolierScroll =>
        gameplayInputEnabled
            ? inputActions.Player.BandolierScroll.ReadValue<float>()
            : 0f;

    public InputActionAsset InputActionAsset => inputActions.asset;

    public Vector2 MoveInput
    {
        get
        {
            if (!gameplayInputEnabled)
                return Vector2.zero;

            return inputActions.Player.Move.ReadValue<Vector2>();
        }
    }

    public Vector2 LookInput
    {
        get
        {
            if (!gameplayInputEnabled)
                return Vector2.zero;

            return inputActions.Player.Look.ReadValue<Vector2>();
        }
    }

    public bool JumpPressed =>
        gameplayInputEnabled &&
        inputActions.Player.Jump.WasPressedThisFrame();

    public bool InteractPressed =>
        gameplayInputEnabled &&
        inputActions.Player.Interact.WasPressedThisFrame();

    public bool DropPressed =>
        gameplayInputEnabled &&
        inputActions.Player.Drop.WasPressedThisFrame();

    public bool FirePressed =>
        gameplayInputEnabled &&
        inputActions.Player.Attack.WasPressedThisFrame();

    public bool FireHeld =>
        gameplayInputEnabled &&
        inputActions.Player.Attack.IsPressed();

    public bool ReloadPressed =>
        gameplayInputEnabled &&
        inputActions.Player.Reload.WasPressedThisFrame();

    public bool CycleNextWeaponPressed =>
        gameplayInputEnabled &&
        inputActions.Player.Next.WasPressedThisFrame();

    public bool CyclePreviousWeaponPressed =>
        gameplayInputEnabled &&
        inputActions.Player.Previous.WasPressedThisFrame();

    public bool PausePressed =>
        inputActions.Player.Pause.WasPressedThisFrame();

    private void Awake()
    {
        inputActions = new InputSystem_Actions();
        LoadBindingOverrides();
    }

    private void OnEnable()
    {
        inputActions.Player.Enable();
        inputActions.UI.Enable();
    }

    private void OnDisable()
    {
        inputActions.Player.Disable();
        inputActions.UI.Disable();
    }

    public void SetGameplayInputEnabled(bool enabled)
    {
        gameplayInputEnabled = enabled;
    }

    //-------------------------------------------------------------------------
    //-------------------------Key Bindings Helpers----------------------------
    //-------------------------------------------------------------------------

    public InputAction FindAction(string actionPath)
    {
        if (inputActions == null)
            return null;

        return inputActions.asset.FindAction(actionPath, false);
    }

    public void SaveBindingOverrides()
    {
        string json = inputActions.asset.SaveBindingOverridesAsJson();
        PlayerPrefs.SetString(BindingOverridesKey, json);
        PlayerPrefs.Save();

        Debug.Log("Binding overrides saved.");
    }

    public void LoadBindingOverrides()
    {
        if (!PlayerPrefs.HasKey(BindingOverridesKey))
            return;

        string json = PlayerPrefs.GetString(BindingOverridesKey);
        inputActions.asset.LoadBindingOverridesFromJson(json);

        Debug.Log("Binding overrides loaded.");
    }

    public void ResetBindingOverrides()
    {
        inputActions.asset.RemoveAllBindingOverrides();
        PlayerPrefs.DeleteKey(BindingOverridesKey);
        PlayerPrefs.Save();

        Debug.Log("Binding overrides reset.");
    }

    //-------------------------------------------------------------------------
    //-------------------------Optional Helpers--------------------------------
    //-------------------------------------------------------------------------

    public bool HasBandolierScrollInput(float threshold = 0.01f)
    {
        return Mathf.Abs(BandolierScroll) > threshold;
    }
}
//-----PlayerInputReader.cs END-----

Problems Solved

This system prevents input from becoming scattered across every gameplay script.

Key Iteration Wins

Without an input wrapper, every gameplay script would need to know about the generated Input Action Asset. That makes refactors harder because movement, weapons, interaction, menus, and future systems all depend on the same low-level generated structure.

PlayerInputReader keeps gameplay scripts simple. Movement reads MoveInput and LookInput. Weapons read FirePressed, FireHeld, and ReloadPressed. Interaction reads InteractPressed. The pause menu reads PausePressed and controls whether gameplay input is available.

The binding override helpers also turn rebinding into a project-level feature. Players can customize controls, save them, reload the game, and keep their preferred layout.

Before

Gameplay systems would need direct knowledge of action maps, generated classes, and raw input calls.

After

Gameplay systems read simple properties from one centralized input reader.

Result

Cleaner scripts, safer pause behavior, saved rebinds, and a scalable input foundation for future systems.

Portfolio Value

Input architecture is invisible when it works, but it touches every playable system.

New Input System

Shows practical usage of Unity's action-based input workflow with generated actions and clean runtime access.

Centralized Input Layer

Keeps input access consistent across movement, weapons, interaction, pause, loadout, and future systems.

Rebinding Support

Demonstrates player-facing control customization with saved binding overrides.

Menu-Safe Gameplay

Shows how menus can disable gameplay input without breaking pause and UI navigation.

Persistent Settings

Binding override JSON is saved through PlayerPrefs and restored on startup.

Expandable Foundation

New actions, UI rows, control schemes, and gameplay systems can plug into the same input gateway.

Related Systems

Input and pause flow support nearly every other gameplay feature in Echo Systems Lab.

Weapon Data, Loadout & Drill Restrictions

Weapon firing, reload, dry fire, and loadout cycling all read gameplay actions through PlayerInputReader.

Audio Subsystem

UI audio, menu feedback, pause menus, settings sliders, and gameplay events all benefit from clean input state.

Mission Terminal & Scene Flow

Interaction input opens the mission terminal and pause flow safely disables gameplay while menu panels are active.