Search Unity

InControl - input and cross platform controller support for Unity, made easy!

Discussion in 'Assets and Asset Store' started by pbhogan, Jul 18, 2014.

  1. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    I was wondering what the correct approach was for resetting some, but not all, bindings in a PlayerAction to a default state? For example, I have PlayerActions that have a mix of Keyboard bindings and Controller bindings. On my input screen, I let the player reset the Keyboard inputs, or reset the Controller inputs, with two distinct buttons. Because I don't want to reset every binding (only the keyboard bindings, or only the controller bindings) I can't use 'ResetBindings', since that would reset everything. Instead, I'm instantiating a copy of my PlayerActionSet, and using ReplaceBinding to replace the binding with the copy. Unfortunately, that results in "Binding source is already bound to action" warnings. It doesn't seem possible to clear the BoundTo field on the copy to avoid this.

    Is there a general approach to making one binding take on the characteristics of another? Or do I need to test what concrete BindingSource type the object is, instantiate a new one, a manually assign all of the public properties?

    Edit: I did end up just creating a utility method to return a "copy" of a binding, by newing up a binding and setting its values. I didn't handle every possible case, because I didn't need everything, but here's the code I wrote:

    Code (CSharp):
    1.         public static BindingSource CopyBinding(BindingSource binding)
    2.         {
    3.             BindingSource newBindingSource = null;
    4.             if (binding is DeviceBindingSource)
    5.             {
    6.                 var existingDeviceBinding = (DeviceBindingSource)binding;
    7.                 newBindingSource = new DeviceBindingSource(existingDeviceBinding.Control);
    8.             }
    9.             else if (binding is KeyBindingSource)
    10.             {
    11.                 var existingKeyBinding = (KeyBindingSource)binding;
    12.                 newBindingSource = new KeyBindingSource(existingKeyBinding.Control);
    13.             }
    14.             else if (binding is MouseBindingSource)
    15.             {
    16.                 var existingMouseBinding = (MouseBindingSource)binding;
    17.                 newBindingSource = new MouseBindingSource(existingMouseBinding.Control);
    18.             }
    19.             else
    20.             {
    21.                 throw new InvalidOperationException($"{binding.GetType()} not supported");
    22.             }
    23.  
    24.             return newBindingSource;
    25.         }
     
    Last edited: Dec 31, 2019
  2. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    This seems like a fairly complicated way to try to answer what seems like a fairly simple question: Is the player currently using Keyboard & Mouse, or are they using a Controller? I'm trying to find a reliable way to determine that. I've tried to use the approach mentioned here, to look at the LastInputType on various PlayerActionSets, but there's a downside to that: it only fires when one of the specific PlayerActions in the PlayerActionSet is used.

    Is there any lower-level information I can access to learn that the last device that generated some input (even unused input) was keyboard, mouse, or controller? I'm trying to set up some common behavior where if the player does something on a controller, the screen changes to show some controller-specific content. But then, ideally, if they move the mouse or press any key on the keyboard, that controller-ui content will be hidden. The LastInputType only works if the player happens to press one of the buttons associated with a PlayerAction. And very often there are no player actions associated with mouse movement on Pause/Menu screens.

    Anyway, I'm hoping I'm just missing something simple here. Just trying to answer the question of whether, at any given moment, the player is using the Mouse & Keyboard, or the Controller.
     
  3. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I'm afraid there's no shortcut here. I think the way to try do it would be to check for mouse position changes (store the current mouse position each frame and next frame check if it's different, maybe with some minimum delta to exclude mouse jitter) and also use Input.anyKey to check for key/mouse button presses. If there is key/mouse activity, compare InputManager.CurrentTick against the LastInputTick of all devices in InputManager.ActiveDevices. If the former is larger then keyboard/mouse has last produced input otherwise a controller. Ideally, this should run after InControl updates either using script execution order, or you could hook into InputManager.OnCommitDevices which fires once all devices have fully updated and committed state.
     
    dgoyette likes this.
  4. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    Okay. Thanks for confirming there isn't a simple alternative. I don't mind some code complexity as long as I'm not reinventing the wheel.
     
  5. Grhyll

    Grhyll

    Joined:
    Oct 15, 2012
    Posts:
    92
    Uh I don't mean to be annoying or anything, but is there a reason why you won't answer my question about supporting vibrations for PS4 pads on Windows? It's not even so I can decide if I want to use InControl or not, since I've been using it anyway for years and loving it, it's just so I can know if I have to find a way to do it myself or if it might be available before the game I'm working on is released, so I don't have to worry about it.
     
  6. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    This mostly worked. The only snag was that Input.anyKey also picks up joystick buttons. I took an extra step of making sure the that none of the joystick buttons was pressed, and it seems to work pretty well.

    I'll probably find issues with this code in the future, but for now this is what I'll be going with. The only non-standard stuff here is my SubscriptionDirector which I use to notify other parts of my game that the input changed. Happy to be informed if any of this looks suspect.

    The basic idea is that if we're in GamepadMode, we check for keyboard/mouse inputs to know if we should switch to keyboard and mouse mode. Similarly, if we're not currently in GamepadMode, we check if the gamepad has input this frame. Note that I didn't keep track of the last keyboard update tick. Instead, I'm just checking whether the ActiveDevice was updated this tick. The code in Start() handles the active device changing

    Code (CSharp):
    1.  
    2.  
    3. void Start()
    4. {
    5.     // See MonitorForInputDeviceChange() for full information on device tracking.
    6.     InputManager.OnActiveDeviceChanged += inputDevice =>
    7.     {
    8.         InGamepadMode = true;
    9.         CurrentGamepadDevice = inputDevice;
    10.  
    11.         SubscriptionDirector.Instance.Publish(SubscriptionType.InputDeviceChanged, new InputDeviceChangedSubscriptionPayload()
    12.         {
    13.             InGamepadMode = InGamepadMode,
    14.             GamepadDevice = CurrentGamepadDevice
    15.         });
    16.     };
    17. }
    18.  
    19.  
    20. void Update()
    21. {
    22.     MonitorForInputDeviceChange();
    23. }
    24.  
    25.  
    26. // This is based on guidance from https://forum.unity.com/threads/incontrol-input-and-cross-platform-controller-support-for-unity-made-easy.257619/page-12#post-5296677
    27. private void MonitorForInputDeviceChange()
    28. {
    29.     // This method is partially responsible for monitoring for input and decide generally whether the player is currently
    30.     // using Mouse&Keyboard, or a controller. There are three cases we need to test for:
    31.     //  1. Currently in Mouse & Keyboard mode, but a gamepad was used.
    32.     //  2. Currently in Gamepad mode, but started using Mouse & Keyboard  
    33.     //  3. Currently in Gamepad mode, but started using a different gamepad.
    34.     // For efficiency, we try to avoid looking at inputs that don't matter. So, if we're in Mouse and Keyboard
    35.     // mode, we don't keep looking for mouse and keyboard input. We only look for gamepad input. It may seem like we'd
    36.     // need to look for gamepad input even if we're in gamepad mode, but actually changes to the gamepad device are handled
    37.     // via the OnActiveDeviceChanged in the InputManager. So this method only actually handles cases 1 and 2, while case
    38.     // 3 is handles via OnActiveDeviceChanged
    39.  
    40.     bool hasChange = false;
    41.     if (InGamepadMode)
    42.     {
    43.         // We're in Gamepad mode. See if we should switch to Mouse and Keyboard.
    44.         // We do this if we get any non-joystick key input, or we get mouse input.
    45.         if (Input.anyKeyDown || Input.GetAxis(UnityConstants.Axes.MouseX) != 0 || Input.GetAxis(UnityConstants.Axes.MouseY) != 0)
    46.         {
    47.             if (!JoystickKeyDown())
    48.             {
    49.                 // It wasn't a joystick key. Switch to Keyboard mode.
    50.                 InGamepadMode = false;
    51.                 CurrentGamepadDevice = null;
    52.                 hasChange = true;
    53.  
    54.             }
    55.         }
    56.     }
    57.     else
    58.     {
    59.         // We're in Mouse & Keyboard mode. See if there's an ActiveDevice which received input this frame.
    60.         // If so, we switch to that gamepad.
    61.         if (InputManager.ActiveDevice != null
    62.             && InputManager.ActiveDevice.LastInputTick == InputManager.CurrentTick)
    63.         {
    64.             InGamepadMode = true;
    65.             CurrentGamepadDevice = InputManager.ActiveDevice;
    66.             hasChange = true;
    67.         }
    68.     }
    69.  
    70.     if (hasChange)
    71.     {
    72.         SubscriptionDirector.Instance.Publish(SubscriptionType.InputDeviceChanged, new InputDeviceChangedSubscriptionPayload()
    73.         {
    74.             InGamepadMode = InGamepadMode,
    75.             GamepadDevice = CurrentGamepadDevice
    76.         });
    77.     }
    78. }
    79.  
    80. private bool JoystickKeyDown()
    81. {
    82.     for (int keyIndex = 0; keyIndex < _rawJoystickKeycodes.Length; keyIndex++)
    83.     {
    84.         if (Input.GetKeyDown(_rawJoystickKeycodes[keyIndex]))
    85.         {
    86.             return true;
    87.         }
    88.     }
    89.  
    90.     return false;
    91. }
    92.  
     
  7. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    Sorry, it's unlikely to come soon at this point. I've taken a few stabs at it without success.
     
    Grhyll likes this.
  8. cloverme

    cloverme

    Joined:
    Apr 6, 2018
    Posts:
    32
    Is there a way to force the update of the buttonmapping handle names? So that "Action3" turns into "Square" for Ps4 for example...It does happen after a rebinding event for any input, but I can't seem to find anything to call to force it on my own. Use Fixed Update doesn't seem to be doing it, so I figure there's something else I can call that will do it?

    When I create the default mappings, I'm binding an action "fire" to Action3 for a generic controller. But if the player has a Ps4 controller plugged in, all the buttonmapping handle names stay as the default until I do a remapping, then they all update to the Ps4 types. So I just want to be able to "force" that call on my own but can't seem to figure out what does it.
     
  9. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    Technically, it updates the names as soon as a device is considered active (or the active device changes). Rebinding would cause that too since it's obvious the device that pressed the button is now active. But before that, it has no idea which devices are relevant.

    So, for example, lets say you have 4 controllers attached:
    1. Xbox One
    2. PlayStation 4
    3. Nintendo
    4. A broken, unknown or non-responsive controller.

    Now, you make an action set and add some default bindings. Which control names should it use? It's ambiguous. And if you arbitrarily pick one, let's say the first one.

    What if (1) was the broken controller? Note, InControl does not know when a controller is broken or non-responsive.

    What if this is the second action set you're creating? Multiple input sets can listen to the same device. This is a frequent use case to separate game and menu input, for example.

    It gets trickier because you can change (after you've added bindings) which devices the action set should include or exclude for listening.

    What happens if there were no devices at the time the bindings were added? What if the device you pick arbitrarily disconnects soon after? What if happens if another device connects?

    So, all this is to say, it gets extremely non-trivial to decide which names to use and when to update them, until the moment a device gives input.

    I could, and probably should, add something to the API where you can do something like actionSet.SetDefaultDeviceContext( InputDevice device ); and it'll just use what you tell it for better or worse. Which lets you choose... but of course then you need to ask yourself all these questions and more.

    If you know it's only going to listen to a single device, you could just set actionSet.Device = inputDevice and that's the one it'll use because you've been explicit about it. Otherwise it'll use the first active device it finds after filtering them with IncludeDevices and ExcludeDevices. So you could also force a device to become active, although this currently requires calling an internal method so you'd need to change that to public: inputDevice.RequestActivation();

    Okay, but what should you do. Here's my recommendation:

    Don't use binding names for DeviceBindingSource. Rather, use DeviceBindingSource.Control which is of InputControlType, and combine that with the PlayerAction/PlayerActionSet's LastDeviceStyle property in a lookup table for icons instead, and as a fallback use Xbox icons, or, even better, something neutral like many games do like the diamond of four buttons with just the one you intend filled in and the rest empty circles.
     
  10. lazylukey

    lazylukey

    Joined:
    May 26, 2017
    Posts:
    6
    @pbhogan This bug reported by malyKuba still seems to be around in the v1.7.4. I am using Unity 2019.2.16f1. Is there any workaround for it?
     
  11. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    Huh. I seem to have missed that one.

    Here's how I'll fix it in the next update. In InControlManager.cs, add:
    bool applicationHasQuit = false;

    In OnApplicationQuit() add:
    applicationHasQuit = true;

    In Update() and FixedUpdate() add:
    if (applicationHasQuit) return;
     
  12. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    Any thoughts on this one, @pbhogan? Is there a known issue where XBox360 controllers are misidentified as XBoxOne controllers?
     
  13. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I'd need to see the output of the TestInputManager scene to know for sure, but my guess is it's happening with XInput enabled. Microsoft's XInput API provides no identifying information about the underlying hardware, so InControl has no idea what exact controller it is other than it's an XInput-compatible controller... which means it's either an Xbox 360 or Xbox One controller (or a third-party version of one of these) and I had to pick one for the device style representation, so I picked the newer one.

    The same goes for the UWP gamepad API incidentally. It's a weird omission by Microsoft, but it is what it is. ¯\_(ツ)_/¯
     
  14. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    Yes, this is with XInput enabled. So basically, XInput can't tell XBox360 from XBoxOne? Okay. I'll see what I can do to work around that. Thanks for the confirmation.
     
  15. TCROC

    TCROC

    Joined:
    Aug 15, 2015
    Posts:
    127
    The TouchControlManager.cs script throws a Null Reference Exception. Modifying line 541 to be
    var touchControlCount = touchControls?.Length;
    instead of
    var touchControlCount = touchControls.Length;
    fixes the error.

    Controls Enabled is always marked as true in the inspector as well, even if it is false.

    The current workaround is to check it on / off once and it fixes itself.

    Not sure what is causing that one.

    Current version of Unity = 2019.2.13f1.

    TouchControlManager is a prefab and being modified in the prefab editor.
     
  16. bigpants

    bigpants

    Joined:
    Dec 9, 2009
    Posts:
    35
    (not an emergency, and apologies if you've already addressed this)
    I'm using Unity 2020.1.0a (alpha) and InControl 1.7.2 is working well!
    PC using old Unity control system - XBOX 360 controller, mouse and keyboard.

    However, I cannot get the latest InControl 1.74.
    PackageManager only lists/downloads 1.7.2
    I can download the 1.7.4 .unitypackage file from your site,
    BUT I don't think I can import that anymore in 2020 - it needs a .json file even for disk import.

    NOT an emergency, just FYI.
     
  17. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I don't know what to tell you... I haven't had that problem at all with Unity 2020. I just checked with 2020.1.0a19 (latest as of writing) and it shows 1.7.4 and imports fine.

    Maybe there's some package cache that is corrupted. ¯\_(ツ)_/¯
     
  18. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I'll take a look... it's probably related to being a prefab.
     
  19. bigpants

    bigpants

    Joined:
    Dec 9, 2009
    Posts:
    35
    I'm using 2020.1.0a18.
    I just tried a FRESH new project and only 1.7.2 is shown.
    I think that rules out the package cache (unless that's shared between Unity projects).
    At some point I will upgrade to 2020.1.0a19 and see if that fixes it.
     
  20. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I'm pretty sure the package caches are system level and shared between projects on some level. But I'm not sure how Unity deals with assets since they're not really packages. There's an asset store cache too somewhere on the system so maybe you need to find that and delete it.

    In any event, it's a Unity issue, not InControl. I didn't have any problems with any of the previous 2020.1.0a releases either.
     
  21. bigpants

    bigpants

    Joined:
    Dec 9, 2009
    Posts:
    35
    PROBLEM FIXED (see above postings)

    Details:
    Unity 2020.1.0a18 (fresh new project) ONLY showing InControl 1.7.2 - NO option to download latest 1.7.4.
    (I used InControl 1.7.2 in an older Unity 2018 project)

    Solution:
    Downloaded packages (imported into project or not) are cached.
    Delete the "Gallant Games" folder, and package manager will display 1.7.4
    (presumably goes to the online store now, rather than just your local drive)
    C:\Users\yourname\AppData\Roaming\Unity\Asset Store-5.x\Gallant Games
     
  22. Player7

    Player7

    Joined:
    Oct 21, 2015
    Posts:
    1,535
    Any update on this with the Unity input system now?
     
  23. drinkycode

    drinkycode

    Joined:
    Sep 28, 2014
    Posts:
    7
    @pbhogan It's been a little while since the last update and I was wondering if InControl has plans in the future to support some of the improvements added in iOS 13 (https://developer.apple.com/videos/play/wwdc2019/616/) especially for handling both the Share (PS4) and View (Xbox One) buttons (right now they all map the same as the home/menu button ala MFi controls).
     
  24. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    As it happens, yes, I've got this in for the next update. It'll also have MFi support for Mac, although that is a bit more experimental at the moment (and will be labelled as such). But MFi on iOS should be good to go. Both will be part of the native input plugin directly using Apple's Game Controller framework. The update has a number of other additions and internal changes which has held up release, but I'm hoping to get it out soon.

    If you're interested in trying it out, keep an eye out on the downloads page (http://www.gallantgames.com/downloads) for a new beta in the next few days. It will probably be labelled version 1.8.0
     
    Grhyll and drinkycode like this.
  25. drinkycode

    drinkycode

    Joined:
    Sep 28, 2014
    Posts:
    7
    That sounds great! We'd be happy to take a look at the beta and implement it in our current game (and hopefully provide feedback for that too).
     
  26. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    It's up now at http://www.gallantgames.com/downloads

    You'll need to create an account and add your InControl license to see the relevant package. Look for 1.8.0 beta
     
    mlee_schell likes this.
  27. drinkycode

    drinkycode

    Joined:
    Sep 28, 2014
    Posts:
    7
    @pbhogan Testing out the InControl 1.8.0 beta, everything looks good up until I need to archive the release for uploading to the App Store. When I try to archive, I get the following error:


    ld: bitcode bundle could not be generated because 'XXX/Libraries/InControl/Plugins/iOS/InControlNative.a(main.o)' was built without full bitcode. All object files and libraries for bitcode must be generated from Xcode Archive or Install build for architecture arm64


    It looks like the plugin might need a recompile to force bitcode (ala this issue: https://stackoverflow.com/questions/31233395/ios-library-to-bitcode). Thanks for your support with maintaining this plugin!
     
  28. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I've uploaded a new package that includes binaries with the embed bitcode flag. Would you give that a go please?
     
    drinkycode likes this.
  29. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,496
    Hi, apologies if this has already been answered, but the search-in-thread function showed nothing:

    From what I understand, it's not possible to bind a modifier key plus mouse button (example: Shift + Left Click).

    The easiest way I can see doing this is modify KeyBindingSource to optionally have mouse buttons in them too, or maybe an entirely new BindingSource class altogether. Maybe I can use KeyBindingSource as a starting point. But basically from what I see I need to somehow combine KeyBindingSource's key detection plus MouseBindingSource's mouse button detection. Is that correct?

    Note: I'm on v1.7.2 since the project is still on Unity 5.5, we don't have time to do an update to the latest version of Unity in the middle of production right now. I don't know if the latest version has added what I'm looking for.
     
  30. paulogodinhoaq

    paulogodinhoaq

    Joined:
    Aug 22, 2018
    Posts:
    3
    Hi, we are using Unity 2019.3.3f1 with InControl 1.7.4 on Apple TV and it looks like MENU button on the Remote Control(Siri Controller) is not being recognized, the TestInputManager Scene shows all button being pressed but this one. It also happens with both middle center buttons from the Xbox One controller(back and start) when they are connected to the Apple TV, they don't show as pressed on the test scene.
     
  31. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    I made an experimental ComboBindingSource a while back. I think it might still work. You can check it out here: https://gist.github.com/pbhogan/6ad47df1331da9353cbd3717effbc7a1
     
    AnomalusUndrdog likes this.
  32. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    Try the 1.8.0 beta available here: http://www.gallantgames.com/downloads

    Unity changed some mappings for iOS, but also the beta (with native input enabled) includes native support for Apple's Game Controller API which should fully support all the controllers and additional buttons made available in iOS/tvOS 13+
     
    drinkycode and paulogodinhoaq like this.
  33. drinkycode

    drinkycode

    Joined:
    Sep 28, 2014
    Posts:
    7
    That worked @pbhogan and archived successfully! Thanks!
     
  34. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    2,463
    I had a remote tester try my game, and ran into problems because his DeviceClass was "Unknown". I'm trying to track down the person and find out what controller they were using, but I see this in their log file:

    [InControl] Joystick 1: "Controller (GAME FOR WINDOWS)"

    Is this just a super generic kind of controller than can't be mapped to a real controller? Just trying to decide how I should handle this.
     
  35. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    249
    Yes, it's almost certainly a generic controller that is unknown to InControl. A mapping profile can most likely be created (unless the controller just doesn't work in some way) but it requires some work to make the profile and, importantly, requires having the physical controller present to figure it out.

    I do try to buy many commonly used and requested controllers to add and maintain support for them, but obviously I can't buy or obtain every esoteric generic or cloned controller out there.

    Since you don't have the controller yourself you probably won't want to do this, but should you (or anyone else) need to know how to make mapping profiles, you can consult this outline: https://gist.github.com/pbhogan/34096baa4e619e4b9db0b0093d89541e

    One thing I do recommend is enabling XInput (either the managed version without native input, or the version included in native input). This will vastly increase compatibility for Xbox controller clones, which makes up a significant chunk of cheap, off-brand controllers. It should also allow users to use tools like x360ce to map generic controllers themselves.
     
    dgoyette likes this.
unityunity