Feature suggestion: Constraining monitored InputDevices in generated C# class file via constructor

I’ve been scrutinizing the Input System code for a while trying to figure out how I might set up local co-op, and I’ve noticed a kind of mutual exclusivity between the controller filtering of the PlayerInput class and the well-laid-out interface of generating a C# class from an action asset (quick note, I’m interested primarily in using C# events, as my understanding is that they’re the most performant of the four methods available in PlayerInput). The former only contains a single event that’s used whenever any action at all fires, while the latter handles input from all controllers that have a matching binding in the asset, regardless of which specific controller it came from.

With that in mind, I’m wondering if it might be possible in future versions to have generated C# class files allow for optional parameters (e.g. InputDevice or their device ID(s)) in their constructors that would constrain the devices they react to, such as follows:

public class InputReference : IDisposable
{
     // this would be the generated C# class file

     // default constructor
     public InputReference()
     {
     }

     // device ID-constrained constructor (could also be of type InputDevice)
     public InputReference(params int[] deviceIDs)
     {
     }

     // rest of the class...
}

public class Foo : MonoBehaviour
{
     private InputReference _universalInput;
     private InputReference _p1Input;
     private InputReference _p2Input;

     private void Awake()
     {
          // assume these are already initialized from somewhere else
          int p1DeviceID;
          int p2DeviceID1;
          int p2DeviceID2;

          _universalInput = new InputReference(); // this would be unconstrained and receive input from all devices
          _p1Input = new InputReference(p1DeviceID) // this would only pass on input from Player 1's device
          _p2Input = new InputReference(p2DeviceID1, p2DeviceID2) // this would only pass on input from either of Player 2's devices
     }
}

Something like this would allow us to cleanly define inputs on a per-player’s-devices basis without needing to either make multiple action assets or have to constantly check if an action’s context control device is the one for a given player’s. Seems like the best of both worlds to me, to be able to easily go “Okay, this will fire on a jump from Player 1” without needing to do boilerplate condition checking in each callback we hook in.

What do you think?

The whole reason having the InputSystem is to abstract away the devices… Why would anyone bring them back and not use the other existing methods to read a specific device if it’s needed?

There are other ways? (Legitly asking; I might have simply been looking in the wrong spot.)

Okay, what’s wrong with the PlayerInputManager + PlayerInput combo? Yeah, I understand, it is not that convenient as the generated class, but it works and more suitable for N players.

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerFire : MonoBehaviour
{
    private const string FIRE_ACTION = "Fire";
   
    [SerializeField] private PlayerInput playerInput;

    private int _thisPlayer = 0;
    private static int _playerNo = 1;

    private void Awake()
    {
        playerInput.actions.FindAction(FIRE_ACTION).performed += Fire;
        _thisPlayer = _playerNo++;
    }

    private void Fire(InputAction.CallbackContext obj) => Debug.Log(_thisPlayer + " Fired!");
}

Even in the case that you dont want to or can’t use the PlayerInputManager/PlayerInput pattern (it does have drawbacks), you can still use InputUser to bind specific devices and act as device<–>event filter; this is how PIM and PI do it under the hood too.