Local Multiplayer w/support for multiple players on a keyboard

So I’ll outline my scenario for background.

Making a 2D, local multiplayer game. I want to have multiple people on the same keyboard, along with the ability for people to hop in on a controller, up to 4 players total.

I’ll preface this with that I do have this working; however, I’m not happy with this solution as I’m essentially checking for specific inputs in Update, which just feels dirty.

My approach is having the PlayerInputManager component on a ConfigurationManager object, with Prefabs that have PlayerInput components with a different default scheme (either Keyboard 1 aka WASD, Keyboard 2 aka Arrows, or Gamepad). PlayerInputManager join behavior is set to “Join Players Manually” since “Join Players When a Button is Pressed” will join a player when any button is pressed and map it to that input device. So my solution was to call PlayerInput.Instantiate when specific buttons are pressed, hook up the control scheme in that call, and pair with that device if a custom class I created doesn’t have a keyboard1/keyboard2 slot taken up.

This just feels inefficient and requires checking for specific inputs in Update, knowing that’s the input button you want to hook up for a specific control scheme, and mapping it based on those inputs. Seems like it defeats the purpose of having control schemes. I’ll post the code for my ConfigurationManager; is there an easier solution? Any ideas would be appreciated.

7466078–916883–PlayerConfigurationManager.cs (7.69 KB)

My thought would be something along these lines.

// Data from component.
GameObject m_PlayerPrefab;

// For any button press, see if we can spin up a player for it.
var listener = InputSystem.onAnyButtonPress
    .Call(ctrl =>
    {
        var device = ctrl.device;
     
        // Except for the keyboard, do nothing if the device has already been grabbed
        // by a player.
        if (!(device is Keyboard) && PlayerInput.FindFirstPairedToDevice(device) != null)
            return;
     
        // Grab the actions from the player prefab and look for a binding that fits
        // the control that was pressed.
        var playerInput = m_PlayerPrefab.GetComponentInChildren<PlayerInput>();
        foreach (var map in playerInput.actions.actionMaps)
        {
            foreach (var binding in map.bindings)
            {
                if (InputControlPath.TryFindChild(device, binding.effectivePath) != ctrl)
                    continue;
             
                // Find the control scheme for the binding.
                var controlScheme =
                    playerInput.actions.controlSchemes.FirstOrDefault(c => InputBinding.MaskByGroup(c.bindingGroup).Matches(binding));
                if (controlScheme == default)
                    continue;
             
                // For the keyboard, only allow a single player on a given
                // keyboard control scheme.
                if (device is Keyboard && PlayerInput.all.Any(p => p.currentControlScheme == controlScheme.name))
                    continue;
             
                // Spawn a player.
                PlayerInput.Instantiate(m_PlayerPrefab, controlScheme: controlScheme.name, pairWithDevice: device);
            }
        }
    });

// When no longer needed, get rid of the listener.
listener.Dispose();