Input — the Input System and Legacy Input
Keyboard, mouse, gamepad, and touch — two paths and why you should take the new one.
Unity currently has two input systems coexisting. The legacy Input class is old and simple. The Input System
package is new, powerful, and recommended for new projects.
Legacy Input — a quick start
The Input class is available out of the box. It’s polled every frame in Update:
private void Update() {
float h = Input.GetAxis("Horizontal"); // -1..1, left stick/A-D/arrow keys
float v = Input.GetAxis("Vertical");
if (Input.GetButtonDown("Jump")) {
// space was pressed this frame
}
if (Input.GetMouseButton(0)) {
// the left mouse button is held down
}
Vector2 mouseDelta = new Vector2(
Input.GetAxis("Mouse X"),
Input.GetAxis("Mouse Y")
);
}
Buttons and axes are defined in Edit → Project Settings → Input Manager. It’s a dictionary with default values: “Horizontal”, “Vertical”, “Jump”, “Fire1”, “Mouse X”, and so on.
In Unity 6.3 LTS, the legacy Input Manager is officially marked deprecated with a warning in the editor. The API still works, but it will be removed in a future major version. Use legacy only for quick prototypes or maintaining old projects. In new code, use the Input System.
Input System — the modern path
The Input System is a package (installed via the Package Manager). The main ideas:
- Actions, not keys. You describe actions (“Move”, “Jump”, “Shoot”) and bind physical buttons/axes/gestures to them. One action, many bindings.
- Composite bindings. “WASD” = a composite of 4 keys that returns a
Vector2. - Control schemes. “Keyboard & Mouse” vs “Gamepad” — automatic switching, with different bindings.
- Callbacks, not polling. You subscribe to
performed,started,canceled.
The Action Asset
In Project — Create → Input Actions. A visual editor opens: you create an Action Map (for example, “Player”), with actions inside it, and bindings for each one.
Once configured, Unity will generate a C# class (via “Generate C# Class” in the inspector) that is convenient to use:
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private PlayerControls _controls; // the generated class
private Vector2 _moveInput;
private void Awake() {
_controls = new PlayerControls();
_controls.Player.Move.performed += ctx => _moveInput = ctx.ReadValue<Vector2>();
_controls.Player.Move.canceled += _ => _moveInput = Vector2.zero;
_controls.Player.Jump.performed += _ => TryJump();
}
private void OnEnable() => _controls.Enable();
private void OnDisable() => _controls.Disable();
private void Update() {
var move = new Vector3(_moveInput.x, 0, _moveInput.y);
transform.Translate(move * 5f * Time.deltaTime, Space.Self);
}
private void TryJump() { /* ... */ }
}
You can skip the manual code: attach a PlayerInput component to a GameObject, point it at the Action
Asset, and wire actions to your methods via UnityEvents in the Inspector. Good for prototypes
and local co-op (it’s easy to bind a second player to a second gamepad).
Rotating the camera with the mouse
A classic task: turn the player’s head to follow the cursor. A pattern with the Input System:
public class MouseLook : MonoBehaviour
{
[SerializeField] private float sensitivity = 0.1f;
[SerializeField] private Transform cameraPivot;
private float _pitch;
private float _yaw;
private Vector2 _lookInput;
private void Awake() {
Cursor.lockState = CursorLockMode.Locked; // hide and lock
Cursor.visible = false;
}
public void OnLook(InputAction.CallbackContext ctx) {
_lookInput = ctx.ReadValue<Vector2>();
}
private void Update() {
_yaw += _lookInput.x * sensitivity;
_pitch -= _lookInput.y * sensitivity;
_pitch = Mathf.Clamp(_pitch, -85f, 85f);
transform.localRotation = Quaternion.Euler(0, _yaw, 0);
cameraPivot.localRotation = Quaternion.Euler(_pitch, 0, 0);
}
}
An important detail: pitch (vertical) is applied to the child camera object, while yaw (horizontal) is applied to the player’s body. This separation simplifies physics: the player’s collider doesn’t tilt when looking up and down.
Geometry Raycast: “where the player is looking”
Shooting, picking up an item, click-to-move — all of this is built on raycasts. A ray from a point in a direction that finds the first intersection with a collider:
private void Shoot() {
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, maxDistance: 100f)) {
Debug.Log($"Hit {hit.collider.name} at a distance of {hit.distance:F2}");
if (hit.collider.TryGetComponent<Enemy>(out var enemy)) {
enemy.TakeDamage(10);
}
}
}
Camera.main.ScreenPointToRay is the standard technique for turning the cursor’s position into a ray from the camera. For
an FPS you can do it more simply: transform.forward from the camera.
In the next section: physics — how a Rigidbody and a Collider interact with each other and with your scripts.