Multiple controllers (711317)

Hi,

I’m using the latest Input System implementation (from github/develop). I’ve set up an Input Action Asset, and I’m attaching my actions through this:

_input.GamePlay.Move.performed += Move;
_input.GamePlay.Aim.performed += Aim;
_input.GamePlay.Fire.started += StartFire;
_input.GamePlay.Fire.performed += StopFire;
_input.GamePlay.AltFire.started += StartAltFire;
_input.GamePlay.AltFire.performed += StopAltFire;

Now… How do I use multiple controllers (ie. multiple players) using this system? Is there an example using the current system?

Ideally it’d be something along the lines of:

_input.GamePlay.GetDevice(playerIndex).Move.performed += Move;

I see that we can get a device id through the InputAction.CallbackContext, but that seems awfully clunky, and the id seems to be based on the connection number, so disconnecting and reconnecting the same controller won’t give the same id.

Thanks.

2 Likes
_input.GamePlay.GetDevice(playerIndex).Move.performed += Move;

The equivalent of this is

var player1 = _input.GamePlay;
var player2 = player1.Clone();

player1.ApplyBindingOverridesOnMatchingControls(deviceForPlayer1);
player2.ApplyBindingOverridesOnMatchingControls(ceviceForPlayer2);

player1.Move.performed += Move;
player2.Move.performed += Move;

API is still clunky. Also, ATM you have to manage allocating devices to player yourself. I expect there ultimately will be APIs to help you with that. Overall, there’s still various bits to improve around local multiplayer.

But anyway, the central idea is that you have an action map with general bindings (say, for gamepad) and clone it so that each player gets a copy and then you “specialize” each copy on the specific device allocated to the player.

1 Like

Cool, that’ll do, thanks!

Just a quick aside. I found an issue, when I call:

_input.GamePlay.Disable();

…and the system gets input events I get spammed with errors. I have to call Disable() on every InputAction in _input.GamePlay, I’m pretty sure that negates the point of being able to call GamePlay.Disable(). Is this a known issue or should I get the error messages and post an issue to github?

Ok, that’s not right indeed :slight_smile: Not a known issue and I gave it a quick go and couldn’t reproduce. Opening a ticket on GitHub with the errors would be great.

1 Like

can u help me with the error @Rene-Damm ?

public SkyHunters_Inputs inputsMap;

    public List<InputDevice> devicesInput = new List<InputDevice>();

    public List<SkyHunters_Inputs> inputsMaps = new List<SkyHunters_Inputs>();
    // Start is called before the first frame update
    void Awake() {
        inputsMap.SpaceShip.Join.performed += ctx => {
            var currentDevice = ctx.control.device;
            if (devicesInput.Contains(currentDevice)) return;
            var playerClone = new SkyHunters_Inputs();
            playerClone.SetAsset(ScriptableObject.Instantiate(inputsMap.asset));
            var Intvalue = InputActionRebindingExtensions.ApplyBindingOverridesOnMatchingControls(playerClone.SpaceShip, ctx.control);
            Debug.Log($"Some index return on Override Binding {value}");
            inputsMaps.Add(playerClone);
            playerClone.SpaceShip.Shoot.performed += xControl => {
                PlayerShooting();
            };
            PlayerJoining(currentDevice);
        };
    }

the Intvalue return 0 every time!

@GilbertoBitt Could you show me the binding for Spaceship.Join and a Debug.Log of the ctx.control.path? Not yet sure whether it’s the setup or whether it’s a bug at work here that prevents the override from succeeding.

BTW there’s an easier way to do this now by just setting the devices property on the InputActionAsset. This way you don’t have to go into overrides. Instead, you can just tell the actions to only bind to a specific set of devices.

There’s also InputUser which can handle most of the user/action management for you. We have some high-level MonoBehaviours in the pipeline that should make using this functionality a breeze (well, hopefully :)). This includes player join support with no custom scripting required.

i still don’t know howto pair device to input action asset! and inputUser i want to learn how to user on plataform that suport account pairing like xbox and on pc or others that don’t have this funcion and how to attach more than one device like mouse/keyboard and other just one like gamepad.

here is the project uploaded is a small project for testing this things.

https://drive.google.com/file/d/16kH0Ks0IWwMSIaQQrMKrx5FTEX2kpDMf/view?usp=sharing

@Rene-Damm i change my MB_InputSystem to this!

MB_InputSystem

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Graphing;
using UnityEngine;
using UnityEngine.Experimental.Input;
using UnityEngine.Experimental.Input.Interactions;
using UnityEngine.Experimental.Input.Plugins.Users;
using UnityEngine.Experimental.Input.Utilities;

public class MB_InputSystem : MonoBehaviour {
    public SkyHunters_Inputs inputsMap;
    // Start is called before the first frame update
    private InputDevice _keyboardInput;
    private InputDevice _mouseInput;
    void Awake() {
        _keyboardInput = Keyboard.current.device;
        _mouseInput = Mouse.current.device;
        inputsMap.SpaceShip.Join.performed += ctx => {
            var currentDevice = ctx.control.device;
            foreach (var device in InputUser.GetUnpairedInputDevices()) {
                Debug.Log(device);
            }
            if (!InputUser.GetUnpairedInputDevices().Contains(currentDevice)) return;
            var input = new SkyHunters_Inputs();
            input.SetAsset(ScriptableObject.Instantiate(inputsMap.asset));
            var user = InputUser.PerformPairingWithDevice(currentDevice, options:InputUserPairingOptions.ForcePlatformUserAccountSelection);
            if (currentDevice == _mouseInput) {
                user = InputUser.PerformPairingWithDevice(_keyboardInput, user, options:InputUserPairingOptions.None);
            } else if (currentDevice == _keyboardInput) {
                user = InputUser.PerformPairingWithDevice(_mouseInput, user, options:InputUserPairingOptions.None);
            }
            user.AssociateActionsWithUser(input);
            input.SpaceShip.Shoot.performed += context => ShootStarted();
            input.Enable();
        };
      
        foreach (var user in InputUser.all) {
            user.actions.Enable();
            SkyHunters_Inputs input = (SkyHunters_Inputs) user.actions;
            input.SpaceShip.Shoot.performed += context => ShootStarted();
          
            user.actions.Enable();
          
            //First Enable All than Disable it.
            input.SpaceShip.Join.Disable();
        }
    }

    public void ShootStarted() {
        Debug.Log("Shooting");
    }

    /*(bool, InputDevice, int) HasPlayerDevice(InputDevice currentDevice) {     
        return devicesInput.Contains(currentDevice) ? (true, currentDevice, devicesInput.IndexOf(currentDevice)) : (false, currentDevice, -1);
    }

    void PlayerShooting(InputDevice controlDevice) {
        var index = devicesInput.IndexOf(controlDevice);
        Debug.Log($"Player Shooting using {index}");
    }

    void PlayerJoining(InputDevice currentDevice) {
        Debug.Log("Player Just Join the Party!");
        devicesInput.Add(currentDevice);
        Debug.Log(currentDevice);
    }*/

    private void OnEnable() {
        inputsMap.SpaceShip.Join.Enable();
        foreach (var user in InputUser.all) {
            user.actions.Enable();
        }
    }

    private void OnDisable() {
        inputsMap.SpaceShip.Join.Disable();
        foreach (var user in InputUser.all) {
            user.actions?.Disable();
        }
    }
}

the only problem with it is that when using Xbox One Gamepad i’m not able to shoot unless i disconnect from pc and then reconnect again! then without the need to join again it start recoganizing the shooting action the keyboard and Mouse input work like a charm but has u r able to see i need make then current to get a copy/reference of InputDevice from keyboard and Mouse to then compare it inside the context callback without making then current! how i can solve the problem with Xbox One GamePad? it’s becouse i use
ForcePlatformUserAccountSelection on option? i will try change the option to see if work on pc without the need to reconnect the xbox one gamepad! maybe this will happen to any device but for now i only have xbox one to test it.

I’m having a hard time understanding how to implement this into my code, could you give me an example?

My initial suggestions are somewhat outdated by now. While the suggested setup is still doable, there are better ways now.

The simplest way to set this up these days is PlayerInput. It will automatically take care of cloning actions and restricting them to controllers for specific players. Simply have multiple PlayerInputs in your game (either instantiated by you or through PlayerInputManager; see documentation).

For doing it manually, there is a “devices” property on both InputActionAsset and InputActionMap now. Setting that will automatically restrict the given actions to just those devices. It’s what PlayerInput does internally but is accessible as a general API.

Hey, I have 2 prefabs with separate PlayerInputs set up. It automatically detects the controllers and assigns a unique one to each which is wonderful. However, when I try to assign the input actions to generic gamepad south button, they apply to both characters for some reason. I’m using separate scripts and my unity version is 2019.2.5f1. Am I forced to use two different inputs specific to the controller? (for example cross instead of south button) Here is the input debugger:

5018183--491408--upload_2019-9-30_22-15-44.png

Thanks in advance

1 Like

How do you set up those actions?

Judging from the input debugger, it appears that those two actions are not associated with either player. They will thus go and bind to everything in the system. For actions to automatically be restricted to the devices of a player, they must belong to the same InputActionAsset as that assigned to the player (PlayerInput may duplicate the asset instance so it really must be the same asset instance used by a particular player).

I’m having the problem you described, where every device controls every player. I have a Player Input on each player object and this in my code.

inputAction = new PlayerInputActions();
inputAction.Player.Jump.performed += _ => PlayerJump();
}```

Any time I press jump on any device, all players jump. What am I missing here? From the documentation, it sounds like it should assign a different device to each Player Input automatically, but I'm clearly breaking that functionality somehow.

To use multiple PlayerInput independently easily, you should use the PlayerInputManager, it will take care of that for you

I added a PlayerInputManager, joining players manually because I already had my spawning system all setup with the old input manager, and input debugger looks like the devices are correctly assigned, but it’s still behaving the same way.

I see two things there that make me think something is wrong that may be related to my problem, but I’m not sure how:

  1. 50 actions per user. It should only be 10, but they’re all being duplicated four times
  2. All actions for the second user disabled

5094251--502010--Screenshot (4).png

You’re mixing code-generated input actions with PlayerInput. ATM the two don’t yet go together. The code you posted will end up creating a set of input actions independent of PlayerInput. The code-generated path ATM has no support for control schemes and device pairing. So it ends up grabbing every possible device for every player. Thus the all-players-jump issue.

PlayerInput, when instantiated more than once, will duplicate actions. With 10 actions in the asset and 4 players in the game, you should see 40 actions.

From your shot it appears that you probably still have the “new PlayerInputActions” code in there. Removing that should result in the right amount of actions getting created.

Not sure about that one. Would need more information.

Ah ok, that makes sense. Removing “new PlayerInputActions” does get rid of all of those actions. So then if I’m not instantiating the component in my script, how would I go about mapping those controls in my script? Every example I’ve seen from someone using this system has had that in there. Or do I just need to map them through the component in the editor with Send Messages etc?

The Quick Start Guide may be of use. But yes, you can map them through Send Messages or individually through UnityEvents.

There will be an updated tutorial released soon that demonstrates this. The fact the tutorial posted on the blog showed the C# generation workflow was due to an internal communication mishap.

Ok, I’ll try that out. Thanks for your help on this.

Hello everyone,
I do not understand and need help.
I want to have 2 characters that move every 1 with a single controller and I don’t know how to do it.
could you give me a guide or something that explains it in a simple way
(or a script, and then I can use it to learn)
Thank you.
Here is a script in which I would like it to work:

using UnityEngine;
public class Move : MonoBehaviour
{
    PlayerControl controls;
    //direction horizontal
    float inputHorizontal;

    private void Awake()
    {
        controls = new PlayerControl();
        //inputs
        controls.Gameplay.Address.performed += ctx => inputHorizontal = ctx.ReadValue<float>();
        controls.Gameplay.Address.canceled += ctx => inputHorizontal = 0;
    }

    private void Update()
    {
        //Move
        if (inputHorizontal != 0)
        {
            transform.Translate(new Vector3(inputHorizontal * 10f, 0, 0)*Time.deltaTime);
        }
    }
    private void OnEnable()
    {
        controls.Enable();
    }
    private void OnDisable()
    {
        controls.Disable();
    }
}