Search Unity

Input System - local multiplayer

Discussion in 'Input System' started by RisingMoonGames, Oct 19, 2019.

  1. RisingMoonGames

    RisingMoonGames

    Joined:
    Apr 11, 2017
    Posts:
    1
    I am creating a game that has the option of local multiplayer, I've done it before, but using unity's standard InputManager, but had several device recognition issues, sometimes forcing the player to restart the game.

    So I want to use this new system that looks a lot better, but I'm facing some implementation problems. I will already configure the basic commands of the player, such as shoot, move ... but when trying to start configuring the local cooperative mode I came across some problems.

    A player may have multiple devices connected, I want to give him the option to choose which device will control his ship in the game and if that device is disconnected or he decides to use the keyboard during multiplayer, the game recognizes the change and changes the device. paired with that particular player.

    I know this is possible in this new system, but I have no idea how to get devices connected and pair with a player via code, or how to detect if the player pressed a key from a device not paired with a player and pair. How do I do that?

    - Get a list of connected devices
    - Pick a device and pair with a particular player when he appears in the scene.
    - Detect if an unpaired device is being used and pair with the player.

    Already searched in forums, websites and youtube, found nothing.

    Note: my english is not very good, sorry if that got confusing.
     
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Built-in join behaviors are provided by the PlayerInputManager component. This does not cover all possible scenarios but depending on your use case may work for you. A short demonstration can be found in the Unite Copenhagen talk. It will handle joins such that players are automatically paired to devices they have joined from.

    For going from scratch, here's a couple API pointers.
    • PlayerInput.Instantiate for instantiating players with specific devices paired to them
    • InputUser.PerformPairingWithDevice for manually performing pairing
    • InputSystem.devices to get all connected devices
    • InputUser.GetUnpairedDevices() to get a list of devices not currently paired to any user
    • InputUser.onUnpairedDeviceUsed for listening to activity on unpaired devices
    Code (CSharp):
    1. // Listening must be enabled explicitly.
    2. ++InputUser.listenForUnpairedDeviceActivity;
    3.  
    4. // Example of how to spawn a new player automatically when a button
    5. // is pressed on an unpaired device.
    6. InputUser.onUnpairedDeviceUsed +=
    7.     (control, eventPtr) =>
    8.     {
    9.         // Ignore anything but button presses.
    10.         if (!(control is ButtonControl))
    11.             return;
    12.  
    13.         // Spawn player and pair device. If the player's actions have control schemes
    14.         // defined in them, PlayerInput will look for a compatible scheme automatically.
    15.         PlayerInput.Instantiate(playerPrefab, device: control.device);
    16.     };
    If you roll your own joining logic and you have control schemes, make sure to set neverAutoSwitchControlSchemes to true on the players in order to prevent the first player in the game from switching away from paired devices on input.
     
  3. jacasch

    jacasch

    Joined:
    Jan 20, 2017
    Posts:
    16
    What is the InputUser? I can't find it UnityEngine.Inputsystem, or when trying to add a component.

    nvmd found it under
    UnityEngine.InputSystem.Users
     
  4. Koru

    Koru

    Joined:
    Feb 14, 2012
    Posts:
    42
    Any examples of how to create a local multiplayer without PlayerInput? I want to use an existing game object without instantiating prefabs.
     
  5. Koru

    Koru

    Joined:
    Feb 14, 2012
    Posts:
    42
    I will answer my own question :). I had to pair a device with a user and then assign input actions. Below I paste minimal example for assigning a single gamepad to the user.
    Code (CSharp):
    1.  
    2.         _actions = new GameplayInputActions();
    3.         _user = InputUser.PerformPairingWithDevice(Gamepad.all[_gamepadNumber]);
    4.         _user.AssociateActionsWithUser(_actions);
    5.         _user.ActivateControlScheme(_schemeName);
     
  6. scream870102

    scream870102

    Joined:
    Jan 31, 2018
    Posts:
    1
    Can you paste the whole code?Or github link is also okay thank you
     
  7. deeprest

    deeprest

    Joined:
    Jun 8, 2016
    Posts:
    17
    I followed Koru's lead and managed to have local multiplayer without using PlayerInput or PlayerInputManager.
    Here is the relevant code for my mutliplayer Tetris clone.
    To see the code in context (in a working project) check out the source.
    Keep in mind that the Tetris class here basically is the player.

    Code (CSharp):
    1. using UnityEngine.InputSystem;
    2. using UnityEngine.InputSystem.Controls;
    3. using UnityEngine.InputSystem.LowLevel;
    4. using UnityEngine.InputSystem.Users;
    5.  
    6. public class Global : MonoBehaviour
    7. {
    8.   public GameObject Tetrisprefab;
    9.   private bool executingGameplay = false;
    10.   [SerializeField] GameObject GameOverScreen;
    11.   [SerializeField] GameObject JoinScreen;
    12.   [SerializeField] GameObject ReadyScreen;
    13.   [SerializeField] Camera camera;
    14.   Controls Controls;
    15.  
    16.   [SerializeField] int MaxPlayers = 3;
    17.   private List<Tetris> players;
    18.   private List<Tetris> newplayers;
    19.  
    20.   void Awake()
    21.   {
    22.     ReadyScreen.SetActive( false );
    23.     GameOverScreen.SetActive( false );
    24.     JoinScreen.SetActive( true );
    25.     players = new List<Tetris>();
    26.     newplayers = new List<Tetris>();
    27.     // my IInputActionCollection
    28.     Controls = new Controls();
    29.     // you must enable
    30.     Controls.Enable();
    31.     // bind controls that any/every device can use
    32.     Controls.Global.StartGame.performed += context => StartGame();
    33.     // assign the callback for listening
    34.     InputUser.onUnpairedDeviceUsed += OnUnpairedDeviceUsed;
    35.     // listening will not start until InputUser.listenForUnpairedDeviceActivity > 0
    36.     BeginJoining();
    37.   }
    38.  
    39.   private void OnDestroy()
    40.   {
    41.     // Avoid lingering calls after play mode has ended!!!
    42.     InputUser.onUnpairedDeviceUsed -= OnUnpairedDeviceUsed;
    43.   }
    44.  
    45.   void OnUnpairedDeviceUsed( InputControl control, InputEventPtr eventPtr )
    46.   {
    47.     // Ignore anything but button presses.
    48.     if( !( control is ButtonControl ) )
    49.       return;
    50.     Debug.Log( "Unpaired device detected" + control.device.displayName );
    51.  
    52.     // get a new InputUser, now paired with the device
    53.     InputUser user = InputUser.PerformPairingWithDevice( control.device );
    54.     // Create a new instance of input actions to prevent InputUser from triggering actions on another InputUser.
    55.     Controls controlsForThisUser = new Controls();
    56.     // you must enable the controls to use them
    57.     controlsForThisUser.Enable();
    58.     // the real work is done for us in InputUser
    59.     user.AssociateActionsWithUser( controlsForThisUser );
    60.  
    61.     GameObject go = Instantiate( Tetrisprefab );
    62.     Tetris tetris = go.GetComponent<Tetris>();
    63.     // store the InputUser so you can unpair later
    64.     tetris.user = user;
    65.     // initialize your script with the new controls
    66.     tetris.BindControls( controlsForThisUser.Gameplay );
    67.  
    68.     // You cannot do things like set the parent transform in this callback, so
    69.     // add the game object to a list of new players for processing in Update()
    70.     newplayers.Add( tetris );
    71.  
    72.     InputUser.listenForUnpairedDeviceActivity--;
    73.     if( InputUser.listenForUnpairedDeviceActivity == 0 )
    74.     {
    75.       EndJoining();
    76.       ReadyScreen.SetActive( true );
    77.     }
    78.   }
    79.  
    80.   void BeginJoining()
    81.   {
    82.     InputUser.listenForUnpairedDeviceActivity = MaxPlayers;
    83.     JoinScreen.SetActive( true );
    84.   }
    85.  
    86.   void EndJoining()
    87.   {
    88.     InputUser.listenForUnpairedDeviceActivity = 0;
    89.     JoinScreen.SetActive( false );
    90.   }
    91.  
    92.   void GameOver( Tetris loser )
    93.   {
    94.     executingGameplay = false;
    95.     GameOverScreen.SetActive( true );
    96.     Controls.Disable();
    97.     // Timer class not shown here, see the full source
    98.     new Timer( this, 3, null, () =>
    99.     {
    100.       Controls.Enable();
    101.       GameOverScreen.SetActive( false );
    102.       foreach( var player in players )
    103.         Destroy( player.gameObject );
    104.       players.Clear();
    105.       BeginJoining();
    106.     } );
    107.   }
    108.  
    109.   void StartGame()
    110.   {
    111.     if( executingGameplay )
    112.       return;
    113.     EndJoining();
    114.     executingGameplay = true;
    115.     GameOverScreen.SetActive( false );
    116.     ReadyScreen.SetActive( false );
    117.     foreach( var game in players )
    118.       game.Reset();
    119.   }
    120.  
    121.   void Update()
    122.   {
    123.     // add new players
    124.     foreach( var player in newplayers )
    125.     {
    126.       // this is the reason for the newplayers list. This cannot be called in
    127.       // the onpairedDeviceUsed callback above
    128.       player.transform.parent = transform;
    129.       players.Add( player );
    130.     }
    131.     newplayers.Clear();
    132.  
    133.     if( executingGameplay )
    134.       for( int i = 0; i < players.Count; i++ )
    135.         players[i].ManualUpdate();
    136.   }
    137. }
    138.  
    Code (CSharp):
    1. using UnityEngine.InputSystem;
    2. using UnityEngine.InputSystem.Users;
    3.  
    4. // NOT using the callback interface because I couldn't figure out to bind it properly
    5. public class Tetris : MonoBehaviour //, Controls.IGameplayActions
    6. {
    7.   public InputUser user;
    8.   public Controls.GameplayActions gameplayActions;
    9.   // Controls the board with player input or AI using this struct
    10.   public struct InputStruct
    11.   {
    12.     public bool right;
    13.     public bool left;
    14.     public bool down;
    15.     public bool rotCW;
    16.     public bool rotCCW;
    17.   }
    18.   InputStruct inputState;
    19.  
    20.   private void OnDestroy()
    21.   {
    22.     user.UnpairDevices();
    23.   }
    24.  
    25.   public void BindControls( Controls.GameplayActions iam )
    26.   {
    27.     gameplayActions = iam;
    28.     // Bind discrete events using C# delegates
    29.     iam.RotateCW.started += ( obj ) => { inputState.rotCW = true; };
    30.     iam.RotateCCW.started += ( obj ) => { inputState.rotCCW = true; };
    31.   }
    32.  
    33.   public void ManualUpdate()
    34.   {
    35.     // Use polling for continuous player actions if appropriate
    36.     inputState.down = gameplayActions.Down.phase == InputActionPhase.Started;
    37.     inputState.left = gameplayActions.Left.phase == InputActionPhase.Started;
    38.     inputState.right = gameplayActions.Right.phase == InputActionPhase.Started;
    39.     // HandleInput( inputState )
    40.   }
    41.  
    42.   // NOT using the SendMessage() technique
    43.   //public void OnRight( Context ){}
    44. }
    45.  
     
  8. DerekSturm263

    DerekSturm263

    Joined:
    Sep 23, 2019
    Posts:
    4
    Thanks Koru! This helped so much!