Missing features: Sensitivity, Unity Action arguments

Use case
Rotating camera with gamepad or mouse.

Approach
Having Vector2 RotateCamera action with a binding for each control scheme. Sending UnityEvent in PlayerInput when action is triggerd. Reading the event and applying the given input value in camera control script.

Missing Features
I need to be able to send value in unity events in PlayerInput.
I need sensitivity setting in binding properties so I can use this action’s value directly no matter if its a mouse or a gamepad stick. Currently I would have to create additional layer for handling those events and set sensitivities there. Also I need that layer because Unity Actions are void for some reason instead of having the value type of the action (V2 in this case).

With those features in place I could just directly use PlayerInput anywhere I want.

If you have control schemes, then you can just do something simple like :

switch (_playerInput.currentControlScheme)
{
    case "MouseKeyboard":
        yourInput * MouseSensitivity;
        break;
    case "Gamepad":
        yourInput * GamepadSensitivity;
        break;
}

On closer inspection I can’t make another generic “layer” for the input

5069093--498278--upload_2019-10-15_15-52-57.png

because Player input doesn’t send any data about the event.

Instead it seems that my only option is to manually handle each Action… I t could be so easy but now I need a class for each action.

This could also could be solved by _playerInput having a .currenAction so when it’s triggered I could go and get any additional data I want.

The idea here is to put sensitivity scaling as processors on the bindings. E.g. have a scale processor on the gamepad left stick binding with one value and another scale processor with a different value on the mouse delta binding.

One thing we’re missing here is the ability to set parameters dynamically. ATM, if, for example, you want to have a mouse sensitivity setting in your game settings and want that to affect the mouse delta binding, the only way to do so currently is by writing a custom processor.

#if UNITY_EDITOR
[InitializeOnLoad]
#endif
public class MouseSensitivityProcessor : InputProcessor<Vector2>
{
    public override Vector2 Process(Vector2 value, InputControl control)
    {
        return value * MyGameSettings.mouseSensitivity;
    }

    #if UNITY_EDITOR
    static MouseSensitivityProcessor()
    {
        Register();
    }
    #endif

    private static void Register()
    {
        InputSystem.RegisterProcessor<MouseSensitivityProcessor>();
    }
}

BTW, to find out where input is coming from, you can use InputAction.CallbackContext.control. This gives you the control that triggered the action. From it, you can also reach the device via InputControl.device.

Thanks. Is there a way to get the current control scheme without strings and preferably without PlayerInput? Ideally subscribe via script to an event on control scheme change?

Yeah, you can do :

InputUser.onChange += yourCallback;

private void yourCallback (InputUser user, InputUserChange change, InputDevice device)
{
    if (_playerInput.user != user) return;
    if (change != InputUserChange.ControlSchemeChanged) return;
  
    yourLogic here
}

That’s what I’m doing at least

PlayerInput is built on InputUser which can be used directly to manage devices, actions, and control schemes. However, PlayerInput is currently the only way to get fully rigged support for automatic control scheme switching. It’s on the TODO list to equip generated C# classes with equivalent means.

// Start user out with keyboard&mouse.
var user = InputUser.PerformPairingWithDevice(Keyboard.current);
InutUser.PerformPairingWithDevice(Mouse.current, user: user);

// Pick keyboard&mouse control scheme
user.ActivateControlScheme("Keyboard&Mouse");

// Associate actions with user.
user.AssociateActionsWithUser(myActions);

// Listen for unpaired device activity to initiate control scheme switches.
++InputUser.listenForUnpairedDeviceActivity;
InputUser.onUnpairedDeviceUsed +=
    (control, eventPtr) =>
    {
        // Initiate control scheme switch. "control" is the control that the user actuated.
        // Guaranteed to not be noise. "control.device" is the device that received input.
        // Determine which control scheme to use and update device pairing accordingly.
    };

xxx

Thank you. I totally missed the processors.

So what is the recommended way to read the event value? InputAction.CallbackContext.control I have no idea what that is…

5069264--498293--upload_2019-10-15_16-31-58.png

I don’t see InputUser class anywhere either…

5069264--498302--upload_2019-10-15_16-37-0.png

UnityEngine.InputSystem is imported.

I’m using Rider so all using are done automatically, but I think it’s in this :

using UnityEngine.InputSystem.Users;

Just to be clear I would prefer to use just the PlayerInput, but I can’t find a way to access the input value. This is the core issue.

Right, makes sense. Thanks.

Your callback using PlayerInput/Invoke Unity events should be

public void YourCallback(InputAction.CallbackContext ctx)
{
ctx.ReadValue();
}

Yes, I just remembered how to use the context

Now gonna try in PlayerInput event callbacks.

So, I thought you mean this:
5069471--498359--upload_2019-10-15_17-7-5.png
But PlayerInput event can’t see OnRotationInput();

Here is the weird part for me. Out following three methods it can see only bool one… Why??
5069471--498365--upload_2019-10-15_17-12-49.png

5069471--498362--upload_2019-10-15_17-11-26.png

I guess this is not bad at all:

But ideally I would avoid making these input scripts and would use PlayerInput instead.

Another reason to have these input scripts is to handle global sensitivity later if it dynamic sensitivity is not supported. Or is there dynamic global sensitivity built into the input system?

Offtopic

Sorry for the offtopic, but please use the CODE tags on the forums. It is very hard to follow your code in dark, unreadable screenshots. You can find info how to paste code in the forums here .

Offtopic

Okay. I thought it’s much more readable than the style in which code is presented here with tags.

For what it’s worth, I didn’t have a problem reading your code. And I much prefer the darker theme. :smile::sunglasses:

That said, it is better to use actual text rather than screenshots, for accessibility purposes.

I have an issue working with custom processors. I’ve copied your code, changed MyGameSettings.mouseSensitivity to my value.
Everything works fine in the editor. When I hit play - mouse is working properly.
But when I do Build & Run - code that didn’t use processors, and code that uses default ones like ‘Scale Vector2’ works, and code that uses this ‘MouseSensitivityProcessor’ seems not working and affected input completely lost.

On a side note I have Cursor.lockState = CursorLockMode.Locked in my application

I had the same problem when copying this code. Everything worked in the editor, but when I made a build, the actions using my custom processor was not working, as if the input was lost/didn’t happen.

I solved it by adding [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] to the Register-method.

#if UNITY_EDITOR
[InitializeOnLoad]
#endif
public class MouseSensitivityProcessor : InputProcessor<Vector2>
{
    public override Vector2 Process(Vector2 value, InputControl control)
    {
        return value * MyGameSettings.mouseSensitivity;
    }
    #if UNITY_EDITOR
    static MouseSensitivityProcessor()
    {
        Register();
    }
    #endif

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Register()
    {
        InputSystem.RegisterProcessor<MouseSensitivityProcessor>();
    }
}

I can’t say I completely understand why it works. I found this thread with a different issue with custom processors, that’s why I tried it. Possibly a bad solution but seems to work the way I want it to now.

If anyone come across this and know why this worked or if there is a better way to solve it I would love to know.

(The actions I use the custom processor for is used only with the CinemachineInputProvider-script, if that is relevant.)