Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

I wand to read, in an Update loop, which device did which action (local multiplayer)

Discussion in 'Input System' started by PhilSA, Oct 28, 2019.

  1. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Here's my use case:
    I want to make an input manager for a local multiplayer game in DOTS. The goal is that for each local multiplayer player, we'll have a "Player" entity with a "PlayerInputs" component on it containing all of the input info (JumpWasPressed, MoveVector, CrouchIsHeld, etc....) but only for the input devices that were assigned to that specific player

    Here's my problem:
    If I try to query the inputs on Update, for example with MyInputActions.MyActions.Jump.ReadValue<float>(), I can't get which device id it's coming from, so this doesn't work for local multiplayer.

    If I work with the input callbacks from the generated InputActions interface, I end up with an insanely boilerplatey setup to achieve what I want. Every time I add a new input to the game, I have to write like 20 lines of code just to plug it in the right places. Take a look at what we did for player inputs in DOTS of the Dead, you'll see what I mean: https://github.com/illogika-studio/..._Project/Scripts/Systems/PlayerInputSystem.cs

    Here's what I'd want:
    I can see two ways this could work:

    1- Make the input events be queriable per-device. So instead of calling MyInputActions.MyActions.Jump.ReadValue<float>(), I would be calling MyInputActions.devices[0].MyActions.Jump.ReadValue<float>()

    2- On Update(), I'd like to be able to call MyInputActions.MyActions.Jump.GetAllEvents(), which returns me a list of all the "InputAction.CallbackContext" for the Jump input that happened this frame. Basically the same thing the interface approach does, but in the form of a list. I think that would be the ideal solution to my problem.

    I actually don't know if these things are already possible so I'm hoping someone can tell me
     
    Deleted User likes this.
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Code (CSharp):
    1. var device = MyInputActions.MyActions.Jump.activeControl.device;
    InputActionTrace may be of use to you. If your aim is to merely record actions for processing elsewhere, it can help do that with little code.
     
  3. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    In my situation I don't think that works, because if devices 1 and 2 both pressed jump this frame, I won't know that they both did it

    Oh that looks very promising! I'll look into it, thanks
     
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    @Rene-Damm how would you recommend passing InputActionTrace's data to a job? Or just handling input in DOTS in general?

    Also, I'm starting to see a problem with the InputActionTrace approach: in my dots input component, I want to store a "JumpReleased" bool value, which I update on every action trace. The problem is that I don't get any action trace when no input is happening, so I never get an opportunity to set "JumpReleased" back to false the frame after the button was released. The last trace we always get from the Jump action is it being released

    With all this, I still feel like being able to poll actions on Update per device would be ideal and would solve all my problems
     
    Last edited: Oct 29, 2019
  5. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Shooting from the hip, something like this: (just sketched out; haven't even tried to compile, let alone run)

    Code (CSharp):
    1.  
    2. // Somewhere else (e.g. lobby screen), handle player setup.
    3. void JoinPlayer(InputDevice device)
    4. {
    5.     // Sets up a new user and pairs the given device.
    6.     // InputUser.all is all current players.
    7.     var user = InputUser.PerformPairingWithDevice(device);
    8.  
    9.     // Give custom actions to user.
    10.     // Use generated C# class here.
    11.     var actions = new MyGameActions();
    12.     user.AssociateActionsWithUser(actions);
    13.  
    14.     // Optionally, pick control scheme. If several are supported, probably
    15.     // want to find a suitable one for the given device.
    16.     var controlScheme = InputControlScheme.FindControlSchemeForDevice(device, actions.controlSchemes);
    17.     user.ActivateControlScheme(controlScheme.Value);
    18. }
    19.  
    20. // In the component system.
    21. struct MyGameInputs
    22. {
    23.     public int playerIndex;
    24.     public bool interact;
    25.     public float2 move;
    26.     public float2 look;
    27. };
    28. [ReadOnly] NativeList<MyGameInputs> inputs;
    29. protected override JobHandle OnUpdate(JobHandle inputDependencies)
    30. {
    31.     // Read out input for each player.
    32.     var users = InputUser.all;
    33.     var userCount = users.Count;
    34.     var inputs = new NativeList<MyGameInputs>(userCount, Allocator.TempJob);
    35.     for (var i = 0; i < userCount; ++i)
    36.     {
    37.         var actions = (MyGameActions) users[i].actions;
    38.         inputs.Add(new MyGameInputs
    39.         {
    40.             playerIndex = i, // Or alternatively use InputUser.id
    41.             interact = actions.Gameplay.Interact.triggered,
    42.             move = actions.Gameplay.Move.ReadValue<Vector2>(),
    43.             look = actions.Gameplay.Look.ReadValue<Vector2>(),
    44.         });
    45.     }
    46.  
    47.     // Set up job and feed "inputs" into it...
    48. }
    49.  
    Of course, this assumes it's okay for you to just go frame-to-frame (or, well, update-to-update) with your input granularity. If you do want to go event-to-event instead, you're back to keeping lists of inputs. In that case, I'd probably still go and simply produce one MyGameInputs for each (meaningful) event and would probably employ InputActionTrace in combination with keeping one persistent MyGameInputs state for each player around.

    Indeed. In its current form, InputActionTrace is more suitable to event-style processing than "state-style" processing.
     
    Last edited: Oct 29, 2019
    PhilSA likes this.
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    ooooh, I had no idea there were concepts of "InputUser", "PerformPairingWithDevice" and "AssociateActionsWithUser"! This is really useful

    I'll give your solution a try later this week. I can see how this would work