🏠 Home

Input in Unity: From Classic to Modern

Input handling is one of the core parts of any game. Through input, Unity understands the player’s intentions: movement, actions, and reactions.

We’ll start with a brief look at the old input system (Input Manager), which is still widely used but is gradually becoming outdated. It was appreciated for its simplicity and worked well for quick prototypes, but turned out to be too inflexible for modern projects.

Over time, Unity introduced a new input system (Input System). It is better suited for cross-platform projects, gamepads, and key rebinding, but it requires some initial setup.

In most tutorials on this site, we will use direct access to input devices — keyboard and mouse — without creating an Input Action Asset. This approach is not the most flexible, but it allows you to move faster into learning other Unity mechanics without getting stuck on input configuration. At the same time, it is still part of the new Input System.

Classic Input (Legacy Input Manager)

This is the standard input system that has been part of Unity since the very beginning. It still works today and is great for learning, since it does not require installing any additional packages.

The classic input settings are located at Edit → Project Settings → Input Manager. This is where the default axes and buttons are defined, which we reference by name in code.

Axes

Example: Input.GetAxis("Horizontal")

This method returns a floating-point value in the range from -1 to 1.

This is convenient for smooth movement. On a keyboard, Unity simulates inertia, while on a gamepad the value depends on how far the stick is tilted.

using UnityEngine;

void Update()
{
    float x = Input.GetAxis("Horizontal");
    Debug.Log(x);
}

Mouse buttons and cursor position

Example: Input.GetMouseButtonDown(0)

The method takes the mouse button index:

There are three different checks available:

using UnityEngine;

void Update()
{
    if (Input.GetMouseButtonDown(0))
    {
        Debug.Log("Left mouse button pressed");
    }
}

In addition to button presses, the classic input system allows you to read the mouse cursor position on the screen. This is done using the Input.mousePosition property, which returns the cursor coordinates in pixels relative to the bottom-left corner of the screen.

The cursor position is commonly used for aiming, selecting objects with the mouse, or determining an action direction. In 2D games, screen coordinates are often converted to world coordinates using the camera.

using UnityEngine;

void Update()
{
    Vector3 mouseScreenPos = Input.mousePosition;
    Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(mouseScreenPos);

    Debug.Log(mouseWorldPos);
}

Keyboard keys

Example: Input.GetKeyDown(KeyCode.Space)

This is a direct reference to a specific physical key via the KeyCode enumeration.

This approach is useful when you need to quickly bind an action to a specific key — for example, Escape for a menu or R for reloading.

using UnityEngine;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Debug.Log("Jump");
    }
}

⚠️ Important:
If only the new Input System is enabled in the project, this code will not work. To support classic input, the following mode must be set in the Player settings:
Edit → Project Settings → Player → Active Input Handling → Both

Summary: classic input is a simple, fast, and easy-to- understand way to handle controls, especially for learning and prototyping. However, for modern projects it is considered outdated.

Creating Input Actions (Action Asset)

In Unity’s new input system, controls are defined using Input Actions. These represent the player’s actions (for example, movement, jump, or shoot), which are then bound to keyboard keys, mouse buttons, or a gamepad.

In some projects, Unity already creates such a file automatically as a default template. While it works, it is usually too generic and not well suited for learning. That’s why in this tutorial we’ll create our own asset from scratch, so we can clearly understand each step.

Create a new asset: right-click in the Project window (inside the Assets folder) → CreateInput Actions. Name the file PlayerControls.

The created Input Actions asset PlayerControls in the Project window
The new PlayerControls file is an Action Asset where we will define the player’s actions and their bindings.

Setting Up Movement as a Vector (Vector2)

In the new input system, movement is best described not by individual buttons, but by a direction vector. This approach works equally well for keyboards and gamepads and does not require rewriting your logic later.

Select the created Action Asset PlayerControls and double-click it to open the Input Actions Editor.

In the left column, Action Maps, click + and create a new action map named Player. In the middle column, Actions, click + and create an action called Move.

On the right side of the window, in the settings for the Move action, set:

This means the action will return a movement direction as a vector (x, y), rather than a simple “pressed / not pressed” signal.

Creating the Player Action Map and the Move action with Value type and Vector2 control type
The Player Action Map and the Move action, configured as Value → Vector2.

Next, let’s add key bindings. For the Move action, click + and choose Add Up/Down/Left/Right Composite.

This composite combines four directions into a single movement vector, which Unity assembles automatically.

For each direction, click the Listen button and simply press the corresponding key on the keyboard:

As a result, Unity will automatically combine the input into a single movement vector: up, down, left, right, or diagonally.

Setting up 2D Vector bindings for the Move action using Listen mode
2D Vector bindings for the Move action, configured using Listen mode.

This setup provides clean and universal controls. In code, you receive a ready-made movement direction without worrying about individual keys.

💡 Don’t forget to save your changes. After configuring actions and bindings, click Save Asset in the top-right corner of the Input Actions Editor so Unity applies all changes.

Adding a Jump Action

In addition to movement, you often need a separate action that triggers only once — for example, a jump. For this purpose, the Input System uses an action of type Button.

In the same Player action map, click + in the Actions column and create a new action named Jump. In its settings, set the Action Type to Button.

Next, add a binding and assign the Space key. The easiest way to do this is by using the Listen button and simply pressing the spacebar on the keyboard.

This type of action works as an event — it does not provide a direction or value, but only reports the fact that the button was pressed.

The Jump action set to Button type with a Space binding in the Input Actions Editor
The Jump action configured as a button and bound to the Space key.

💡 Don’t forget to click Save Asset to save the newly added action and its binding.

Assigning the Input Actions Asset as Project-Wide

In Unity, only one Input Actions asset can be active for the entire project at the same time. In new projects, this asset is often the default file created automatically by Unity.

If you create your own PlayerControls asset, you must assign it as the main one for the project. Otherwise, Unity will continue using the previous (default) asset, and input may not work as expected.

Select the PlayerControls file in the Project window. In the Inspector, click the Assign as the Project-wide Input Actions button, if it is visible.

Inspector view of the Input Actions asset with the Assign as the Project-wide Input Actions button
Assigning the PlayerControls asset as the active Input Actions file for the entire project.

💡 If the Assign as the Project-wide Input Actions button is not visible, it means this asset is already set as active and no additional action is required.

Removing the Default Input Actions Asset (If Present)

In new Unity projects, a default Input Actions asset may be created automatically. It remains in the project as a regular file and may contain actions with the same names as those in our PlayerControls asset.

Because of this, you may see duplicate names when selecting actions or events, which can easily lead to confusion — especially in learning and tutorial projects.

If you are not using the default asset, simply delete it: right-click the file in the Project window and choose Delete.

After removing it, only the PlayerControls asset will remain in the project, and action selection will be unambiguous.

💡 Removing the default Input Actions asset is safe as long as it is not used in the project. It is not required for this tutorial.

Creating the PlayerInputDemo Script

Now we’ll write a simple script that receives events from the Input System and turns them into visible object behavior. We’ll move the object directly via Transform, without physics, to focus purely on input handling.

In the Assets folder, right-click → Create → MonoBehaviour Script and name the file PlayerInputDemo. Then open it and replace its contents with the following code.

using UnityEngine;
using UnityEngine.InputSystem;

// This script receives input from the Input System
// and controls the object directly via Transform
public class PlayerInputDemo : MonoBehaviour
{
    // Movement speed of the object
    // Can be adjusted in the Inspector
    public float moveSpeed = 5f;

    // Stores the current movement direction
    // (for example: (-1,0), (1,0), (0,1), or (0,0))
    private Vector2 moveInput;

    // This method is called by Player Input
    // when the Move action is triggered
    public void OnMove(InputAction.CallbackContext context)
    {
        // Read the Vector2 value from the Input System
        // It represents the movement direction
        moveInput = context.ReadValue<Vector2>();
    }

    // This method is called when the Jump action is pressed
    public void OnJump(InputAction.CallbackContext context)
    {
        // Make sure the action was actually performed,
        // not canceled or in progress
        if (!context.performed) return;

        // As a simple visual effect,
        // rotate the object by 90 degrees
        transform.Rotate(0f, 0f, 90f);
    }

    // Update is called once per frame
    void Update()
    {
        // Move the object every frame
        // as long as there is a non-zero movement direction
        transform.position +=
            (Vector3)(moveInput * moveSpeed * Time.deltaTime);
    }
}

💡 What is InputAction.CallbackContext?
This is the “execution context” of an action from the Input Actions asset. Unity provides already processed input data here — not a specific key, but the value produced by your settings (Action Type, Control Type, and Bindings).

How to read the data:
For value-based actions (Value), we read them using ReadValue<T>(): for example, Vector2 for movement (Move) or float for an axis. For buttons (Button), the exact value is usually less important than the moment the action triggers — that’s why we check the phase, such as context.performed.

Note an important detail: the OnMove and OnJump methods do not move the object directly every frame. They only update the input state or react to an event.

The actual movement happens in Update(). This approach makes the code clearer and more predictable: input communicates the player’s intention, while movement logic is handled separately.

💡 The same principle is used in more complex projects: the Input System handles input, and game systems are responsible for behavior.

Setting Up Player Controls Using Player Input

First, let’s create a simple player object. Add a triangle sprite to the scene: GameObject → 2D Object → Sprite → Triangle. It will serve as a clear visual example of a controllable object.

Select the created object and add two components to it: Player Input and PlayerInputDemo.

In the Player Input component, make sure that the Actions field is set to the PlayerControls asset (or select it manually).

Set the Default Map field to Player. This is the name of the Action Map we created in the PlayerControls asset, and it will be active when the scene starts.

Set the Behavior option to Invoke Unity Events. This means that actions from the Input System will trigger corresponding events, which we can connect to script methods.

Expand the Events section, then the Player group. Inside, you will see two events — Move and Jump — corresponding to the actions defined in the PlayerControls asset.

In the Move event, add a new callback. As the target object, select the triangle object you created, and as the method, choose OnMove from the PlayerInputDemo script.

Similarly, in the Jump event, select the same object and assign the OnJump method from the PlayerInputDemo script.

Configuring the Player Input component and Move and Jump events for the Triangle object
The Player Input component linked to the PlayerControls asset and the PlayerInputDemo script methods.

Now, when you run the scene, you’ll be able to control the object using the movement keys, and rotate it by pressing Space.

What’s Important to Understand About the Input System

In this tutorial, we covered the basic principles of Unity’s new Input System. In practice, it offers many more advanced features — support for gamepads, key rebinding, multiple control contexts, and complex input scenarios. All of these can be introduced gradually, as needed in a specific project.

To start with, it’s important to remember one key idea: input in Unity is not a single operation, but a sequence of steps.

  1. Describing input data.
    First, you define what type of data you are interested in: a button, a number, a direction vector, and so on. This is determined by the Action Type and Control Type settings in the Input Actions asset.
  2. Passing data to code.
    Next, Unity delivers this data to your code using the chosen method — through events (Invoke Unity Events), messages, or direct callbacks. At this stage, raw input becomes a clear signal for game logic.
  3. Processing the data.
    Only after that do you decide what to do with the received input: move an object, trigger an action, change a state, or pass the data on to other game systems.

This approach separates responsibilities: the Input System handles input acquisition and interpretation, while game code is responsible for object behavior. This makes projects more flexible and easier to understand, especially as complexity grows.

💡 You don’t need to use all Input System features right away. For learning and simple tutorials, it’s enough to understand this chain of steps — the rest can be added when it becomes truly necessary.

Direct Input Using the Input System

Since tutorials on the site are often read out of order, configuring Input Actions for every example would require unnecessary setup time. In addition, there is no guarantee that all readers are using the same input configuration in Unity.

For this reason, we will often use direct access to input devices through the new Input System, without setting up assets and Action Maps in advance.

This approach is not very flexible and is not suitable for complex projects, but it requires no preparation and allows you to move straight into learning game mechanics. This makes it convenient for simple examples and lightweight prototypes, including educational tutorials.

Conceptually, this method is similar to the old Input Manager: we directly query the state of the keyboard and mouse. In most cases, two movement axes and mouse input are all that’s needed.

Next, we’ll look at how this input method works and how to use it in simple scenarios without any additional setup.

Direct Input for Most Tutorials

In educational tutorials, controls are usually limited to a basic set of input: movement using WASD or arrow keys, a single action button (usually Space), the mouse cursor position, and two mouse buttons — left and right.

For such scenarios, there is no need to configure Input Actions, Action Maps, or additional assets. Instead, you can directly access input devices through the new Input System.

Don’t forget to include the new input system namespace:

using UnityEngine.InputSystem;

After that, you get direct access to the keyboard and mouse and can read their state in code. Conceptually, this approach is close to the old Input Manager, but it uses a modern API and does not depend on project settings.

Below are example scripts for handling movement, buttons, and mouse position using this input method. This set is sufficient for most simple prototypes and learning examples.

💡 Direct input is convenient for tutorials and quick prototypes. The more flexible features of the Input System (Action Assets, contexts, remapping) make sense to introduce only when the project truly requires them.

Direct Input: Keyboard and Mouse

Below are examples of direct input using the new Input System. This approach is suitable for most tutorials and simple prototypes where basic controls are needed without prior asset setup.

Movement (WASD / arrow keys)

We assemble the movement direction manually, similar to how axes worked in the old Input Manager.

using UnityEngine.InputSystem;

float x = 0f;
float y = 0f;

if (Keyboard.current != null)
{
    if (Keyboard.current.aKey.isPressed || Keyboard.current.leftArrowKey.isPressed)
        x -= 1f;

    if (Keyboard.current.dKey.isPressed || Keyboard.current.rightArrowKey.isPressed)
        x += 1f;

    if (Keyboard.current.sKey.isPressed || Keyboard.current.downArrowKey.isPressed)
        y -= 1f;

    if (Keyboard.current.wKey.isPressed || Keyboard.current.upArrowKey.isPressed)
        y += 1f;
}

Vector2 move = new Vector2(x, y);

Mouse cursor position

Read the cursor position in screen coordinates.

using UnityEngine.InputSystem;

Vector2 mouseScreenPos = Mouse.current.position.ReadValue();

If needed, screen coordinates can be converted to world coordinates using the camera.

Vector3 mouseWorldPos =
    Camera.main.ScreenToWorldPoint(mouseScreenPos);

Mouse buttons

Checking presses of the left and right mouse buttons.

using UnityEngine.InputSystem;

if (Mouse.current != null)
{
    if (Mouse.current.leftButton.wasPressedThisFrame)
        Debug.Log("Left mouse button");

    if (Mouse.current.rightButton.wasPressedThisFrame)
        Debug.Log("Right mouse button");
}

Space (action button)

Checking a single press of the Space key.

using UnityEngine.InputSystem;

if (Keyboard.current != null &&
    Keyboard.current.spaceKey.wasPressedThisFrame)
{
    Debug.Log("Space pressed");
}

These examples cover most control scenarios in educational projects. The game logic remains simple and clear, while input is handled without any additional setup.

Summary: Which Approach Should You Choose?

Unity’s input system has come a long way, and today the choice of approach depends not on what is “right” or “wrong”, but on the scale and goals of your project.

Method Best use case
Legacy Input Learning basic logic, supporting very old projects.
Direct Input Quick prototypes, tutorials, simple PC games.
Action Assets More serious projects, gamepads, mobile and cross-platform games.

In most lessons on this site, we will use Direct Input — directly polling the keyboard and mouse. This avoids spending time configuring assets in every example and allows you to focus on what really matters: building game mechanics.

It’s important to remember that there is no “correct” or “incorrect” way to handle input. What matters is choosing the approach that solves the problem efficiently and without unnecessary complexity in a given situation.