Image for post
Image for post
Photo by Igor Karimov on Unsplash

Modularity Boost with New Input System in Unity

Get started with the new input system to add cross-platform support in your games with a modular approach

Introduction

Unity announced a new input system around 2018. It had been in a preview state for a long time and changed a lot over time. The first stable version is released in 2020 but still hasn’t replaced the old input system by default.

So why Unity developed a new input system? What is the problem with the old one?

Well, the old one did a job for some time but the increasing number of devices and different kinds of controls made it quickly outdated. Also, It’s not a modular system due to its architecture.

A lot of people opt for 3rd party assets when it comes to cross-platform support and features like in-game input mappers.

The new input system promises to solve all these problems in a modular and customizable package.

In this piece, we’ll go through how to get started with the new system without drowning in detail. We’ll see how to implement some simple mechanics like walking, shooting, etc.

Installation

You can download the final version of the project material from my Patron page. You can also complete the tutorial without it but you will need to configure everything for yourself.

At the time of this writing, I’ve used Unity 2020.2.1f1 and Input System 1.0.1.

First of all, make sure that you have the correct version of Unity in your system with Unity Hub. After that create a new 3D project using the correct version of Unity.

Open up the package manager through theWindow/Package Manager menu once your newly created project starts. In the Package Manager window change Packages: In Project option to Unity Registry. Find and select the Input Manager package in the list then click the install button. After a few seconds, you will see a warning window about enabling the new input system and restarting the project. Just click yes and wait for your project to restart.

Image for post
Image for post

That’s it. Now we can start using the new input system!

Setup

Before diving into any feature implementation we need to set up a scene.

Open up the SampleScene in the Assets/Scenes folder. Next, create a 3D terrain object in the scene as the ground and then create a 3D cube object in the active scene and name it Player.

Then create a C# script under the Assets/Scripts folder and name it PlayerMove. Open this newly created script in your IDE and paste the below boilerplate code into it. Save the file and add thePlayerMove script to the Player game object as a component. This component will add two more components to the Player game object: a RigidBody and a Player Input.

Please refer to official documentation about adding components to game objects if you have any questions.

The Player Input component is where the magic happens. It needs an input actions asset in order to work.

Image for post
Image for post

An input actions asset is where we can define actions like move, look, fire, etc., and then assign keybindings for those actions. While it’s possible to create everything from scratch there is an option to create a pretty useful default one.

To create a default input actions asset, click on the Create Actions... button in the Player Input component. Name it Actions and save it under the Assets/Settings/Input folder. Now we have an input actions asset assigned to the Player Input component.

Input Actions

Let’s look into this created input actions asset. Double click on the created asset to open it up in the Input Actions window.

Image for post
Image for post

We will use this window to setup all about input mappings.

On the left side, there are Action Maps. These are different profiles for different action groups. For example, we can create an action group for the player character and then create a different group for vehicle driving. Also for the UI actions.

In the middle, there areActions for the selected action map. These are the actions that a player can do in the game. For example, moving, jumping, shooting, opening doors, etc.

Each action has its bindings. We can bind multiple controls under one action. For example, we can bind WASD keys for and the left stick for gamepads to move action.

On the right side, there are properties for the selected action or binding.

We can define the action type here. Is it a value like a mouse position or a button? Depending on the action type we can add interactions like tap or multi-tap. It’s also possible to add processors. For example, we can add Vector2.Normalize as a processor in case of a Vector2 action type. This way we’ll get the normalized value when we read the action value in our code.

Implement Player Movement

Ok, now it’s time to get our hands dirty with coding. Let’s implement some basic player movement with the current setup.

First, add the following code into PlayerMove script.

Next, open up the Player Input component and change Behaviur option to Invoke Unity Events. This will add an Events option to the list. Expand the Events and then Player options. You will see a list of action events that are defined for thePlayer action map inside the input actions asset.

Image for post
Image for post

Now it’s time to connect OnMove method in PlayerMove script to Move event in here.

Click the + button under the Move action and then drag the Player game object from the scene to the object section here. After that, you will be able to select the PlayerMove.OnMove method from the dropdown menu.

That’s it. Now you can click on the Player button and move the player character with a keyboard or a gamepad!

Image for post
Image for post

Events

In the previous example, we’ve used Unity Events with the Input System by changing the Behavior option. It’s a great solution for decoupling your components and modularity.

There are also other behavior options. These are all code-driven. There might be use cases for these in your project or not.

As a starting point, I would recommend you to stick with the Unity Events option. It’s modular and you can control the dependencies from the editor. If you’re still curious about the other options please refer to Notification behaviors in the official documentation.

Let’s talk about the event code a bit.

If we look at the OnMove method in the PlayerMove script it takes a parameter: InputAction.CallbackContext context. This contains a lot of information about the action.

Then we’ve used context.ReadValue<Vector2>() to read the direction value for the movement. You might wonder how to know what kind of data we’ll get from this method. Is it just Vector2? Well, no.

If we open the Input Actions window again and select Move action in the Player action map then we’ll see action properties on the right side. The Action type is Value and the Control Type is Vector2. This is how we can define what kind of value we’ll get from the ReadValue method.

If we change the Action type to for example toButton then it’s a different story which we will cover in Fire a Projectile section.

Fire a Projectile

In terms of input handling, firing a projectile is not that different than the movement. There is already a Fire action in the created input actions asset. All we need to do is create an OnFire method and add it to the Fire event in the Player Input component like how we’ve done it for OnMove in the previous example.

Create a new script called PlayerFire and paste the below code into it.

Add this script to the Player game object in the scene. Connect OnFire method to Fire event in PlayerInput component. Notice the serialized field projectilePrefab in the script. In order to fire a projectile, we need to create one.

Create a 3D sphere object in the Assets/Prefabs folder as a prefab and name it Projectile. Please refer to official documentation about creating prefabs. Add a Rigidbody component to it and uncheck the Use Gravity checkbox. Add it to the PlayerFire component in the editor.

That’s it. Now you can click on the play button and keep the left mouse button pressed to fire projectiles.

Image for post
Image for post

If we look at the OnFire method we will notice that it’s a little bit different than theOnMove method. We don’t read any values here. Instead, we read the action phase and perform different actions respectively.

That’s because the fire action type is a button. In the case of a button, all we care about is if a button is clicked, holt, or released. In the performed phase, we start the fire and stop it in the canceled phase. We can fine-tune these phases in the Input Actions window.

Challenge

So far we’ve seen the fundamentals of the new input system. We’ve seen the Player Input component, Input Actions, and how to use these in our game by moving the player and firing projectiles.

Now it’s your turn. Try to create a jumping behavior for example. In the default actions asset, we don't have a Jump action. Configure it using the Input Actions window and implement a simple jump behavior for the character.

Conclusion

The new input system for Unity is great. I especially love how modular it is. It’s also pretty easy to implement features like control mapping, touch controls, and so on.

Check out my Patreon page to support me and see some cool offers!

Stay tuned for more content like this!

Written by

Hey! I’m Emre, an indie game dev using Unity and C#. I’m here to share my knowledge and experience with you! Say hi to me on https://emreloper.dev 👋

Get the Medium app