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!