U
tab 03Camera & Input6 lessons — tap to open
04 / 06 · 11 min
Camera & Input

The new Input System

your project so far

Assets/

Scripts/

PlayerMovement.csCharacterSwitcher.csSkillCaster.csInput.GetAxis / GetKey→InputRouter.cs

Settings/

PlayerControls.inputactions

Project settings

Input System package

you already havethis lesson addsthis lesson retires

Three scripts you wrote still call Input directly: PlayerMovement reads GetAxisRaw, CharacterSwitcher reads the number keys, SkillCaster reads Q, E and R. All three are locked to a keyboard, which is a problem, because tab 05 puts this on a phone.

The Input System lets you define an action once and bind keyboard, gamepad and touch to it. After this lesson none of your gameplay scripts know which device the player is holding.

animated diagram

Devices on the left, one action asset in the middle, your gameplay code on the right.

Install and switch

  1. 1

    Package Manager → Input System → Install. Unity will ask to restart; say yes.

  2. 2

    Edit▸Project Settings▸Player▸Active Input Handling → Both while you migrate.

  3. 3

    Project: Create▸Input Actions, name it PlayerControls, double-click to open the editor.

  4. 4

    Tick Generate C# Class and press Apply — Unity writes a wrapper class for you.

Build the action map

  1. 1

    Add an Action Map called Gameplay.

  2. 2

    Action Move → Action Type Value → Control Type Vector2. Add a 2D Vector Composite binding: W/A/S/D. Add another binding: Gamepad left stick.

  3. 3

    Action Look → Value → Vector2 → bind Mouse Delta and Gamepad right stick.

  4. 4

    Actions Jump and Attack → Action Type Button → bind Space / South button and Left Mouse / West button.

  5. 5

    Actions Skill1 / Skill2 / Skill3 → Button → bind Q, E, R. These replace the keys SkillCaster reads today.

  6. 6

    Action SwapCharacter → Value / Axis → bind the 1, 2, 3 keys. These replace what CharacterSwitcher reads today.

Read it in code

InputRouter.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class InputRouter : MonoBehaviour
{
    // filled by the generated class
    private PlayerControls controls;

    public Vector2 Move { get; private set; }
    public Vector2 Look { get; private set; }
    public bool JumpPressed { get; private set; }

    void Awake()
    {
        controls = new PlayerControls();

        controls.Gameplay.Move.performed += ctx => Move = ctx.ReadValue<Vector2>();
        controls.Gameplay.Move.canceled  += ctx => Move = Vector2.zero;   // never forget this line

        controls.Gameplay.Look.performed += ctx => Look = ctx.ReadValue<Vector2>();
        controls.Gameplay.Look.canceled  += ctx => Look = Vector2.zero;

        controls.Gameplay.Jump.performed += ctx => JumpPressed = true;
        controls.Gameplay.Attack.performed += ctx => GetComponent<SkillCaster>().TryCast(0);
    }

    void OnEnable()  => controls.Enable();    // no Enable = no input at all
    void OnDisable() => controls.Disable();

    void LateUpdate() => JumpPressed = false; // one-frame flag
}
  • performed fires when the input starts or changes; canceled fires when it is released. Forgetting canceled means the character walks forever.

  • Move is a Vector2 (x, y). In 3D you map it to (x, 0, y) — the y of the stick becomes world Z.

  • A second action map called UI can be enabled while Gameplay is disabled — that is how pause menus stop the player from moving.

Retire the three old readers

This is the tidy-up that makes the whole tab worth it. Delete every Input.GetAxis, Input.GetKey and Input.GetMouseButton in your project and route them through InputRouter instead.

the three edits
// 1 — PlayerMovement.cs
- float h = Input.GetAxisRaw("Horizontal");
- float v = Input.GetAxisRaw("Vertical");
- Vector3 input = new Vector3(h, 0f, v).normalized;
+ Vector3 input = new Vector3(router.Move.x, 0f, router.Move.y).normalized;

// 2 — CharacterSwitcher.cs
- for (int i = 0; i < characters.Count && i < 9; i++)
-     if (Input.GetKeyDown(KeyCode.Alpha1 + i)) Select(i);
+ // in InputRouter.Awake:
+ controls.Gameplay.SwapCharacter.performed += c => switcher.Select((int)c.ReadValue<float>() - 1);

// 3 — SkillCaster.cs
- for (int i = 0; i < skills.Count && i < keys.Length; i++)
-     if (Input.GetKeyDown(keys[i])) TryCast(i);
+ // in InputRouter.Awake:
+ controls.Gameplay.Skill1.performed += _ => caster.TryCast(0);
+ controls.Gameplay.Skill2.performed += _ => caster.TryCast(1);
+ controls.Gameplay.Skill3.performed += _ => caster.TryCast(2);

Using it in the movement script

C# script
[SerializeField] private InputRouter input;

void Update()
{
    Vector3 dir = new Vector3(input.Move.x, 0f, input.Move.y);
    // ... rest is identical to the old script
}

✓ PlayerInput component

Send Messages / Unity Events mode. Fastest to set up, good for prototypes.

✓ Generated C# class

Explicit, refactor-safe, no magic string method names. Better for a real project.