Rene-Damm.github.io

View on GitHub

How do I…?

Get Started:

Players:

Actions:

Rebinding:

UI:

Devices:

Gamepads:

Touch:

Keyboards:

Sensors:

Debugging:

Testing:

Scripting:


Get Started

… set up input in a blank project?

  1. Install the input system package through the Unity Package Manager as per the documentation. Installation This adds the UnityEngine.InputSystem API to your project and enables support for it in the native Unity runtime.

  2. Add a PlayerInput to a GameObject. Add Player This adds an .inputactions asset to your project and sets up one player in the game to use those actions.

    If you prefer to not rely on MonoBehaviours and GameObjects here, see “How do I set up input without using MonoBehaviours”.

  3. Read input from the actions in script. Read Input
    public class CharacterController : MonoBehaviour
    {
        public InputActionReference fire;
        public InputActionReference move;
    
        public void Update()
        {
            if (fire.action.WasPressedThisFrame())
                Fire();
    
            var moveVector = move.action.ReadValue<Vector2>();
            Move(moveVector);
        }
    
        //...
    }
    

… set up uGUI to work with the input system?

Go to the EventSystem object and click the “Replace with InputSystemUIInputModule” button. Optionally, to use your own bindings instead of the default ones, drag your custom .inputactions asset into the Actions Asset field.

Setup UI

This will set up uGUI to receive events generated from input defined in an .inputactions asset. If needed, you can customize the bindings.

… keep using the old input system alongside the new one?

After installation, set Active Input Handling in Player Preferences to Both.

Use Old and New


Players

… have multiple players in my game?

Each PlayerInput represents one player. Having more than one GameObject with the PlayerInput component on it in the game creates multiple independent players. Each player gets assigned devices for exclusive use by that player.

Create a player prefab

Each player needs to be self-contained in so far as it should only respond to input from its own respective PlayerInput.

Player Prefab

Alternatively, you can respond to input using callbacks/events:

Read Input Callbacks

Join players from a lobby

A simple lobby setup can be created using PlayerInputManager.

Player Lobby

Alternatively, you can create a simple “press button to join” setup using InputSystem.onAnyButtonPress.

public class PlayerLobby : MonoBehaviour
{
    public GameObject playerPrefab;
    
    private IDisposable m_ButtonPressListener;
    
    private void OnEnable()
    {
        m_ButtonPressListener = InputSystem.onAnyButtonPress
            .Call(button =>
            {
                // Grab the device from the button control.
                var device = button.device;
                
                // Check if the device is already used by another player.
                if (PlayerInput.FindFirstPairedToDevice(device) != null)
                {
                    // It is. Ignore this button press.
                    return;
                }
                
                // It is not, so create a new player.
                var player = PlayerInput.Instantiate(playerPrefab, pairWithDevice: device);
                Debug.Log($"Player {player.playerIndex+1} joined");
            });
    }
    
    private void OnDisable()
    {
        m_ButtonPressListener?.Dispose();
    }
}

Spawn players

You can manually spawn new players using PlayerInput.Instantiate.

// Four players, each one on a gamepad.
PlayerInstantiate(playerPrefab, controlScheme: "Gamepad", Gamepad.all[0]);
PlayerInstantiate(playerPrefab, controlScheme: "Gamepad", Gamepad.all[1]);
PlayerInstantiate(playerPrefab, controlScheme: "Gamepad", Gamepad.all[2]);
PlayerInstantiate(playerPrefab, controlScheme: "Gamepad", Gamepad.all[3]);

Customize each player individually


Actions

… let the user rebind an action to a different control?

… save and load rebinds?

Call SaveBindingOverridesAsJson to create a string containing all rebinds for the given set of actions and LoadBindingOverridesFromJson to restore rebinds from such a string.

void SaveUserRebinds(PlayerInput player)
{
    var rebinds = player.actions.SaveBindingOverridesAsJson();
    PlayerPrefs.SetString("rebinds", rebinds);
}

void LoadUserRebinds(PlayerInput player)
{
    var rebinds = PlayerPrefs.GetString("rebinds");
    player.actions.LoadBindingOverridesFromJson(rebinds);
}

Devices

… find out which devices are available?

foreach (var device in InputSystem.devices)
{
    if (device is Keyboard)
        Debug.Log("Keyboard detected");
    else if (device is Mouse)
        Debug.Log("Mouse detected");
    else if (device is Gamepad)
        Debug.Log("Gamepad detected");
    else if (device is Touchscreen)
        Debug.Log("Touchscreen detected");
    else
        Debug.Log("Other kind of device: " + device);
}

… wait for the player to press a button on any device?

// Call delegate once on button press.
InputSystem.onAnyButtonPress
    .CallOnce(button => Debug.Log($"Button {button} was pressed!"));
   
// Call delegate whenever a button is pressed.
// NOTE: This will add overhead to event processing until the listener is disposed.
var listener = InputSystem.onAnyButtonPress
    .Call(button => Debug.Log($"Button {button} was pressed!"));

// To dispose of the listener.
listener.Dispose();

… read input directly from a device?

// Keyboard.
if (Keyboard.current.spaceKey.isPressed)
    Debug.Log("Space key is pressed");

// Mouse.
if (Mouse.current.leftButton.isPressed)
    Debug.Log("LMB is pressed");
var mousePosition = Mouse.current.position.ReadValue();

// Gamepad.
if (Gamepad.all[0].buttonSouth.isPressed)
    Debug.Log("A button on first gamepad is pressed");
var leftStick = Gamepad.all[0].leftStick.ReadValue();

// Generic.
var device = InputSystem.devices[0];
var buttonSouthValue = device["buttonSouth"].ReadValueAsObject(); // Allocates.
var leftStickX = ((AxisControl)device["leftStick/x"]).ReadValue();
foreach (var control in device.allControls)
    Debug.Log($"Control {control.path} = {control.ReadValueAsObject()}");

Gamepads

… let a gamepad control the mouse cursor?

… determine whether the player is using an Xbox or PlayStation controller?

// Xbox/XInput.
if (gamepad is XInputController)
   Debug.Log("Using Xbox controller");
   
// PlayStation.
if (gamepad is DualShockGamepad)
   Debug.Log("Using DualShock controller");

For PlayerInput such as when receiving OnControlsChanged.

void OnControlsChanged(PlayerInput player)
{
    if (player.GetDevice<XInputController>() != null)
        Debug.Log("Player is using an Xbox controller");
    else if (player.GetDevice<DualShockGamepad>() != null)
        Debug.Log("Player is using a PlayStation controller");
}

You can also utilize these classes in control schemes. For example, you can have a base “Gamepad” scheme with bindings shared between the controllers and then have additional control schemes specific to Xbox and PlayStation controllers. PlayerInput will pick the control scheme that best matches a given controller.

Xbox and PlayStation

Note:

Xbox and PlayStation and Switch controller support is available across platforms. However, when working on these consoles, you will, in addition to the input system package, need the console-specific input package available through the respective licensee channels.


Keyboards

… bind to the ‘a’ text input?

Use the “By Character Mapped to Key” group in the control picker instead of the “By Location of Key (Using US Layout)” group.

BindChar

When manually creating actions/bindings in script:

// Bind to the key to the right of the CAPS LOCK key.
// This binding will refer to the key regardless of the language
// layout currently selected.
var action1 = new InputAction(binding: "<Keyboard>/a");

// Bind to the key that inputs the 'a' character. If no such key
// exists in the currently active language layout, the action will
// remain unbound.
var action2 = new InputAction(binding: "<Keyboard>/#(a)");

… have two players use the same keyboard?

PlayerInput will, by default, not assign two players to the same device. However, you can manually switch players to use the same keyboard or spawn them that way from the beginning.

  1. Create two separate control schemes for the keyboard. SplitKeyboard
  2. Spawn players using the control schemes or switch existing players to them.
     // Spawn two players. One using WASD and one using arrows.
     PlayerInput.Instantiate(playerPrefab, controlScheme: "KeyboardWASD", pairWithDevice: Keyboard.current);
     PlayerInput.Instantiate(playerPrefab, controlScheme: "KeyboardArrows", pairWithDevice: Keyboard.current);
       
     // Alternatively, switch existing players.
     PlayerInput.all[0].SwitchCurrentControlScheme("KeyboardWASD", Keyboard.current);
     PlayerInput.all[1].SwitchCurrentControlScheme("KeyboardArrows", Keyboard.current);
    

… receive text input?

Use Keyboard.onTextInput.

Keyboard.current.onTextInput +=
    character => Debug.Log($"Char {character} input received");

For IME input, use Keyboard.onIMECompositionChange.


Sensors

… read gyroscope input?

You can read out rotation rates via Gyroscope.angularVelocity.

// Turn on sensor.
InputSystem.EnableDevice(Gyroscope.current);

// Read out value.
var value = Gyroscope.current.angularVelocity.ReadValue();

You can read out gravity via GravitySensor.gravity.

// Turn on sensor.
InputSystem.EnableDevice(GravitySensor.current);

// Read out value.
var value = GravitySensor.current.gravity.ReadValue();

You can read out acceleration via LinearAccelerationSensor.acceleration.

// Turn on sensor.
InputSystem.EnableDevice(LinearAccelerationSensor.current);

// Read out value.
var value = LinearAccelerationSensor.current.acceleration.ReadValue();

You can read out attitude via AttitudeSensor.attitude.

// Turn on sensor.
InputSystem.EnableDevice(AttitudeSensor.current);

// Read out value.
var value = AttitudeSensor.current.attitude.ReadValue();

These controls can also be bound in actions.


Debugging

… test my game on Android or iOS?

There are several ways to test your mobile game with respect to input.

Unity Remote

Device Simulator

Build and run player


Testing

… create mock input in an automated test?

Automated Tests

  1. Create a test assembly and add references for UnityEngine.InputSystem and UnityEngine.InputSystem.TestFramework.
  2. Add com.unity.inputsystem to testables in your project’s manifest.json file.
// InputTestFixture isolates input for testing. No devices and no input from the host machine.
// Automatically cleans up any input-related setup put in place by a test.
class Tests : InputTestFixture
{
   [Test] // Works with [Test] and [UnityTest].
   public void Test()
   {
      // Can add devices here or in setup. Whatever we add to or register with the
      // input system will be removed after the test has finished.
      var mouse = InputSystem.AddDevice<Mouse>();

      // Move the mouse.
      Set(mouse.position, new Vector2(123, 234));

      // Can also step input manually in tests. In a [UnityTest], this
      // happens automatically whenever Unity advances by a frame.
      InputSystem.Update();
   }
}

More info in the docs.

… set an action’s value programmatically?

Actions need to be bound to controls. They do not have values by themselves.

You can create an InputDevice automatically from an InputActionAsset using the following function. This creates controls on the device that mirror the actions found in the asset.

// Take a set of actions and create an InputDevice for it that has a control
// for each of the actions. Also binds the actions to that those controls.
public static InputDevice SetUpMockInputForActions(InputActionAsset actions)
{
    var layoutName = actions.name;
  
    // Build a device layout that simply has one control for each action in the asset.
    InputSystem.RegisterLayoutBuilder(() =>
    {
        var builder = new InputControlLayout.Builder()
            .WithName(layoutName);
  
        foreach (var action in actions)
        {
            builder.AddControl(action.name) // Must not have actions in separate maps with the same name.
                .WithLayout(action.expectedControlType);
        }
  
        return builder.Build();
    }, name: layoutName);
  
    // Create the device.
    var device = InputSystem.AddDevice(layoutName);
  
    // Add a control scheme for it to the actions.
    actions.AddControlScheme("MockInput")
        .WithRequiredDevice($"<{layoutName}>");
  
    // Bind the actions in the newly added control scheme.
    foreach (var action in actions)
        action.AddBinding($"<{layoutName}>/{action.name}", groups: "MockInput");
  
    // Restrict the actions to bind only to our newly created
    // device using the bindings we just added.
    actions.bindingMask = InputBinding.MaskByGroup("MockInput");
    actions.devices = new[] { device };
  
    return device;
}

Using this function, you can, for example, do:

var mockInput = SetUpMockInputForActions(actions);
Press((ButtonControl)mockInput["fire"]);

… record and replay input?


Scripting

… set up input without using MonoBehaviours?

While it is possible to create input actions from scratch through the API, an easier way is usually to create an .inputactions asset and turn it into code automatically by turning on Generate C# Class in its properties.

Generate Class

public class InputController : MonoBehaviour
{
    private MyActions m_Actions;
    
    private void Start()
    {
        m_Actions = new MyActions();
    }
    
    private void OnEnable()
    {
        m_Actions.Player.Enable();
    }
    
    private void OnDisable()
    {
        m_Actions.Disable();
    }
    
    private void Update()
    {
        if (m_Actions.Player.Fire.WasPressedThisFrame())
            Fire();
            
        var moveValue = m_Actions.Player.Move.ReadValue<Vector2>();
        Move(moveValue);
    }
    
    //...
}

To instead create input actions entirely in code:

// "Standalone" action.
var action = new InputAction(binding: "<Gamepad>/buttonSouth");
action.Enable();

// "Standalone" action map.
var map = new InputActionMap();
var action1InMap = map.AddAction("action1", binding: "<Gamepad>/buttonSouth");
var action2InMap = map.AddAction("action2", binding: "<Gamepad>/rightTrigger);
map.Enable();

// Entire action asset.
var asset = ScriptableObject.CreateInstance<InputActionAsset>();
var gameplay = asset.AddActionMap("Gameplay");
var moveAction = gameplay.AddAction("Move");
moveAction.AddCompositeBinding("2DVector")
    .With("Up", "<Keyboard>/w")
    .With("Left", "<Keyboard>/a")
    .With("Down", "<Keyboard>/s")
    .With("Right", "<Keyboard>/d");
var fireAction = gameplay.AddAction("Fire", binding: "<Mouse>/leftButton");
asset.Enable();

Support control schemes

Manage multiple players