How to disable specific control schemes on device change?

Hey there,

the last couple of days I spent some time fiddling around with the new Input System (reading the docs, looking into these forums, studying the API) and so far I really do like what I’m seeing. Setting up working controls isn’t too complicated and for know most of the things are well documented, although here’s my first nitpick:

  • the filter in the Docs (Input System | Input System | 1.0.2) is dumb. While looking into the API I stumbled over something called “InputUser”. Eager to know what that is and how it can be used I typed that into the filter and nothing popped up. However there is this big section talking about UserManagement with “InputUser” plastered all over the site (User Management | Input System | 1.0.2) :smile:

But yeah, moving on!

So here’s what I’m trying to achieve and I don’t know exactly how. We’re developing a multiplatform game (PC/Xbox, Playstation, Nintendo Switch) and from the ground up we want to design the Input to be able to support all these platforms. We also installed the Input Packages for the respective console platforms, so that’s not an issue. Also we want to allow the player to use at least the popular gamepads also on the PC version if they should decide to hook up a gamepad (Playstation Gamepad on PC, any Xbox Gamepad on PC, Nintendo Switch JoyCons on PC)

Which means, we came up with this:

For every possible way of playing the game we created a very own control scheme, at least when playing on PC. We would also further add control schemes for the Consoles as well.

Now at first I asked myself. Why would I want to use different control schemes at all. I tried to find anything about the control schemes in the docs but didn’t find the information which I need. Most of the examples and video tutorials (which aren’t that much btw.) max. two control schemes (Keyboard and Gamepad) or one universal scheme which combined both.

Aaaand then I stumbled over this topic: Multiple control schemes or actions

There @Rene-Damm stated:

Yeah! This is exactly what I want. What I need is: the game starts on PC and the default control scheme is KeyboardOnly but as soon a let’s say Xbox One Controller is connected I want to immediately change the control scheme to the XOne Controller. And not only simple change; the keyboard control scheme should be blocked/disabled/whatever. Of course in the actual game there will be an UI Popup asking for permission before actually changing.
For now I need the information how to do this in code. In the topic above there are some directions to potential solutions but they are all related to the PlayerInput Behaviour. Isn’t there any other way?

Currently I’m trying to handle my input like this (and please tell me if this is a dumb way of doing it)

I have these ActionMaps for now, selected the InteractionMap for PC and Xbox as an example:

I have this class InputHandler:

public class InputHandler : MonoBehaviour
    {
        private PlayerInputActions playerInputActions;

        public PlayerInputActions PlayerInputActions
        {
            get { return playerInputActions; }
            set { playerInputActions = value; }
        }

        private void Awake()
        {
            playerInputActions = new PlayerInputActions();
        }

        private void OnEnable()
        {
            InputSystem.onDeviceChange += InputSystemOnDeviceChange;
        }

        private void InputSystemOnDeviceChange(InputDevice device, InputDeviceChange deviceChange)
        {
            switch (deviceChange)
            {
                case InputDeviceChange.Added:
                    Debug.Log("A new device has been added! Device is: " + device.displayName);
                   
                    //TODO: Change pairing from Keyboard to Xbox and dont allow/disable any other schemes
                   
                   
                    break;
                case InputDeviceChange.Removed:
                    Debug.Log(device.displayName + "has been removed!");

                    break;
                case InputDeviceChange.Disconnected:
                    Debug.Log(device.displayName + "has been disconnected!");

                    break;
                case InputDeviceChange.Reconnected:
                    break;
                case InputDeviceChange.Enabled:
                    break;
                case InputDeviceChange.Disabled:
                    break;
                case InputDeviceChange.UsageChanged:
                    break;
                case InputDeviceChange.ConfigurationChanged:
                    break;
                case InputDeviceChange.Destroyed:
                    break;
            }
        }
    }

And then a simple behaviour like this which sets callbacks for the Interaction Map:

public class Interaction : MonoBehaviour,  PlayerInputActions.IInteraction_MapActions
    {
        private InputHandler inputHandler;

        private void Awake()
        {
            inputHandler = GetComponent<InputHandler>();
            inputHandler.PlayerInputActions.Interaction_Map.SetCallbacks(this);
        }

        private void OnEnable()
        {
            inputHandler.PlayerInputActions.Interaction_Map.Enable();
        }

        private void OnDisable()
        {
            inputHandler.PlayerInputActions.Interaction_Map.Disable();
        }


        public void OnInteract(InputAction.CallbackContext context)
        {
            Debug.Log("Phase: " + context.phase);
            Debug.Log("Interaction triggered!");        }
    }

As for now when I hit the “E” key on the Keyboard the OnInteract-Callback is triggered. When I connect my Xbox One Controller the InputHandler recognises it and prints the name. When I hit “X” on the controller the OnInteract-Callback gets triggered as well, awesome. Only thing is: I still can hit the “E” key on the keyboard.

So ultimately: how can I enable/disable controlschemes in code without using the PlayerInput Behaviour?

2 Likes

Bump?

Didn’t read everything, sorry. But I think you’re asking for something that is already exactly what you have.

If you switch to the “gamepad” control scheme when a gamepad is connected, then only the actions contained in this control scheme will be processed. That’s the point of control scheme lol.

Maybe your player input is set to Auto Switch ? Because if it’s not, then it should do what you want out of the box

As mentionend in the post, I don’t use the PlayerInput Behaviour and it’s not working - When both the Keyboard AND the Controllers are connected it registeres both the inputs, Keyboard AND Controller.

And I’d like to know a solution without using the PlayerInput behaviour, I want to control everything via my own behaviours/code.

The generated C# wrappers do not make use of control schemes ATM. Having them support control schemes is on the list. This is why both gamepad and keyboard bindings are active at the same time. We need to make that clearer in the docs.

For now, if you want to restrict things to schemes, you’ll need to do that manually by setting a binding mask.

// Get binding mask for "PC_Scheme_Gamepad_Xbox".
var bindingGroup = playerInputActions.controlSchemes.First(x => x.name == "PC_Scheme_Gamepad_Xbox).bindingGroup;

// Set as binding mask on actions. What this does is cause any binding that doesn't
// match the mask to be ignored. So, by setting the binding mask to that of the "PC_Scheme_Gamepad_Xbox"
// group (whose mask name will default to just "PC_Scheme_Gamepad_Xbox" so probably don't even need to
// look up the name like above), only bindings in that control scheme will be used.
playerInputActions.bindingMask = InputBinding.MaskByGroup(bindingGroup);
8 Likes

Yeah, this looks what I’m after, thanks @Rene-Damm
Any idea when the C# wrappers will also make use of the control schemes? What do you think, in 1.0?

Won’t make it for 1.0 but is high on the list for after given it’s a pretty small work item.

1 Like

Alrighty then, so when’s 1.0 coming out then? :smile:

1 Like

preview.7 was meant to go out last Friday and then, after a round of additional QA, turn into 1.0 this week. Unfortunately, I’ve hit an issue around focus handling that hasn’t yet been willing to give way (been frustrating me plenty). In other words, things have slipped by a few days.

But the plan remains unchanged. 1.0 is about to be cut. After which we will keep addressing issues (1.0 isn’t going to be issues-at-zero) but will also start opening things up again to adding some of the various things that people have been asking for.

I’m trying to implement the workaround method for selecting certain control schemes at a time and I’ve run into a road block. @Rene-Damm You have it set up to make a binding group and then assign the binding mask to the current input action. All of that makes sense to me except when you are looking for a particular binding group. I can’t find the “First()” method with controlSchemes Read Only Array. Am I missing something? or should the method be written in a different way than:
controlSchemes.First(x => x.name == "SchemeName").bindingGroup;

Same here

You need to include Linq

using System.Linq;

Edit: Problem solved (see my next post)

I’m working on a local multiplayer game and when implementing this method, one controller for a control scheme (“Xbox”) is still controlling a character assigned to another control scheme (“Switch”), and vice versa.

Side note: I have two gameObjects with InputActions, one set to a default control scheme of Switch and the other Xbox, both with auto-swtich off, to be sure. Each is assigned to a player, and the bindingMask set respectively to Xbox or Switch.

I’m looking for any alternative than having to store references to separate InputActions per controller type per player (then, hence, having to continually check which controller is being used when retrieving controller inputs).

Of course, I would need to be sure two controllers of the same type wouldn’t control multiple players either.

As control-scheme integration seems to be a high-priority issue (given the potential amount of local multiplayer games in development), thank you for your expertise here and any time spent elsewhere of higher priority.

If there are any other details I need to post, please let me know.

Update: If it might help to know, I solved the issue (of one controller controlling multiple players despite having separate control schemes) by using the ‘invoke Unity events’ method (instead of SendMessages) and accessing the context (e.g. started, canceled) within those functions. I did that instead of adding bindings as such:

player_controls = new Controllers();
// Example
player_controls.Player.Start.started += ctx =>
            {            
                ctrl_start_down = true;
            };

Did this control scheme stuff get sorted? Can we disable/enable control schemes?

1 Like