Search Unity

Multiple controllers

Discussion in 'Input System' started by Wahooney, Aug 13, 2018.

  1. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    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:

    Code (CSharp):
    1. _input.GamePlay.Move.performed += Move;
    2. _input.GamePlay.Aim.performed += Aim;
    3. _input.GamePlay.Fire.started += StartFire;
    4. _input.GamePlay.Fire.performed += StopFire;
    5. _input.GamePlay.AltFire.started += StartAltFire;
    6. _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:
    Code (CSharp):
    1. _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.
     
    Just_Max_ and chjm655 like this.
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Code (CSharp):
    1. _input.GamePlay.GetDevice(playerIndex).Move.performed += Move;
    The equivalent of this is

    Code (CSharp):
    1. var player1 = _input.GamePlay;
    2. var player2 = player1.Clone();
    3.  
    4. player1.ApplyBindingOverridesOnMatchingControls(deviceForPlayer1);
    5. player2.ApplyBindingOverridesOnMatchingControls(ceviceForPlayer2);
    6.  
    7. player1.Move.performed += Move;
    8. 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.
     
    RavenOfCode likes this.
  3. Wahooney

    Wahooney

    Joined:
    Mar 8, 2010
    Posts:
    281
    Cool, that'll do, thanks!

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

    Code (csharp):
    1. _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?
     
  4. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Ok, that's not right indeed :) 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.
     
    Wahooney likes this.
  5. GilbertoBitt

    GilbertoBitt

    Joined:
    May 27, 2013
    Posts:
    111
    can u help me with the error @Rene-Damm ?

    Code (CSharp):
    1. public SkyHunters_Inputs inputsMap;
    2.  
    3.     public List<InputDevice> devicesInput = new List<InputDevice>();
    4.  
    5.     public List<SkyHunters_Inputs> inputsMaps = new List<SkyHunters_Inputs>();
    6.     // Start is called before the first frame update
    7.     void Awake() {
    8.         inputsMap.SpaceShip.Join.performed += ctx => {
    9.             var currentDevice = ctx.control.device;
    10.             if (devicesInput.Contains(currentDevice)) return;
    11.             var playerClone = new SkyHunters_Inputs();
    12.             playerClone.SetAsset(ScriptableObject.Instantiate(inputsMap.asset));
    13.             var Intvalue = InputActionRebindingExtensions.ApplyBindingOverridesOnMatchingControls(playerClone.SpaceShip, ctx.control);
    14.             Debug.Log($"Some index return on Override Binding {value}");
    15.             inputsMaps.Add(playerClone);
    16.             playerClone.SpaceShip.Shoot.performed += xControl => {
    17.                 PlayerShooting();
    18.             };
    19.             PlayerJoining(currentDevice);
    20.         };
    21.     }
    the Intvalue return 0 every time!
     
  6. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    @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.
     
  7. GilbertoBitt

    GilbertoBitt

    Joined:
    May 27, 2013
    Posts:
    111
    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
     
  8. GilbertoBitt

    GilbertoBitt

    Joined:
    May 27, 2013
    Posts:
    111
    @Rene-Damm i change my MB_InputSystem to this!

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using UnityEditor.Graphing;
    5. using UnityEngine;
    6. using UnityEngine.Experimental.Input;
    7. using UnityEngine.Experimental.Input.Interactions;
    8. using UnityEngine.Experimental.Input.Plugins.Users;
    9. using UnityEngine.Experimental.Input.Utilities;
    10.  
    11. public class MB_InputSystem : MonoBehaviour {
    12.     public SkyHunters_Inputs inputsMap;
    13.     // Start is called before the first frame update
    14.     private InputDevice _keyboardInput;
    15.     private InputDevice _mouseInput;
    16.     void Awake() {
    17.         _keyboardInput = Keyboard.current.device;
    18.         _mouseInput = Mouse.current.device;
    19.         inputsMap.SpaceShip.Join.performed += ctx => {
    20.             var currentDevice = ctx.control.device;
    21.             foreach (var device in InputUser.GetUnpairedInputDevices()) {
    22.                 Debug.Log(device);
    23.             }
    24.             if (!InputUser.GetUnpairedInputDevices().Contains(currentDevice)) return;
    25.             var input = new SkyHunters_Inputs();
    26.             input.SetAsset(ScriptableObject.Instantiate(inputsMap.asset));
    27.             var user = InputUser.PerformPairingWithDevice(currentDevice, options:InputUserPairingOptions.ForcePlatformUserAccountSelection);
    28.             if (currentDevice == _mouseInput) {
    29.                 user = InputUser.PerformPairingWithDevice(_keyboardInput, user, options:InputUserPairingOptions.None);
    30.             } else if (currentDevice == _keyboardInput) {
    31.                 user = InputUser.PerformPairingWithDevice(_mouseInput, user, options:InputUserPairingOptions.None);
    32.             }
    33.             user.AssociateActionsWithUser(input);
    34.             input.SpaceShip.Shoot.performed += context => ShootStarted();
    35.             input.Enable();
    36.         };
    37.      
    38.         foreach (var user in InputUser.all) {
    39.             user.actions.Enable();
    40.             SkyHunters_Inputs input = (SkyHunters_Inputs) user.actions;
    41.             input.SpaceShip.Shoot.performed += context => ShootStarted();
    42.          
    43.             user.actions.Enable();
    44.          
    45.             //First Enable All than Disable it.
    46.             input.SpaceShip.Join.Disable();
    47.         }
    48.     }
    49.  
    50.     public void ShootStarted() {
    51.         Debug.Log("Shooting");
    52.     }
    53.  
    54.     /*(bool, InputDevice, int) HasPlayerDevice(InputDevice currentDevice) {    
    55.         return devicesInput.Contains(currentDevice) ? (true, currentDevice, devicesInput.IndexOf(currentDevice)) : (false, currentDevice, -1);
    56.     }
    57.  
    58.     void PlayerShooting(InputDevice controlDevice) {
    59.         var index = devicesInput.IndexOf(controlDevice);
    60.         Debug.Log($"Player Shooting using {index}");
    61.     }
    62.  
    63.     void PlayerJoining(InputDevice currentDevice) {
    64.         Debug.Log("Player Just Join the Party!");
    65.         devicesInput.Add(currentDevice);
    66.         Debug.Log(currentDevice);
    67.     }*/
    68.  
    69.     private void OnEnable() {
    70.         inputsMap.SpaceShip.Join.Enable();
    71.         foreach (var user in InputUser.all) {
    72.             user.actions.Enable();
    73.         }
    74.     }
    75.  
    76.     private void OnDisable() {
    77.         inputsMap.SpaceShip.Join.Disable();
    78.         foreach (var user in InputUser.all) {
    79.             user.actions?.Disable();
    80.         }
    81.     }
    82. }
    83.  

    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.
     
  9. Awkin

    Awkin

    Joined:
    Aug 24, 2018
    Posts:
    3
    I'm having a hard time understanding how to implement this into my code, could you give me an example?
     
  10. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    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.
     
  11. gdzee

    gdzee

    Joined:
    Jul 9, 2018
    Posts:
    1
    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:

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

    Thanks in advance
     
  12. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    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).
     
  13. djvarnau

    djvarnau

    Joined:
    Jan 19, 2018
    Posts:
    5
    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.

    void Awake () {
    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.
     
  14. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    To use multiple PlayerInput independently easily, you should use the PlayerInputManager, it will take care of that for you
     
  15. djvarnau

    djvarnau

    Joined:
    Jan 19, 2018
    Posts:
    5
    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
     

    Attached Files:

  16. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    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.
     
  17. djvarnau

    djvarnau

    Joined:
    Jan 19, 2018
    Posts:
    5
    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?
     
  18. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    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.
     
  19. djvarnau

    djvarnau

    Joined:
    Jan 19, 2018
    Posts:
    5
    Ok, I'll try that out. Thanks for your help on this.
     
  20. javiercasadogames

    javiercasadogames

    Joined:
    Oct 23, 2019
    Posts:
    2
    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:

    Code (CSharp):
    1. using UnityEngine;
    2. public class Move : MonoBehaviour
    3. {
    4.     PlayerControl controls;
    5.     //direction horizontal
    6.     float inputHorizontal;
    7.  
    8.     private void Awake()
    9.     {
    10.         controls = new PlayerControl();
    11.         //inputs
    12.         controls.Gameplay.Address.performed += ctx => inputHorizontal = ctx.ReadValue<float>();
    13.         controls.Gameplay.Address.canceled += ctx => inputHorizontal = 0;
    14.     }
    15.  
    16.     private void Update()
    17.     {
    18.         //Move
    19.         if (inputHorizontal != 0)
    20.         {
    21.             transform.Translate(new Vector3(inputHorizontal * 10f, 0, 0)*Time.deltaTime);
    22.         }
    23.     }
    24.     private void OnEnable()
    25.     {
    26.         controls.Enable();
    27.     }
    28.     private void OnDisable()
    29.     {
    30.         controls.Disable();
    31.     }
    32. }
     
  21. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Recommend switching to PlayerInput. Setting up multiplayer using the C# code generated from .inputactions files currently requires several pieces of custom scripting that can be avoided by using PlayerInput's built-in support for this kind of setup.
     
  22. SNARLSBURG

    SNARLSBURG

    Joined:
    Nov 3, 2017
    Posts:
    1
    I have been working with the new input system 1.0.0. I have a player prefab created which has a Player Input component on it. The Player Input has an Input Actions attached to it. The Input Actions has a map created called "Player" and it has your basic actions on it. I then created a Player Input Manager and attached my player prefab to it. I then preceded to plug in two controllers and used the "Join Behavior: Join Players When Button Is Pressed". When one player joins they can navigate and it takes inputs from the controller. When another controller joins there is no input going to their player. The left stick of the first player is also manipulating the second players character. I was wondering if I am doing something wrong here? Is there any videos that explain this deeper?

    Thanks
     
    javiercasadogames likes this.
  23. javiercasadogames

    javiercasadogames

    Joined:
    Oct 23, 2019
    Posts:
    2
    Hi, I think you found a video that explains how to put the input manegaer for multiplayer
     
  24. luisanton

    luisanton

    Joined:
    Aug 25, 2009
    Posts:
    325
    But how to do it yourself? In that video - which is the only one I've found explaining something related to multiplayer - the PlayerInputManager spawns prefabs by itself. Your game may require something more complex, not just spawning a prefab.

    I guess we should use the Join Behavior 'Join Players Manually' option, but is there an example with that? (I manage to spawn players like in the video, at least) When I set that option, no script answers to the OnPlayerJoined message (not even the one that was answering when using the Join Players When Button Is Pressed)
     
    Last edited: Nov 11, 2019
  25. Just_Max_

    Just_Max_

    Joined:
    Nov 13, 2019
    Posts:
    4
    Hi,

    I have found that
    ApplyBindingOverridesOnMatchingControls
    doesn't do what I have expected from it.

    Code (CSharp):
    1.             public InputGroup(InputBinder inputBinder, Gamepad gamepad, CharacterController characterController, HammerController hammerController)
    2.             {
    3.                 Debug.Log($"Device Id: {gamepad.deviceId}");
    4.  
    5.                 this._inputBinder = inputBinder;
    6.                 this._characterController = characterController;
    7.                 this._hammerController = hammerController;
    8.  
    9.                 this._inputActionMove = this._inputBinder._inputActionMove.action.Clone();
    10.              
    11.                 // this._inputActionMove = new InputAction(
    12.                 //     this._inputActionMove.name,
    13.                 //     this._inputActionMove.type,
    14.                 //     this._inputActionMove.bindings.ToArray()[0].path,
    15.                 //     this._inputActionMove.processors,
    16.                 //     this._inputActionMove.expectedControlType
    17.                 // );
    18.  
    19.                 this._inputActionRightStick = this._inputBinder._inputActionRightStick.action.Clone();
    20.                 this._inputActionR2 = this._inputBinder._inputActionR2.action.Clone();
    21.  
    22.                 // if (lelSmellCode == 0)
    23.                 // {
    24.                 Debug.Log(this._characterController.transform.parent.gameObject.name);
    25.  
    26.                 this._inputActionMove.ApplyBindingOverridesOnMatchingControls(gamepad);
    27.                 this._inputActionRightStick.ApplyBindingOverridesOnMatchingControls(gamepad);
    28.                 this._inputActionR2.ApplyBindingOverridesOnMatchingControls(gamepad);
    29.  
    30.                 // lelSmellCode++;
    31.                 // }
    32.  
    33.                 for (int a = 0; a < this._inputActionMove.controls.Count; a++)
    34.                 {
    35.                     Debug.Log($"InputActionMove Id: {this._inputActionMove.id} Control index: {a} Control: {this._inputActionMove.controls[a]} Control Device Id: {this._inputActionMove.controls[a].device.deviceId}");
    36.                 }
    37.  
    38.                 Debug.Log($"InputActionRightStick Id: {this._inputActionRightStick.id}");
    39.                 Debug.Log($"InputActionR2 Id: {this._inputActionR2.id}");
    40.  
    41.             }
    upload_2019-11-13_18-14-43.png
    As you can see on the screenshot, even though I am passing the method a device with Id: 2, it still assigns a controlling device with Id: 1. Thus I have one device controlling both characters.

    I will have a look at how PlayerInput is implemented, but this one seems like a bug. And I cannot get local multiplayer to work this way.

    Thanks,
    Max
     
    Last edited: Nov 13, 2019
    PixelLifetime likes this.
  26. Just_Max_

    Just_Max_

    Joined:
    Nov 13, 2019
    Posts:
    4
    @Rene-Damm sorry to bother you, but is my question above a bug and should I file a bug or is it expected behaviour?
     
  27. PixelLifetime

    PixelLifetime

    Joined:
    Mar 30, 2017
    Posts:
    90
    @Griz hi, I have seen your answer in a different thread and I presume that you have a knowledge of how new input system works in more detail than me. This question above about
    Code (csharp):
    1. .ApplyBindingOverridesOnMatchingControls(gamepad);
    - it's intended in this case to use the same action map but for multiple devices. I couldn't get this to work. I am using `InputActionReference` for this. I don't want to use `PlayerInput` because it's limited, enforces design, and has some overhead. Is there a way having `InputActionReference`s referenced in inspector, them using them as a base for input for some device. I am looping through `InputSystem.devices` and trying to assign a clones of action a different device but with no success.
     
  28. DogmeatKeush

    DogmeatKeush

    Joined:
    Oct 15, 2016
    Posts:
    2
    I am also having issues with Player Input component. I filed an issue / bug in the github repo here
    2 xbox one controllers are recognized in windows 10 as xinput and both are assigned to the same player, I can see that in the debug.

    I find it puzzling that the Tanks Demo does not have this issue. The gamepads are assigned correctly one to each user.

    I understand that the Player Input Manager is not always required, due that the Tanks Demo does not have it and the players get the proper gamepad assigned.

    Any ideas on what may be missing? wrong?
     
  29. anselm_k

    anselm_k

    Joined:
    Nov 24, 2016
    Posts:
    9
    Is there a way to get all the input events and device information for multiple players? Using PlayerInput with Unity's messages only provides very limited information which I now need to work around a whole lot (including additional actions, duplicated code and manual state handling (touch start/cancel) because I can't acceess the device from the messages).
     
    Rockyeagle likes this.