Guides / Unity Input System / Input System: Keyboard, Mouse, and Touch

Unity Input System: Keyboard, Mouse, and Touch

What is Unity Input System?

Unity's Input System is a modern, flexible input handling system that provides a unified way to handle input from various sources including keyboard, mouse, touch, and game controllers. It's designed to be more powerful and flexible than the legacy Input Manager.

Key Components:

  • Input Actions - Define what inputs your game responds to
  • Input Maps - Organize inputs for different game contexts
  • Input Devices - Handle different input devices automatically
  • Cross-Platform Support - Works seamlessly across different platforms
  • Event-Driven Input - Respond to input events efficiently

Why Use the New Input System?

Unity's new Input System offers several advantages over the legacy system:

  • Better Performance - More efficient input processing
  • Cross-Platform - Automatic device detection and mapping
  • Flexible Configuration - Easy to modify input schemes
  • Event-Driven - Better for complex input handling
  • Future-Proof - Modern architecture for upcoming features

Input System Overview

Basic Setup

To use the new Input System, you need to enable it in your project:

  • Go to Edit → Project Settings
  • Select XR Plug-in Management → Input System Package
  • Check "Use Input System Package (New)"
  • Restart Unity when prompted

Creating Input Actions

Input Actions define what inputs your game responds to:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction moveAction;
    public InputAction jumpAction;
    public InputAction fireAction;
    
    [Header("Movement Settings")]
    public float moveSpeed = 5f;
    public float jumpForce = 10f;
    
    private Vector2 moveInput;
    private Rigidbody rb;
    
    void Start()
    {
        rb = GetComponent();
        
        // Enable input actions
        moveAction.Enable();
        jumpAction.Enable();
        fireAction.Enable();
    }
    
    void Update()
    {
        // Read movement input
        moveInput = moveAction.ReadValue();
        
        // Handle jump input
        if (jumpAction.WasPressedThisFrame())
        {
            Jump();
        }
        
        // Handle fire input
        if (fireAction.WasPressedThisFrame())
        {
            Fire();
        }
    }
    
    void FixedUpdate()
    {
        // Apply movement
        Vector3 movement = new Vector3(moveInput.x, 0, moveInput.y) * moveSpeed;
        rb.velocity = new Vector3(movement.x, rb.velocity.y, movement.z);
    }
    
    void Jump()
    {
        if (IsGrounded())
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
    }
    
    void Fire()
    {
        Debug.Log("Firing!");
        // Add your firing logic here
    }
    
    bool IsGrounded()
    {
        return Physics.Raycast(transform.position, Vector3.down, 1.1f);
    }
    
    void OnDestroy()
    {
        // Disable input actions
        moveAction.Disable();
        jumpAction.Disable();
        fireAction.Disable();
    }
}

Keyboard Input

Basic Keyboard Handling

Handle keyboard input with the new Input System:

using UnityEngine;
using UnityEngine.InputSystem;

public class KeyboardInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction keyboardAction;
    
    [Header("Movement Settings")]
    public float moveSpeed = 5f;
    
    private Vector2 inputVector;
    
    void Start()
    {
        keyboardAction.Enable();
    }
    
    void Update()
    {
        // Read keyboard input
        inputVector = keyboardAction.ReadValue();
        
        // Move the object
        transform.Translate(new Vector3(inputVector.x, 0, inputVector.y) * moveSpeed * Time.deltaTime);
    }
    
    void OnDestroy()
    {
        keyboardAction.Disable();
    }
}

Advanced Keyboard Input

Handle multiple keyboard keys and combinations:

public class AdvancedKeyboardInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction moveAction;
    public InputAction sprintAction;
    public InputAction crouchAction;
    public InputAction interactAction;
    
    [Header("Movement Settings")]
    public float walkSpeed = 5f;
    public float sprintSpeed = 8f;
    public float crouchSpeed = 2f;
    
    private Vector2 moveInput;
    private bool isSprinting;
    private bool isCrouching;
    
    void Start()
    {
        // Enable all input actions
        moveAction.Enable();
        sprintAction.Enable();
        crouchAction.Enable();
        interactAction.Enable();
        
        // Subscribe to events
        sprintAction.started += OnSprintStarted;
        sprintAction.canceled += OnSprintCanceled;
        crouchAction.started += OnCrouchStarted;
        crouchAction.canceled += OnCrouchCanceled;
        interactAction.performed += OnInteract;
    }
    
    void Update()
    {
        // Read movement input
        moveInput = moveAction.ReadValue();
        
        // Calculate movement speed
        float currentSpeed = walkSpeed;
        if (isSprinting) currentSpeed = sprintSpeed;
        if (isCrouching) currentSpeed = crouchSpeed;
        
        // Apply movement
        Vector3 movement = new Vector3(moveInput.x, 0, moveInput.y) * currentSpeed;
        transform.Translate(movement * Time.deltaTime);
    }
    
    void OnSprintStarted(InputAction.CallbackContext context)
    {
        isSprinting = true;
    }
    
    void OnSprintCanceled(InputAction.CallbackContext context)
    {
        isSprinting = false;
    }
    
    void OnCrouchStarted(InputAction.CallbackContext context)
    {
        isCrouching = true;
    }
    
    void OnCrouchCanceled(InputAction.CallbackContext context)
    {
        isCrouching = false;
    }
    
    void OnInteract(InputAction.CallbackContext context)
    {
        Debug.Log("Interacting with object!");
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        sprintAction.started -= OnSprintStarted;
        sprintAction.canceled -= OnSprintCanceled;
        crouchAction.started -= OnCrouchStarted;
        crouchAction.canceled -= OnCrouchCanceled;
        interactAction.performed -= OnInteract;
        
        // Disable input actions
        moveAction.Disable();
        sprintAction.Disable();
        crouchAction.Disable();
        interactAction.Disable();
    }
}

Mouse Input

Basic Mouse Handling

Handle mouse input for camera control and interactions:

using UnityEngine;
using UnityEngine.InputSystem;

public class MouseInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction mousePositionAction;
    public InputAction mouseClickAction;
    public InputAction mouseScrollAction;
    
    [Header("Camera Settings")]
    public float mouseSensitivity = 2f;
    public float scrollSensitivity = 2f;
    
    private Vector2 mouseDelta;
    private float scrollInput;
    private Camera cam;
    
    void Start()
    {
        cam = GetComponent();
        
        // Enable input actions
        mousePositionAction.Enable();
        mouseClickAction.Enable();
        mouseScrollAction.Enable();
        
        // Subscribe to events
        mouseClickAction.performed += OnMouseClick;
        mouseScrollAction.performed += OnMouseScroll;
    }
    
    void Update()
    {
        // Read mouse position
        Vector2 mousePos = mousePositionAction.ReadValue();
        
        // Handle mouse movement for camera rotation
        if (Input.GetMouseButton(1)) // Right mouse button
        {
            mouseDelta = mousePos * mouseSensitivity * Time.deltaTime;
            transform.Rotate(-mouseDelta.y, mouseDelta.x, 0);
        }
    }
    
    void OnMouseClick(InputAction.CallbackContext context)
    {
        // Handle mouse clicks
        if (context.performed)
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            
            if (Physics.Raycast(ray, out hit))
            {
                Debug.Log("Clicked on: " + hit.collider.name);
            }
        }
    }
    
    void OnMouseScroll(InputAction.CallbackContext context)
    {
        // Handle mouse scroll
        scrollInput = context.ReadValue().y;
        
        // Zoom camera
        cam.fieldOfView = Mathf.Clamp(cam.fieldOfView - scrollInput * scrollSensitivity, 10f, 90f);
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        mouseClickAction.performed -= OnMouseClick;
        mouseScrollAction.performed -= OnMouseScroll;
        
        // Disable input actions
        mousePositionAction.Disable();
        mouseClickAction.Disable();
        mouseScrollAction.Disable();
    }
}

Advanced Mouse Input

Handle mouse drag, double-click, and other advanced interactions:

public class AdvancedMouseInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction mousePositionAction;
    public InputAction mouseClickAction;
    public InputAction mouseDragAction;
    
    [Header("Interaction Settings")]
    public float doubleClickTime = 0.3f;
    public float dragThreshold = 5f;
    
    private Vector2 mousePosition;
    private Vector2 lastMousePosition;
    private bool isDragging;
    private float lastClickTime;
    private int clickCount;
    
    void Start()
    {
        mousePositionAction.Enable();
        mouseClickAction.Enable();
        mouseDragAction.Enable();
        
        // Subscribe to events
        mouseClickAction.performed += OnMouseClick;
        mouseDragAction.started += OnMouseDragStarted;
        mouseDragAction.performed += OnMouseDrag;
        mouseDragAction.canceled += OnMouseDragEnded;
    }
    
    void Update()
    {
        // Update mouse position
        mousePosition = mousePositionAction.ReadValue();
        
        // Check for double click
        if (Time.time - lastClickTime < doubleClickTime)
        {
            clickCount++;
        }
        else
        {
            clickCount = 1;
        }
    }
    
    void OnMouseClick(InputAction.CallbackContext context)
    {
        lastClickTime = Time.time;
        
        if (clickCount == 2)
        {
            Debug.Log("Double click detected!");
            OnDoubleClick();
        }
        else
        {
            Debug.Log("Single click detected!");
            OnSingleClick();
        }
    }
    
    void OnMouseDragStarted(InputAction.CallbackContext context)
    {
        isDragging = true;
        lastMousePosition = mousePosition;
        Debug.Log("Drag started!");
    }
    
    void OnMouseDrag(InputAction.CallbackContext context)
    {
        if (isDragging)
        {
            Vector2 dragDelta = mousePosition - lastMousePosition;
            
            if (dragDelta.magnitude > dragThreshold)
            {
                Debug.Log("Dragging: " + dragDelta);
                OnDrag(dragDelta);
                lastMousePosition = mousePosition;
            }
        }
    }
    
    void OnMouseDragEnded(InputAction.CallbackContext context)
    {
        isDragging = false;
        Debug.Log("Drag ended!");
    }
    
    void OnSingleClick()
    {
        // Handle single click
        Ray ray = Camera.main.ScreenPointToRay(mousePosition);
        RaycastHit hit;
        
        if (Physics.Raycast(ray, out hit))
        {
            Debug.Log("Single clicked on: " + hit.collider.name);
        }
    }
    
    void OnDoubleClick()
    {
        // Handle double click
        Ray ray = Camera.main.ScreenPointToRay(mousePosition);
        RaycastHit hit;
        
        if (Physics.Raycast(ray, out hit))
        {
            Debug.Log("Double clicked on: " + hit.collider.name);
        }
    }
    
    void OnDrag(Vector2 dragDelta)
    {
        // Handle drag movement
        Debug.Log("Dragging by: " + dragDelta);
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        mouseClickAction.performed -= OnMouseClick;
        mouseDragAction.started -= OnMouseDragStarted;
        mouseDragAction.performed -= OnMouseDrag;
        mouseDragAction.canceled -= OnMouseDragEnded;
        
        // Disable input actions
        mousePositionAction.Disable();
        mouseClickAction.Disable();
        mouseDragAction.Disable();
    }
}

Touch Input

Basic Touch Handling

Handle touch input for mobile devices:

using UnityEngine;
using UnityEngine.InputSystem;

public class TouchInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction touchPositionAction;
    public InputAction touchPressAction;
    
    [Header("Touch Settings")]
    public float touchSensitivity = 1f;
    
    private Vector2 touchPosition;
    private bool isTouching;
    
    void Start()
    {
        touchPositionAction.Enable();
        touchPressAction.Enable();
        
        // Subscribe to events
        touchPressAction.started += OnTouchStarted;
        touchPressAction.canceled += OnTouchEnded;
    }
    
    void Update()
    {
        if (isTouching)
        {
            // Read touch position
            touchPosition = touchPositionAction.ReadValue();
            
            // Handle touch movement
            OnTouchMove(touchPosition);
        }
    }
    
    void OnTouchStarted(InputAction.CallbackContext context)
    {
        isTouching = true;
        touchPosition = context.ReadValue();
        Debug.Log("Touch started at: " + touchPosition);
    }
    
    void OnTouchEnded(InputAction.CallbackContext context)
    {
        isTouching = false;
        Debug.Log("Touch ended");
    }
    
    void OnTouchMove(Vector2 position)
    {
        // Handle touch movement
        Debug.Log("Touch moving to: " + position);
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        touchPressAction.started -= OnTouchStarted;
        touchPressAction.canceled -= OnTouchEnded;
        
        // Disable input actions
        touchPositionAction.Disable();
        touchPressAction.Disable();
    }
}

Multi-Touch Support

Handle multiple touch points for gestures:

public class MultiTouchInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction touchPositionAction;
    public InputAction touchPressAction;
    
    [Header("Gesture Settings")]
    public float pinchThreshold = 0.1f;
    public float rotationThreshold = 5f;
    
    private List touchPositions = new List();
    private bool isMultiTouch;
    
    void Start()
    {
        touchPositionAction.Enable();
        touchPressAction.Enable();
        
        // Subscribe to events
        touchPressAction.started += OnTouchStarted;
        touchPressAction.canceled += OnTouchEnded;
    }
    
    void Update()
    {
        if (isMultiTouch)
        {
            // Read all touch positions
            Vector2[] touches = touchPositionAction.ReadValue();
            touchPositions.Clear();
            touchPositions.AddRange(touches);
            
            // Handle multi-touch gestures
            if (touchPositions.Count == 2)
            {
                HandlePinchGesture();
                HandleRotationGesture();
            }
        }
    }
    
    void OnTouchStarted(InputAction.CallbackContext context)
    {
        isMultiTouch = true;
        Debug.Log("Multi-touch started");
    }
    
    void OnTouchEnded(InputAction.CallbackContext context)
    {
        isMultiTouch = false;
        touchPositions.Clear();
        Debug.Log("Multi-touch ended");
    }
    
    void HandlePinchGesture()
    {
        if (touchPositions.Count >= 2)
        {
            float distance = Vector2.Distance(touchPositions[0], touchPositions[1]);
            
            if (distance > pinchThreshold)
            {
                Debug.Log("Pinch gesture detected: " + distance);
                OnPinch(distance);
            }
        }
    }
    
    void HandleRotationGesture()
    {
        if (touchPositions.Count >= 2)
        {
            Vector2 direction = touchPositions[1] - touchPositions[0];
            float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
            
            if (Mathf.Abs(angle) > rotationThreshold)
            {
                Debug.Log("Rotation gesture detected: " + angle);
                OnRotation(angle);
            }
        }
    }
    
    void OnPinch(float distance)
    {
        // Handle pinch gesture
        Debug.Log("Pinching with distance: " + distance);
    }
    
    void OnRotation(float angle)
    {
        // Handle rotation gesture
        Debug.Log("Rotating by angle: " + angle);
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        touchPressAction.started -= OnTouchStarted;
        touchPressAction.canceled -= OnTouchEnded;
        
        // Disable input actions
        touchPositionAction.Disable();
        touchPressAction.Disable();
    }
}

Input Action Assets

Creating Input Action Assets

Use Input Action Assets to organize your input schemes:

  • Right-click in Project window
  • Create → Input Actions
  • Name it "PlayerInputActions"
  • Double-click to open the Input Actions window

Configuring Input Actions

Set up your input actions in the Input Actions window:

using UnityEngine;
using UnityEngine.InputSystem;

public class InputActionAssetExample : MonoBehaviour
{
    [Header("Input Action Asset")]
    public InputActionAsset inputActions;
    
    [Header("Movement Settings")]
    public float moveSpeed = 5f;
    
    private InputAction moveAction;
    private InputAction jumpAction;
    private Vector2 moveInput;
    
    void Start()
    {
        // Get input actions from asset
        moveAction = inputActions.FindAction("Move");
        jumpAction = inputActions.FindAction("Jump");
        
        // Enable input actions
        moveAction.Enable();
        jumpAction.Enable();
        
        // Subscribe to events
        jumpAction.performed += OnJump;
    }
    
    void Update()
    {
        // Read movement input
        moveInput = moveAction.ReadValue();
        
        // Apply movement
        Vector3 movement = new Vector3(moveInput.x, 0, moveInput.y) * moveSpeed;
        transform.Translate(movement * Time.deltaTime);
    }
    
    void OnJump(InputAction.CallbackContext context)
    {
        Debug.Log("Jump!");
        // Add jump logic here
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        jumpAction.performed -= OnJump;
        
        // Disable input actions
        moveAction.Disable();
        jumpAction.Disable();
    }
}

Cross-Platform Input

Device Detection

Automatically detect and handle different input devices:

using UnityEngine;
using UnityEngine.InputSystem;

public class CrossPlatformInput : MonoBehaviour
{
    [Header("Input Actions")]
    public InputAction moveAction;
    public InputAction lookAction;
    
    [Header("Movement Settings")]
    public float moveSpeed = 5f;
    public float lookSensitivity = 2f;
    
    private Vector2 moveInput;
    private Vector2 lookInput;
    private string currentDevice;
    
    void Start()
    {
        moveAction.Enable();
        lookAction.Enable();
        
        // Subscribe to device change events
        InputSystem.onDeviceChange += OnDeviceChange;
    }
    
    void Update()
    {
        // Read input
        moveInput = moveAction.ReadValue();
        lookInput = lookAction.ReadValue();
        
        // Apply movement
        Vector3 movement = new Vector3(moveInput.x, 0, moveInput.y) * moveSpeed;
        transform.Translate(movement * Time.deltaTime);
        
        // Apply look rotation
        transform.Rotate(0, lookInput.x * lookSensitivity, 0);
    }
    
    void OnDeviceChange(InputDevice device, InputDeviceChange change)
    {
        switch (change)
        {
            case InputDeviceChange.Added:
                Debug.Log("Device added: " + device.name);
                break;
            case InputDeviceChange.Removed:
                Debug.Log("Device removed: " + device.name);
                break;
            case InputDeviceChange.Enabled:
                Debug.Log("Device enabled: " + device.name);
                break;
            case InputDeviceChange.Disabled:
                Debug.Log("Device disabled: " + device.name);
                break;
        }
        
        // Update current device
        currentDevice = device.name;
    }
    
    void OnDestroy()
    {
        // Unsubscribe from events
        InputSystem.onDeviceChange -= OnDeviceChange;
        
        // Disable input actions
        moveAction.Disable();
        lookAction.Disable();
    }
}

Input System Best Practices

Performance Optimization

  • Use events instead of polling when possible
  • Cache input action references to avoid repeated lookups
  • Disable input actions when not needed
  • Use InputAction.CallbackContext for efficient event handling

Code Organization

  • Create input action assets for different game contexts
  • Use input action maps to organize related inputs
  • Implement input action callbacks for clean event handling
  • Separate input logic from game logic

Common Issues and Solutions

Issue: Input not working

  • Check if input actions are enabled
  • Verify input action asset is assigned
  • Ensure Input System package is installed

Issue: Input lag or stuttering

  • Use FixedUpdate for physics-based input
  • Avoid reading input in multiple scripts
  • Check for input action conflicts

Issue: Cross-platform input differences

  • Test on different devices
  • Use input action assets for consistent mapping
  • Implement device-specific input handling

Resources and Next Steps

Unity Documentation

Learning Resources

Ready to create responsive, cross-platform input systems? Start with simple keyboard input and gradually work your way up to complex multi-touch gestures!