Fix: âYou are trying to read Input using the UnityEngine.Input classâ
If you are following an older Unity tutorial, and you run into an error with code like this:
using UnityEngine;
public class OldInputMovement : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private void Update()
{
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
transform.Translate(new Vector3(x, y, 0) * speed * Time.deltaTime);
}
}
You will be rudely greeted by this message in the console (Unity 6.3 and later):
InvalidOperationException: You are trying to read Input using the
UnityEngine.Input class, but you have switched active Input handling
to Input System package in Player Settings.
You may be wondering what happened.
The short answer is that this is using Unityâs old Input Manager. In a new Unity 6 project, the project is configured to use the newer Input System instead.
You may find tutorials telling you to simply enable both input systems. That will make the old code work, but thatâs not what I want to do here.
Even today you still find popular articles with workarounds to call the Input System keyboard handlers directly like this :
// Equivalent to Input.GetKey() (held down)
if (Keyboard.current.wKey.isPressed)
{
Debug.Log("W key is being held down.");
}
For a large project that has complex input, this makes complete sense. Refactoring complex input code is not trivial.
But, if we are starting a new project, I would rather update the code to use the new Input System instead of going back to the old one. Even if I need to spend some time on making the new system work.
This is because the new Input System gives you very robust functionality out of the box, saving you potentially many lines of code. For example, by using âActionsâ that are defined in the Input Asset, you potentially avoid having to then find code for gamepads, touch input, and so on.
So letâs take this simple movement example and update it.
What weâre starting with
Our starting point is a basic 2D movement script.
The important part is this:
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
The old Input Manager gives us two values, one for horizontal movement and one for vertical movement.
We then combine those two values into a Vector3 and use it to move our player.
There isnât anything particularly wrong with the idea here. We still need to get a movement value and use it to move the player.
What changes is how we get that input.
Instead of asking the old Input Manager for "Horizontal" and "Vertical", weâll create an Input Action called Move.
Or better yet, the default installation of Unity 6 and above comes with a pre-defined input asset and a Move action that is already mapped to gamepads, touch, etc.
I cover this in detail in my separate 2D movement video here
Why Input.GetAxis doesnât work
Input.GetAxis is part of Unityâs legacy Input Manager.
Unity has a newer Input System that uses Input Actions and bindings to separate what the player is trying to do from the physical device they are using.
For example, our game doesnât really care whether the player presses the W key or moves a gamepad stick.
What the game cares about is:
The player wants to move.
So weâll create a Move action that gives us a Vector2. Or just map to the default action Unity provides.
You can read more about Unityâs Input System in the official Unity documentation.
Should I just enable both input systems?
You can.
If you go to:
Edit â Project Settings â Player â Active Input Handling
you will find options for the old Input Manager and the new Input System.
Selecting Both allows older code using Input.GetAxis to continue working.
There are certainly situations where that practice is useful, particularly if you are working on an existing project that still uses the old Input Manager.
But that isnât what weâre doing here.
Weâre starting with a new Unity project, so letâs use the new Input System.
This also means that we donât have to worry about learning an API that Unity considers to be the legacy system.
Create the Input Action
So, even though I recommend using the default asset, letâs create a blank asset. I think that is a useful exercise to show for this type of experiment.
Iâve done both, and I found that when I wanted to hook into the UI, switch action maps, create pause screens, etc. adjusting the asset started to become a lot more work ârecreating the wheelâ. So I only add/remove bindings after creating a copy of the default asset Unity provides. Just my two cents.
First Steps
First, we need an Input Actions asset.
If you donât already have one, create an Input Actions asset in your project.
Open it and create an Action Map called:
Player
Then create an action called:
Move
For the Move action, set:
Action Type: Value
Control Type: Vector2
This is a little different from the old Input Manager.
Instead of having separate Horizontal and Vertical axes, our Move action gives us both values together as a Vector2.
Add the keyboard controls
For the keyboard, weâll use a 2D Vector composite.
This lets us map four keys to the four directions:
Up W
Down S
Left A
Right D
You can also add the arrow keys if you want to support those.
At this point, our Input Action knows what Move means for a keyboard.
We could also add a gamepad binding to the same action.
For example, we could bind the gamepadâs left stick to Move.
This is one of the useful things about the new Input System.
Our movement code doesnât need to know whether the player is using WASD or a gamepad.
Both controls are providing the same Move action.
Add PlayerInput
Now we need something to connect our Input Action to our player.
There are different ways of attaching our asset to code. I find that using PlayerInput is great to get you up and running, and itâs very beginner-friendly.
- Add a
PlayerInputcomponent to the player GameObject. - Assign your Input Actions asset to the Actions field.
- Set the Default Map to our
Playeraction map.
The important setting for this tutorial is Behavior.
Set it to:
Invoke Unity Events
This will allow us to hook the Move action directly to our code.
Once you do this, you should see an Events section in the PlayerInput component.
Our Move action will have its own Unity Event.
This is what weâre going to use to connect the Input System to our movement script.
Create the movement script
Now we can create our movement script.
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private Vector2 movement;
public void OnMove(InputAction.CallbackContext context)
{
movement = context.ReadValue<Vector2>();
}
private void Update()
{
transform.Translate(movement * speed * Time.deltaTime);
}
}
Letâs walk through this.
First, we need to add:
using UnityEngine.InputSystem;
This gives us access to the Input System types, including InputAction.CallbackContext.
Next, instead of having two separate variables for horizontal and vertical movement, we have one:
private Vector2 movement;
This is because our Move action is returning a Vector2.
The OnMove function
Next we have:
public void OnMove(InputAction.CallbackContext context)
{
movement = context.ReadValue<Vector2>();
}
This is the function that weâre going to connect to the Move Unity Event.
When the Input System invokes this event, it gives us an InputAction.CallbackContext.
We can then use:
context.ReadValue<Vector2>();
to get the movement value from the action.
So if the player presses W, we get a vector representing movement up.
If the player presses D, we get a vector representing movement to the right.
If the player presses W and D together, we get a vector containing both values.
The important thing here is that the movement script doesnât need to know which keys are being pressed.
It just receives a Vector2 telling it how the player wants to move.
Connect the Unity Event
Now we need to connect our PlayerInput component to the OnMove function.
On the PlayerInput component, find the Move event under the Events section.
Drag the GameObject containing our PlayerMovement component into the event.
Then select:
PlayerMovement â OnMove
The method should accept the InputAction.CallbackContext that Unity provides.
Now our input flow looks like this:
Keyboard / Gamepad
â
Move Action
â
PlayerInput
â
Unity Event
â
OnMove()
â
movement Vector2
Why donât we move the player inside OnMove?
You might be wondering why we donât simply put the movement code inside OnMove.
For example, why not do this?
public void OnMove(InputAction.CallbackContext context)
{
Vector2 movement = context.ReadValue<Vector2>();
transform.Translate(movement * speed * Time.deltaTime);
}
The problem is that input and movement are two different things.
The OnMove function is concerned with receiving input.
Update is concerned with actually moving the player.
So we use OnMove to update our movement value:
movement = context.ReadValue<Vector2>();
and then Update uses that value:
transform.Translate(movement * speed * Time.deltaTime);
This is also useful because the player continues moving while the input is being held.
We donât need an input event to fire every frame just to make the player move.
What happens when the player releases the key?
When the player releases the key, the Move action is canceled and the value we read is a zero vector.
So our movement value becomes:
(0, 0)
and the movement code effectively becomes:
transform.Translate(Vector2.zero * speed * Time.deltaTime);
which means the player stops moving.
What changed from the old code?
Letâs compare the two approaches.
The old code looked like this:
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
transform.Translate(new Vector3(x, y, 0) * speed * Time.deltaTime);
The new code is:
public void OnMove(InputAction.CallbackContext context)
{
movement = context.ReadValue<Vector2>();
}
private void Update()
{
transform.Translate(movement * speed * Time.deltaTime);
}
The actual movement isnât really the part that changed.
We still have a movement value, and we still use Time.deltaTime to make the movement frame-rate independent.
What changed is where the input comes from.
Instead of:
Input.GetAxis()
we now have:
Input Action â PlayerInput â Unity Event
And instead of having separate horizontal and vertical axes, our Move action gives us a Vector2.
Why I prefer this approach for a new project
There are several different ways you can work with Unityâs new Input System.
You can use C# callbacks, read Input Actions directly, use generated C# classes, and use different behaviors with PlayerInput.
For this example, Iâm using PlayerInput with Invoke Unity Events because I think it provides a straightforward way to understand what is happening.
We have an Input Action called Move.
The PlayerInput component receives that action.
The Unity Event calls our OnMove function.
Our movement script then deals with the movement.
There is a clear separation between the input itself and what the game does with that input.
It also means that if we decide later that we want to support a gamepad, we donât need to rewrite our movement code.
We just add another binding to the Move action.
So what should you do with older Unity tutorials?
If you are following an older tutorial and you see:
Input.GetAxis("Horizontal")
or:
Input.GetAxis("Vertical")
you donât necessarily need to turn on the old Input Manager.
Instead, look at what the tutorial is actually trying to achieve.
In this case, the tutorial wants to know:
How does the player want to move?
With the new Input System, we can represent that as a Move action returning a Vector2.
Thatâs the important part.
The old code and the new code are solving the same problem. Weâre just using the current Input System to do it.
Final Code
Here is the complete movement script:
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private float speed = 5f;
private Vector2 movement;
public void OnMove(InputAction.CallbackContext context)
{
movement = context.ReadValue<Vector2>();
}
private void Update()
{
transform.Translate(movement * speed * Time.deltaTime);
}
}
You should now be able to take an older tutorial that uses Input.GetAxis and update the movement portion to use Unityâs new Input System instead.
And if you are following along with my other Unity tutorials, this is also the approach weâll use going forward.
If you need the scripts in a downloadable format, you can check out my GitHub.
Let me know if you have any questions!