Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

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

Discussion in 'Input System' started by VengefulWhitetail, Oct 31, 2021.

  1. VengefulWhitetail


    Mar 12, 2020
    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:

    Code (CSharp):
    1. public class InputReference : IDisposable
    2. {
    3.      // this would be the generated C# class file
    5.      // default constructor
    6.      public InputReference()
    7.      {
    8.      }
    10.      // device ID-constrained constructor (could also be of type InputDevice)
    11.      public InputReference(params int[] deviceIDs)
    12.      {
    13.      }
    15.      // rest of the class...
    16. }
    18. public class Foo : MonoBehaviour
    19. {
    20.      private InputReference _universalInput;
    21.      private InputReference _p1Input;
    22.      private InputReference _p2Input;
    24.      private void Awake()
    25.      {
    26.           // assume these are already initialized from somewhere else
    27.           int p1DeviceID;
    28.           int p2DeviceID1;
    29.           int p2DeviceID2;
    31.           _universalInput = new InputReference(); // this would be unconstrained and receive input from all devices
    32.           _p1Input = new InputReference(p1DeviceID) // this would only pass on input from Player 1's device
    33.           _p2Input = new InputReference(p2DeviceID1, p2DeviceID2) // this would only pass on input from either of Player 2's devices
    34.      }
    35. }
    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?
  2. Lurking-Ninja


    Jan 20, 2015
    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?
  3. VengefulWhitetail


    Mar 12, 2020
    There are other ways? (Legitly asking; I might have simply been looking in the wrong spot.)
  4. Lurking-Ninja


    Jan 20, 2015
    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.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    4. public class PlayerFire : MonoBehaviour
    5. {
    6.     private const string FIRE_ACTION = "Fire";
    8.     [SerializeField] private PlayerInput playerInput;
    10.     private int _thisPlayer = 0;
    11.     private static int _playerNo = 1;
    13.     private void Awake()
    14.     {
    15.         playerInput.actions.FindAction(FIRE_ACTION).performed += Fire;
    16.         _thisPlayer = _playerNo++;
    17.     }
    19.     private void Fire(InputAction.CallbackContext obj) => Debug.Log(_thisPlayer + " Fired!");
    20. }
  5. Fenrisul


    Jan 2, 2010
    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.