Search Unity

Question Preventing a new player from joining manually for a reconnecting device

Discussion in 'Input System' started by pdinklag, May 8, 2020.

  1. pdinklag

    pdinklag

    Joined:
    Jan 24, 2017
    Posts:
    154
    I'm planning to port my project to the new input system (version 1.0.0 in Unity 2019.3.13). What I definitely need to do is join players manually.

    I was hoping that I could hook into the onDeviceChange and handle Added events to spawn PlayerInputs (via PlayerInputManager.JoinPlayer) and pair them with the new devices. Generally speaking, this works. However, I also get Added events for reconnecting gamepads and I don't know how to prevent joining another player for the same physical device. The deviceId changes, so it can't be to used it for device identificiation as its name would suggest. Let's go through my scenario step by step to explain the problem:
    1. Start the game with gamepad turned off.
    2. Turn on the gamepad. This gives me an InputDeviceChange.Added event for the gamepad with deviceId let's say 12. A player is joined.
    3. Turn off the gamepad. I do this by opening the battery bay of the Xbox controller to simulate the battery running out. This gives a Removed event first, and then a Disconnected event.
    4. Turn the gamepad back on. This gives me an Added event so I join a new player, and then (afterwards) a Reconnected event. It is correctly mapped to the first PlayerInput I paired with this gamepad. However, the deviceId is now 13, giving me no chance to intercept joining a new player in the handling of the previous Added event.
    When handling an Added event, how, if not by the device ID, would I be able to tell that this device was already connected earlier and I don't need to spawn a new PlayerInput for it? I presume there must be a way, because the PlayerInputManager handles this case just well if I set joining to "when button is pressed", which I don't want to use for my project, however.
     
  2. pdinklag

    pdinklag

    Joined:
    Jan 24, 2017
    Posts:
    154
    Well, I got this rather ugly solution now:
    Code (CSharp):
    1. private void OnDeviceChange(InputDevice changedDevice, InputDeviceChange change)
    2. {
    3.     Debug.Log("OnDeviceChange: device=" + changedDevice + ", deviceId=" + changedDevice.deviceId + ", change =" + change);
    4.  
    5.     if (change == InputDeviceChange.Added)
    6.     {
    7.         var reconnecting = false;
    8.  
    9.         // check current players if this device was automatically mapped
    10.         foreach (var current in PlayerInput.all)
    11.         {
    12.             foreach (var dev in current.devices)
    13.             {
    14.                 if (dev == changedDevice)
    15.                 {
    16.                     // player is reconnecting, don't join a new one
    17.                     reconnecting = true;
    18.                     break;
    19.                 }
    20.             }
    21.  
    22.             if (reconnecting) break;
    23.         }
    24.  
    25.         if (!reconnecting)
    26.         {
    27.             _manager.JoinPlayer(pairWithDevice: changedDevice);
    28.         }
    29.     }
    30. }
    It works, because when the Added event is invoked, reconnecting devices are already mapped back to their original PlayerInputs, albeit with a new ID.

    So yeah, case in point, the Input System engine knows that the device is reconnecting - is there any way I can find this out via the API? As mentioned in the original post, the Reconnected event is invoked after the Added event, so it doesn't help me.

    EDIT:
    It doesn't seem to work always, so apparently, there's some asynchronous stuff going on. I can handle that by doing the above in a delayed coroutine, but that doesn't make it any nicer. :(