Search Unity

2 Players on same input device

Discussion in 'Input System' started by itsjusteza, Oct 20, 2019.

  1. itsjusteza

    itsjusteza

    Joined:
    Oct 8, 2019
    Posts:
    1
    Hi All,

    Wondering if there is a way to get 2 players on the same input device. For example, having local multiplayer on a simple game where one player uses WASD, and the other uses arrow keys.

    I have gotten local multiplayer working with 2 different devices just fine (Keyboard + Xbox controller). However, when I just have 2 control schemes "Keyboard" and "Keyboard Alt", the second user is not registered as having a control scheme or paired device in the input debugger.

    I am using the PlayerInput component to handle this.

    Any ideas?

    Cheers
     
    robin_pcity and chjm655 like this.
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    There's no support for this getting set up automatically ATM. You'll have to manually call PlayerInput.Instantiate or alter device pairing and control scheme assignments after the players have been created.

    Code (CSharp):
    1. // Instantiate two players using a split-keyboard setup.
    2. var p1 = PlayerInput.Instantiate(playerPrefab,
    3.     controlScheme: "KeyboardLeft",
    4.     device: Keyboard.current);
    5. var p2 = PlayerInput.Instantiate(playerPrefab,
    6.     controlScheme: "KeyboardRight",
    7.     device: Keyboard.current);
     
  3. chjm655

    chjm655

    Joined:
    Jan 10, 2018
    Posts:
    2
    How would one go about altering the device pairing if the PlayerInput class is already instantiated? I can't seem to find any method or publicly accessible variable to switch the device.
     
  4. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    The device pairing for a player is captured by its associated InputUser (the lower-level API that PlayerInput is mostly built on top). You can get the associated user from the PlayerInput.user property. Then you can use the various methods in InputUser to control pairing (e.g. InputUser.PerformPairingWithDevice). There's currently no wrapper methods directly on PlayerInput to affect pairing that way.
     
  5. Extrawurst

    Extrawurst

    Joined:
    May 22, 2013
    Posts:
    43
    I was struggling with the same thing: I want both players to fall back on a KeyboardLeft/KeyboardRight scheme when no gamepads are present. If one or two gamepads are present I want them to use those. Both parts are not supported out of the box by the unity solution.

    I solved it with this:
    Code (CSharp):
    1.  
    2. public PlayerInput Input;
    3.  
    4. void Start()
    5. {
    6.     if(Input.devices.Count == 0)
    7.         Input.SwitchCurrentControlScheme(Input.defaultControlScheme,Keyboard.current);
    8.  
    9.     var unpaired = InputUser.GetUnpairedInputDevices();
    10.     var ipa = new InputPlayerActions();
    11.    
    12.     foreach(var d in unpaired)
    13.     {
    14.         var scheme = InputControlScheme.FindControlSchemeForDevice(d,ipa.controlSchemes);
    15.  
    16.         Debug.Log($"{gameObject.name}: unpaired -> {d.name} ({d.layout}) ({scheme?.name})");
    17.  
    18.         if(scheme?.name == "Gamepad")
    19.         {
    20.             Input.SwitchCurrentControlScheme("Gamepad",d);
    21.             break;
    22.         }
    23.     }
    24.  
    25.     Debug.Log($"{gameObject.name}: {Input.currentControlScheme}");
    26. }
    27.  
    At first it will fallback on the default schema defined in the component on keyboard (left/right). Then it will check if unpaired gamepads are around and assign.

    @Rene-Damm I wondering why such simple straight forward use cases are so hard to do. For example the hoops that I need to jump to find out if a device is a gamepad... is there a better option but to compare the supported scheme names?
     
  6. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Why not just something like this...

    Code (CSharp):
    1. void Start()
    2. {
    3.     var gamepadCount = Gamepad.all.Count;
    4.     if (gamepadCount >= 2)
    5.     {
    6.         PlayerInput.Instantiate(playerPrefab, controlScheme: "Gamepad", device: Gamepad.all[0]);
    7.         PlayerInput.Instantiate(playerPrefab, controlScheme: "Gamepad", device: Gamepad.all[1]);
    8.     }
    9.     else if (gamepadCount == 1)
    10.     {
    11.         PlayerInput.Instantiate(playerPrefab, controlScheme: "Gamepad", device: Gamepad.all[0]);
    12.         PlayerInput.Instantiate(playerPrefab, controlScheme: "KeyboardLeftAndRight", device: Keyboard.current);
    13.     }
    14.     else
    15.     {
    16.         PlayerInput.Instantiate(playerPrefab, controlScheme: "KeyboardLeft", device: Keyboard.current);
    17.         PlayerInput.Instantiate(playerPrefab, controlScheme: "KeyboardRight", device: Keyboard.current);
    18.     }
    19. }
    Is this going in the direction you have in mind?

    No doubt PlayerInputManager's lobby functionality can be much improved. It is important to keep in mind, though, that PlayerInputManager is really just meant as a "hey, here's a simple lobby implementation that *may* do the trick for you" kind of deal. PlayerInput with its Instantiate method is the real core of this and the only thing meant to accommodate all kinds of lobby style behaviors.
     
  7. Extrawurst

    Extrawurst

    Joined:
    May 22, 2013
    Posts:
    43
    @Rene-Damm that goes in the right direction, the biggest issue is that you cannot expect all use cases to instantiate the prefab at runtime like this. is it possible to do it the simple way you did but with pre existing PlayerInput instances like my approach did?
     
  8. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    If you're dealing with existing PlayerInputs loaded as part of the scene, I'd still probably go with the same approach. We don't yet have a way to entirely disable PlayerInputs automatic device pairing (I'm thinking we should probably add one), but I'd probably go and just ignore it entirely. Something like..

    Code (CSharp):
    1. void Start()
    2. {
    3.     var player1 = PlayerInput.all[0];
    4.     var player2 = PlayerInput.all[1];
    5.  
    6.     // Discard existing assignments.
    7.     player1.user.UnpairDevices();
    8.     player2.user.UnpairDevices();
    9.  
    10.     // Assign devices and control schemes.
    11.     var gamepadCount = Gamepad.all.Count;
    12.     if (gamepadCount >= 2)
    13.     {
    14.         InputUser.PerformPairingWithDevice(Gamepad.all[0], user: player1.user);
    15.         InputUser.PerformPairingWithDevice(Gamepad.all[1], user: player2.user);
    16.  
    17.         player1.user.ActivateControlScheme("Gamepad");
    18.         player2.user.ActivateControlScheme("Gamepad");
    19.     }
    20.     else if (gamepadCount == 1)
    21.     {
    22.         InputUser.PerformPairingWithDevice(Gamepad.all[0], user: player1.user);
    23.         InputUser.PerformPairingWithDevice(Keyboard.current, user: player2.user);
    24.  
    25.         player1.user.ActivateControlScheme("Gamepad");
    26.         player2.user.ActivateControlScheme("KeyboardLeftAndRight");
    27.     }
    28.     else
    29.     {
    30.         InputUser.PerformPairingWithDevice(Keyboard.current, user: player1.user);
    31.         InputUser.PerformPairingWithDevice(Keyboard.current, user: player2.user);
    32.  
    33.         player1.user.ActivateControlScheme("KeyboardLeft");
    34.         player2.user.ActivateControlScheme("KeyboardRight");
    35.     }
    36. }
    Probably would go from there and add something to give users control over which player ends up with what (also if there's more than two gamepads) but sort of something along those lines.

    Making more sense?
     
  9. Extrawurst

    Extrawurst

    Joined:
    May 22, 2013
    Posts:
    43
    Yes makes sense! Thank you very much
     
  10. ZexScal4

    ZexScal4

    Joined:
    May 25, 2018
    Posts:
    8
    Is there any way to write these lines of code for an entity instead of a GameObject?

    I've been trying to find a way to apply the PlayerInput Component and keep it after the object has been converted in game. But it seems that once the player object gets converted the PlayerInput loses track of the player since its no longer a gameObject.
    Is there a way to stop that from happening?

    More or less I wanted to know if I can get the PlayerInput to recognize the player entites I have as player in its counter.
     
    davidnibi likes this.