Skip to content
Back to skills

Input System

ASecurity

New Input System — action maps, PlayerInput component, generated C# classes, runtime rebinding, multi-device support, input buffering.

  • 21 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 28, 2026
ai-agentsgoc#performance

Works with

  • cli

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add XeldarAlz/everything-claude-unity --skill input-system --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Input System?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Input System
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/xeldaralz-input-system/badge)](https://www.skillsdirectory.com/skills/xeldaralz-input-system)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: input-system
description: "New Input System — action maps, PlayerInput component, generated C# classes, runtime rebinding, multi-device support, input buffering."
globs: ["**/*.inputactions", "**/Input*.cs", "**/PlayerInput*"]
---

# Unity New Input System

## Input Action Asset Setup

Create an Input Action Asset: Assets > Create > Input Actions. This is the central configuration for all input bindings.

### Action Map Structure

Organize actions into maps based on context:

```
PlayerControls.inputactions
  |-- Player (gameplay)
  |     |-- Move (Value, Vector2)
  |     |-- Look (Value, Vector2)
  |     |-- Jump (Button)
  |     |-- Attack (Button)
  |     |-- Interact (Button)
  |
  |-- UI (menu navigation)
  |     |-- Navigate (Value, Vector2)
  |     |-- Submit (Button)
  |     |-- Cancel (Button)
  |
  |-- Menu (pause/settings)
        |-- Pause (Button)
```

### Action Types

- **Button**: Discrete press/release. Use for jump, attack, interact.
- **Value**: Continuous value. Use for movement, look, triggers.
- **Pass-Through**: Like Value but does not perform initial state check. Use for multi-device scenarios.

## Generated C# Class Workflow

In the Input Action Asset inspector, check "Generate C# Class" and click Apply. This is the recommended approach.

```csharp
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    private PlayerControls _controls;
    private Vector2 _moveInput;

    private void Awake()
    {
        _controls = new PlayerControls();
    }

    private void OnEnable()
    {
        _controls.Player.Enable();

        _controls.Player.Move.performed += OnMove;
        _controls.Player.Move.canceled += OnMove;
        _controls.Player.Jump.performed += OnJump;
        _controls.Player.Attack.performed += OnAttack;
    }

    private void OnDisable()
    {
        _controls.Player.Move.performed -= OnMove;
        _controls.Player.Move.canceled -= OnMove;
        _controls.Player.Jump.performed -= OnJump;
        _controls.Player.Attack.performed -= OnAttack;

        _controls.Player.Disable();
    }

    private void OnMove(InputAction.CallbackContext ctx)
    {
        _moveInput = ctx.ReadValue<Vector2>();
    }

    private void OnJump(InputAction.CallbackContext ctx)
    {
        // Jump logic
    }

    private void OnAttack(InputAction.CallbackContext ctx)
    {
        // Attack logic
    }

    private void Update()
    {
        transform.Translate(new Vector3(_moveInput.x, 0, _moveInput.y) * Time.deltaTime * 5f);
    }
}
```

## PlayerInput Component

The PlayerInput component provides an easier but less flexible approach.

### Behavior Modes

| Mode | Pros | Cons |
|------|------|------|
| Send Messages | Simple, no setup | Uses SendMessage (slow, no type safety) |
| Broadcast Messages | Reaches child objects | Same issues as SendMessages |
| Invoke Unity Events | Inspector-assigned, flexible | Requires wiring in Inspector |
| Invoke C# Events | Best performance, type-safe | Requires code subscription |

### Using Invoke C# Events

```csharp
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(PlayerInput))]
public class PlayerInputHandler : MonoBehaviour
{
    private PlayerInput _playerInput;

    private void Awake()
    {
        _playerInput = GetComponent<PlayerInput>();
    }

    private void OnEnable()
    {
        _playerInput.onActionTriggered += OnActionTriggered;
    }

    private void OnDisable()
    {
        _playerInput.onActionTriggered -= OnActionTriggered;
    }

    private void OnActionTriggered(InputAction.CallbackContext ctx)
    {
        switch (ctx.action.name)
        {
            case "Move":
                HandleMove(ctx.ReadValue<Vector2>());
                break;
            case "Jump":
                if (ctx.performed) HandleJump();
                break;
        }
    }

    private void HandleMove(Vector2 input) { /* ... */ }
    private void HandleJump() { /* ... */ }
}
```

## Reading Input Values

### Callback Phases

```csharp
action.started += ctx => { };   // Input begins (button starts pressing)
action.performed += ctx => { }; // Input completes (button fully pressed)
action.canceled += ctx => { };  // Input released
```

### Polling in Update (Alternative)

```csharp
private void Update()
{
    // Polling approach — simpler but less event-driven
    Vector2 move = _controls.Player.Move.ReadValue<Vector2>();
    bool jumpPressed = _controls.Player.Jump.WasPressedThisFrame();
    bool jumpReleased = _controls.Player.Jump.WasReleasedThisFrame();
    bool jumpHeld = _controls.Player.Jump.IsPressed();
}
```

## Action Map Switching

```csharp
public class InputMapSwitcher : MonoBehaviour
{
    private PlayerControls _controls;

    public void SwitchToUI()
    {
        _controls.Player.Disable();
        _controls.UI.Enable();
    }

    public void SwitchToGameplay()
    {
        _controls.UI.Disable();
        _controls.Player.Enable();
    }

    public void SwitchToMenu()
    {
        _controls.Player.Disable();
        _controls.UI.Disable();
        _controls.Menu.Enable();
    }
}
```

## Composite Bindings

### 2D Vector Composite (WASD / D-Pad)

In the Input Action Asset, add a 2D Vector composite to a Vector2 action:
- Up: W / D-Pad Up
- Down: S / D-Pad Down
- Left: A / D-Pad Left
- Right: D / D-Pad Right

### Button With Modifier

For key combos like Ctrl+S:
- Add ButtonWithOneModifier composite
- Modifier: Left Ctrl
- Button: S

## Runtime Rebinding

```csharp
using UnityEngine;
using UnityEngine.InputSystem;
using TMPro;

public class RebindManager : MonoBehaviour
{
    [SerializeField] private InputActionReference actionToRebind;
    [SerializeField] private TMP_Text bindingDisplayText;
    [SerializeField] private GameObject waitingForInputUI;

    private InputActionRebindingExtensions.RebindingOperation _rebindOperation;

    public void StartRebinding()
    {
        actionToRebind.action.Disable();
        waitingForInputUI.SetActive(true);

        _rebindOperation = actionToRebind.action.PerformInteractiveRebinding()
            .WithControlsExcluding("Mouse") // Exclude mouse movement
            .WithCancelingThrough("<Keyboard>/escape")
            .OnMatchWaitForAnother(0.1f) // Debounce
            .OnComplete(operation => RebindComplete())
            .OnCancel(operation => RebindCanceled())
            .Start();
    }

    private void RebindComplete()
    {
        _rebindOperation.Dispose();
        _rebindOperation = null;
        waitingForInputUI.SetActive(false);

        actionToRebind.action.Enable();
        UpdateBindingDisplay();

        // Save the binding override
        string rebinds = actionToRebind.action.actionMap.asset.SaveBindingOverridesAsJson();
        PlayerPrefs.SetString("InputBindings", rebinds);
    }

    private void RebindCanceled()
    {
        _rebindOperation.Dispose();
        _rebindOperation = null;
        waitingForInputUI.SetActive(false);
        actionToRebind.action.Enable();
    }

    private void UpdateBindingDisplay()
    {
        int bindingIndex = actionToRebind.action.GetBindingIndexForControl(
            actionToRebind.action.controls[0]);
        bindingDisplayText.text = InputControlPath.ToHumanReadableString(
            actionToRebind.action.bindings[bindingIndex].effectivePath,
            InputControlPath.HumanReadableStringOptions.OmitDevice);
    }

    public void LoadSavedBindings()
    {
        string rebinds = PlayerPrefs.GetString("InputBindings", string.Empty);
        if (!string.IsNullOrEmpty(rebinds))
        {
            actionToRebind.action.actionMap.asset.LoadBindingOverridesFromJson(rebinds);
        }
    }
}
```

## Input Buffering Pattern

Buffer inputs so actions like jump register even if pressed slightly before landing.

```csharp
public class InputBuffer : MonoBehaviour
{
    [SerializeField] private float bufferDuration = 0.15f;

    private float _jumpBufferTimer;
    private bool _jumpConsumed;

    private void Update()
    {
        if (_jumpBufferTimer > 0f)
        {
            _jumpBufferTimer -= Time.deltaTime;
        }
    }

    // Called from input callback
    public void OnJumpPressed()
    {
        _jumpBufferTimer = bufferDuration;
        _jumpConsumed = false;
    }

    // Called from movement/physics when jump is possible
    public bool ConsumeJumpBuffer()
    {
        if (_jumpBufferTimer > 0f && !_jumpConsumed)
        {
            _jumpConsumed = true;
            _jumpBufferTimer = 0f;
            return true;
        }
        return false;
    }
}
```

## Device Detection

```csharp
using UnityEngine.InputSystem;

public class DeviceDetector : MonoBehaviour
{
    public enum InputScheme { KeyboardMouse, Gamepad, Touch }
    public InputScheme CurrentScheme { get; private set; }

    public event System.Action<InputScheme> OnSchemeChanged;

    private void OnEnable()
    {
        InputSystem.onActionChange += OnActionChange;
    }

    private void OnDisable()
    {
        InputSystem.onActionChange -= OnActionChange;
    }

    private void OnActionChange(object obj, InputActionChange change)
    {
        if (change != InputActionChange.ActionPerformed) return;

        var action = (InputAction)obj;
        var device = action.activeControl?.device;

        InputScheme newScheme = device switch
        {
            Keyboard or Mouse => InputScheme.KeyboardMouse,
            Gamepad => InputScheme.Gamepad,
            Touchscreen => InputScheme.Touch,
            _ => CurrentScheme
        };

        if (newScheme != CurrentScheme)
        {
            CurrentScheme = newScheme;
            OnSchemeChanged?.Invoke(CurrentScheme);
        }
    }
}
```

## Multiplayer Input (PlayerInputManager)

```csharp
using UnityEngine;
using UnityEngine.InputSystem;

public class LocalMultiplayerManager : MonoBehaviour
{
    [SerializeField] private PlayerInputManager playerInputManager;

    private void OnEnable()
    {
        playerInputManager.onPlayerJoined += OnPlayerJoined;
        playerInputManager.onPlayerLeft += OnPlayerLeft;
    }

    private void OnDisable()
    {
        playerInputManager.onPlayerJoined -= OnPlayerJoined;
        playerInputManager.onPlayerLeft -= OnPlayerLeft;
    }

    private void OnPlayerJoined(PlayerInput playerInput)
    {
        Debug.Log($"Player {playerInput.playerIndex} joined with {playerInput.currentControlScheme}");
    }

    private void OnPlayerLeft(PlayerInput playerInput)
    {
        Debug.Log($"Player {playerInput.playerIndex} left");
    }
}
```

## Touch Input for Mobile

```csharp
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.EnhancedTouch;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch;

public class TouchInputHandler : MonoBehaviour
{
    private void OnEnable()
    {
        EnhancedTouchSupport.Enable();
    }

    private void OnDisable()
    {
        EnhancedTouchSupport.Disable();
    }

    private void Update()
    {
        foreach (var touch in Touch.activeTouches)
        {
            switch (touch.phase)
            {
                case UnityEngine.InputSystem.TouchPhase.Began:
                    HandleTouchStart(touch.screenPosition);
                    break;
                case UnityEngine.InputSystem.TouchPhase.Moved:
                    HandleTouchMove(touch.screenPosition, touch.delta);
                    break;
                case UnityEngine.InputSystem.TouchPhase.Ended:
                    HandleTouchEnd(touch.screenPosition);
                    break;
            }
        }
    }

    private void HandleTouchStart(Vector2 pos) { /* ... */ }
    private void HandleTouchMove(Vector2 pos, Vector2 delta) { /* ... */ }
    private void HandleTouchEnd(Vector2 pos) { /* ... */ }
}
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.