Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to disable specific control schemes on device change?

Discussion in 'Input System' started by BTStone, Jan 30, 2020.

  1. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    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 (https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/index.html) 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 (https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/UserManagement.html) :D

    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:

    Controlschemes.png

    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: https://forum.unity.com/threads/multiple-control-schemes-or-actions.727583/

    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:

    ActionMaps_PC.png

    ActionMaps_Xbox.png


    I have this class InputHandler:


    Code (CSharp):
    1. public class InputHandler : MonoBehaviour
    2.     {
    3.         private PlayerInputActions playerInputActions;
    4.  
    5.         public PlayerInputActions PlayerInputActions
    6.         {
    7.             get { return playerInputActions; }
    8.             set { playerInputActions = value; }
    9.         }
    10.  
    11.         private void Awake()
    12.         {
    13.             playerInputActions = new PlayerInputActions();
    14.         }
    15.  
    16.         private void OnEnable()
    17.         {
    18.             InputSystem.onDeviceChange += InputSystemOnDeviceChange;
    19.         }
    20.  
    21.         private void InputSystemOnDeviceChange(InputDevice device, InputDeviceChange deviceChange)
    22.         {
    23.             switch (deviceChange)
    24.             {
    25.                 case InputDeviceChange.Added:
    26.                     Debug.Log("A new device has been added! Device is: " + device.displayName);
    27.                    
    28.                     //TODO: Change pairing from Keyboard to Xbox and dont allow/disable any other schemes
    29.                    
    30.                    
    31.                     break;
    32.                 case InputDeviceChange.Removed:
    33.                     Debug.Log(device.displayName + "has been removed!");
    34.  
    35.                     break;
    36.                 case InputDeviceChange.Disconnected:
    37.                     Debug.Log(device.displayName + "has been disconnected!");
    38.  
    39.                     break;
    40.                 case InputDeviceChange.Reconnected:
    41.                     break;
    42.                 case InputDeviceChange.Enabled:
    43.                     break;
    44.                 case InputDeviceChange.Disabled:
    45.                     break;
    46.                 case InputDeviceChange.UsageChanged:
    47.                     break;
    48.                 case InputDeviceChange.ConfigurationChanged:
    49.                     break;
    50.                 case InputDeviceChange.Destroyed:
    51.                     break;
    52.             }
    53.         }
    54.     }

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


    Code (CSharp):
    1. public class Interaction : MonoBehaviour,  PlayerInputActions.IInteraction_MapActions
    2.     {
    3.         private InputHandler inputHandler;
    4.  
    5.         private void Awake()
    6.         {
    7.             inputHandler = GetComponent<InputHandler>();
    8.             inputHandler.PlayerInputActions.Interaction_Map.SetCallbacks(this);
    9.         }
    10.  
    11.         private void OnEnable()
    12.         {
    13.             inputHandler.PlayerInputActions.Interaction_Map.Enable();
    14.         }
    15.  
    16.         private void OnDisable()
    17.         {
    18.             inputHandler.PlayerInputActions.Interaction_Map.Disable();
    19.         }
    20.  
    21.  
    22.         public void OnInteract(InputAction.CallbackContext context)
    23.         {
    24.             Debug.Log("Phase: " + context.phase);
    25.             Debug.Log("Interaction triggered!");        }
    26.     }

    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?
     
    GrimnirTheWhite likes this.
  2. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    Bump?
     
  3. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    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
     
  4. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    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.
     
  5. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    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.

    Code (CSharp):
    1. // Get binding mask for "PC_Scheme_Gamepad_Xbox".
    2. var bindingGroup = playerInputActions.controlSchemes.First(x => x.name == "PC_Scheme_Gamepad_Xbox).bindingGroup;
    3.  
    4. // Set as binding mask on actions. What this does is cause any binding that doesn't
    5. // match the mask to be ignored. So, by setting the binding mask to that of the "PC_Scheme_Gamepad_Xbox"
    6. // group (whose mask name will default to just "PC_Scheme_Gamepad_Xbox" so probably don't even need to
    7. // look up the name like above), only bindings in that control scheme will be used.
    8. playerInputActions.bindingMask = InputBinding.MaskByGroup(bindingGroup);
     
  6. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    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?
     
  7. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Won't make it for 1.0 but is high on the list for after given it's a pretty small work item.
     
    BTStone likes this.
  8. BTStone

    BTStone

    Joined:
    Mar 10, 2012
    Posts:
    1,422
    Alrighty then, so when's 1.0 coming out then? :D
     
    EdgarMtz1807 likes this.
  9. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    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.
     
  10. treecki

    treecki

    Joined:
    Feb 6, 2017
    Posts:
    28
    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;
     
  11. VR_Junkie

    VR_Junkie

    Joined:
    Nov 26, 2016
    Posts:
    77
    Same here
     
  12. The_Chef

    The_Chef

    Joined:
    Dec 28, 2016
    Posts:
    2
    You need to include Linq

    Code (CSharp):
    1. using System.Linq;
     
  13. petiatil

    petiatil

    Joined:
    Feb 24, 2021
    Posts:
    2
    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.
     
    Last edited: Feb 3, 2022
  14. petiatil

    petiatil

    Joined:
    Feb 24, 2021
    Posts:
    2
    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:

    Code (CSharp):
    1. player_controls = new Controllers();
    2. // Example
    3. player_controls.Player.Start.started += ctx =>
    4.             {            
    5.                 ctrl_start_down = true;
    6.             };
     
  15. chriscode

    chriscode

    Joined:
    Mar 2, 2015
    Posts:
    46
    Did this control scheme stuff get sorted? Can we disable/enable control schemes?