Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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

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

  1. Odaclick

    Odaclick

    Joined:
    Nov 17, 2014
    Posts:
    8
    Hi, I'm having troubles with the plugin after installing Unity 2017.1. Is that a problem of the plugin or it's on my side?
     
  2. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    It’s a bug. The fix is in InControl/Source/Unity/UnityInputDeviceProfile on line 38 in the constructor. Change the line to:

    MaxUnityVersion = new VersionInfo( int.MaxValue, 0, 0, 0 );

    The package should otherwise be Unity 2017 compatible and this will be fixed in the next update.
     
  3. Odaclick

    Odaclick

    Joined:
    Nov 17, 2014
    Posts:
    8
    mmm it does not work for me. Should wait for the updated. Any idea when it's coming?
     
  4. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    I should also ask exactly what problems you're having. I was assuming you're just seeing all devices as unknown controllers without native input enabled?

    But I've put up a beta of 1.6.15 at http://www.gallantgames.com/downloads
    You'll need to register for an account and add your invoice number to get access to it.
     
  5. Odaclick

    Odaclick

    Joined:
    Nov 17, 2014
    Posts:
    8
    Stick button always worked.
    After doing the version change, menu button also works. However action button didn't work (before or after the change).

    Will register in the beta and let you know
     
  6. Odaclick

    Odaclick

    Joined:
    Nov 17, 2014
    Posts:
    8
    Version: InControl (version 1.6.15 build 9077)

    Works fine!
     
  7. AsmCoder8088

    AsmCoder8088

    Joined:
    Dec 7, 2016
    Posts:
    30
    Apologies if this has been asked before, but I am trying to determine if a gamepad/joystick/controller (whatever the proper term is) is connected, as opposed to _just_ a keyboard/mouse.

    I thought the correct approach was to check if the InputManager.ActiveDevice is null, and if so, that meant that a controller was not connected (just keyboard/mouse).

    However, I still get a non-null ActiveDevice even when no controller is connected. In this case, the name of the ActiveDevice appears to be "None" (InputManager.ActiveDevice.Name)

    So, is the correct approach to determine if a controller is connected to check the name of the active device, and if it is "None", then the active controller is keyboard/mouse, otherwise it is a gamepad/joystick/controller?
     
  8. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    If you just want to know if no devices are attached, simply check the length of InputManager.Devices

    If it's zero, no controllers are attached.

    The caveat is that, for legacy reasons, no keyboards/mice are considered "devices" in InControl. They only interact with the bindings API (PlayerActionSet, etc.).

    This may change in the future. But it would be in a major 2.0 type of update with lots of API changes, so you'll absolutely know when that happens. The most "correct" way to do the check is to iterate over InputManager.Devices and query their DeviceClass property to see what they are and count them appropriately.
     
  9. AsmCoder8088

    AsmCoder8088

    Joined:
    Dec 7, 2016
    Posts:
    30
    I think what I actually want to do at this point is determine not just whether a controller is connected, but whether the active device is itself a controller as opposed to keyboard/mouse. This way, I can present the user with corresponding screen to show what controls to use. If keyboard/mouse is the ActiveDevice, then I would present the controls for that, otherwise I would present a screen showing the controls for a controller.

    So, is checking the name of the ActiveDevice the proper way to do that? Will keyboard/mouse always return a name of "None"? Or is there some other property that indicates that the ActiveDevice is a keyboard/mouse? I would check the InputManager.Devices list, but even if a controller is connected, the player may be using the keyboard instead and would prefer to show the keyboard controls in that circumstance.
     
  10. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    If that's your goal, you'll need to use a PlayerActionSet to determine input type switches:

    http://www.gallantgames.com/pages/incontrol-bindings-faq

    It can't be done with InputManager.ActiveDevice or InputManager.Devices as keyboard/mouse doesn't appear in there and doesn't change the active device. The active device remains the last controller that supplied input, even if a keyboard or mouse did something after that.
     
  11. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,936
    Hi there,
    Perhaps a stupid question but better to ask :p
    I have a touch based game template which i want to publish on Amazon Fire TV, what i want is looking for a Input Plugin that if i use automatically make it work with their controller?
    Please advise
     
  12. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    InControl does work with the Fire TV controller, or at least it did when I last tested it, which admittedly was several months ago. I don't see any reason why it wouldn't still be working though.
     
  13. FM-Productions

    FM-Productions

    Joined:
    May 1, 2017
    Posts:
    72
    Hi,

    I don't know if someone asked something similar before, but I'm trying to implement Online Multiplayer in my game and therefore I need to control the inputs with commands and no real controller/keyboard inputs. Currently, I'm using a system like the MyCharacterAcitons in the following tutorial: http://www.gallantgames.com/pages/incontrol-binding-actions-to-controls

    My question is, is there an easy way to instruct controller inputs manually? Such as a virtual layer that sets values of an physical non existant controller? I took a quick look in the api and noticed functions like UpdateWithValue or UpdateWithState that seem to permit to update input values manually. But I'm not sure about the correct use of it or if it
    http://www.gallantgames.com/incontrol-api/html/class_in_control_1_1_one_axis_input_control.html

    Another option I could resort to is to use interfaces for all relevant InputControl classes and provide my own implementation which allows to set actions manually, but I would rather not resort to that since it could mean I have to chance something in the InControl classes which I want to avoid.

    If there is an easy way to accomplish that, I would love to hear it. A system which allows for virtual input via commands in code could also be beneficial for writing enemy ai logic in my game, but I still have to do a lot of research about that topic.

    Kind regards
     
  14. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    You could create a virtual device and feed input into it for this purpose. It's pretty easy to set up. See the Virtual Device example in the Examples folder. You basically just create a class that inherits from InputDevice, set up the controls you want on it and feed in input when it calls its Update() function. Since you're kind of wanting to push input instead of polling, you may need to create some variables on it to hold the current input state which you then submit when it polls with Update(), but that should be fairly trivial to do. Attach it to the InputManager with InputManager.AttachDevice() and you're good to go.

    Virtual devices are just like any other device in InControl so it'll act the same. If you want to then restrict a PlayerActionSet to reading from it, just set its Device property to the virtual device.
     
  15. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    any chance to see incontrol touch use canvas
     
  16. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    It's unlikely to happen in the short term as it would take a complete rewrite and drop compatibility for older versions of Unity (which is not as big a deal as it used to be). It is something I want to investigate.
     
  17. IndieFist

    IndieFist

    Joined:
    Jul 18, 2013
    Posts:
    518
    Hi!
    I`m searching about to launch my game only if got gamepad and its android tv, but cannot find any plugin or function for this, only for gamepad, with string[] names = Input.GetJoystickNames (); but then, the remote control of android tv its recognized as a gamepad o joystickname
    Got this solved with your plugin?
    Kind regards.
     
  18. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    I'm not 100% sure I understand what you're asking, but last I checked InControl worked with both the remote and gamepads on major Android TV devices.
     
  19. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    Hi!
    Thank you for that nice plugin. I successfully converted most of my old input manager to use InControl, but I have a problem with arrows.

    I have four PlayerAction objects : Left, Right, Down and Up, that I use to create a PlayerTwoAxisAction this way:
    Code (csharp):
    1. this.Navigation = new PlayerTwoAxisAction(this.Left, this.Right, this.Down, this.Up);
    But the following code only logs "Right was pressed.", and Navigation.Value is always (0.0, 0.0).
    Code (csharp):
    1. if(this.KeyboardActions.Right.WasPressed) {
    2.     Debug.Log("Right was pressed.");
    3. }
    4. if(this.KeyboardActions.Navigation.WasPressed) {
    5.     Debug.Log("Navigation is pressed.");
    6. }
    Any idea what's the problem?

    Edit: Well, I'm probably gonna keep a part of my old code and just use the four PlayerAction objects instead of the PlayerTwoAxisAction in fact, cause I don't see a WasRepeated property on PlayerTwoAxisAction. Plus I use a RepeatedCount property and a Direction enum in my old navigation manager anyway. Though, I'm still curious about what I did wrong with the PlayerTwoAxisAction.
     
    Last edited: Aug 18, 2017
  20. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    Usually this kind of thing is due to not creating the actions in the action set constructor or trying to do inheritance with action sets, but to be sure I'd need you to post all the code creating your PlayerActionSet objects. Feel free to e-mail me something to look at if you want.
     
  21. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    I create actions in the action set constructor, but I do use inheritance on my action sets. Does using inheritance pose other problems? Or can I use it, since I will not use PlayerTwoAxisAction anyway?
     
  22. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    Inheritance mostly works with action sets, but there are non-obvious situations where it breaks.

    For the most part, the rule of thumb is create PlayerAction and TwoAxisPlayerAction objects in the constructor and never reassign them i.e. don't try to add bindings on one action set and copy the actions to another action set. There are internal references created when actions are constructed that cannot be copied.

    Basically treat PlayerAction and PlayerTwoAxisAction as if they are readonly fields. In fact, you could add readonly to their declarations to enforce this and make sure you aren't doing something you shouldn't.

    For reference, here's an example of working and non-working code: https://gist.github.com/pbhogan/97353be30ed96067402a430d18cfd180
     
  23. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    My fields already are readonly. This is what I'm doing:

    Code (csharp):
    1. public abstract class CustomActionSet : PlayerActionSet {
    2.  
    3.    protected PlayerAction CreateAction(string name, BindingSource[] defaultBindings, int hiddenBindings = 0, int lockedBindings = 0, int maxBindings = 0, bool noMouse = false, bool noKeyboard = false) {
    4.        PlayerAction action = CreatePlayerAction("Control." + name);
    5.        action.UserData = new ActionEditSettings(hiddenBindings, lockedBindings);
    6.        BindingListenOptions options = new BindingListenOptions();
    7.        action.ListenOptions = options;
    8.        options.AllowDuplicateBindingsPerSet = false;
    9.        options.IncludeControllers = this.IsForControllers();
    10.        options.IncludeKeys = !(this.IsForControllers() || noKeyboard);
    11.        options.IncludeMouseButtons = !(this.IsForControllers() || noMouse);
    12.        options.IncludeModifiersAsFirstClassKeys = true;
    13.        options.IncludeMouseScrollWheel = false;
    14.        options.IncludeNonStandardControls = true;
    15.        options.IncludeUnknownControllers = false;
    16.        options.MaxAllowedBindings = (uint) ((maxBindings > 0) ? maxBindings : Mathf.Max(defaultBindings.Length, 1));
    17.        options.MaxAllowedBindingsPerType = 0;
    18.        options.RejectRedundantBindings = true;
    19.        options.UnsetDuplicateBindingsOnSet = false;
    20.        options.OnBindingAdded = this.OnBindingAdded;
    21.        this.Do1_RegisterAction(action);
    22.  
    23.        try {
    24.            foreach(BindingSource binding in defaultBindings) {
    25.                action.AddDefaultBinding(binding);
    26.            }
    27.        }
    28.        catch(InControlException argh) {
    29.            Debug.LogException(argh);
    30.        }
    31.  
    32.        return action;
    33.    }
    34.  
    35.    //...
    36.  
    37. }
    Code (csharp):
    1. public class KeyboardActions : CustomActionSet {
    2.  
    3.    public readonly PlayerAction Up; // ((DPadUp)) or ((LeftStickUp)) or [[Up]] or [W/Z]
    4.    public readonly PlayerAction Left; // ((DPadLeft)) or ((LeftStickLeft)) or [[Left]] or [A/Q]
    5.    public readonly PlayerAction Down; // ((DPadDown)) or ((LeftStickDown)) or [[Down]] or [S]
    6.    public readonly PlayerAction Right; // ((DPadRight)) or ((LeftStickRight)) or [[Right]] or [D]
    7.  
    8.    public readonly PlayerAction Escape; // [[Escape]] or [E] or [RightClick]
    9.    public readonly PlayerAction Player1JumpKeyboard; // [[Space]] or [...]
    10.    public readonly PlayerAction Player2JumpKeyboard; // [Enter] or [...]
    11.    public readonly PlayerAction Player1JumpMouse; // [[LeftClick]]
    12.    public readonly PlayerAction Player2JumpMouse; // [MouseButton5]
    13.    public readonly PlayerAction RestartLevel; // [R] or [...]
    14.    public readonly PlayerAction ExitLevel; // [F] or [...]
    15.    public readonly PlayerAction FullScreen; // [F1]
    16.  
    17.    public readonly PlayerTwoAxisAction Navigation; // (I no longer use this)
    18.  
    19.    public KeyboardActions() {
    20.        this.Up = this.CreateAction("Up", new BindingSource[] { new DeviceBindingSource(InputControlType.DPadUp), new DeviceBindingSource(InputControlType.LeftStickUp), new KeyBindingSource(Key.UpArrow), new KeyBindingSource(Key.W) }, hiddenBindings: 2, lockedBindings: 1);
    21.        this.Left = this.CreateAction("Left", new BindingSource[] { new DeviceBindingSource(InputControlType.DPadLeft), new DeviceBindingSource(InputControlType.LeftStickLeft), new KeyBindingSource(Key.LeftArrow), new KeyBindingSource(Key.A) }, hiddenBindings: 2, lockedBindings: 1);
    22.        this.Down = this.CreateAction("Down", new BindingSource[] { new DeviceBindingSource(InputControlType.DPadDown), new DeviceBindingSource(InputControlType.LeftStickDown), new KeyBindingSource(Key.DownArrow), new KeyBindingSource(Key.S) }, hiddenBindings: 2, lockedBindings: 1);
    23.        this.Right = this.CreateAction("Right", new BindingSource[] { new DeviceBindingSource(InputControlType.DPadRight), new DeviceBindingSource(InputControlType.LeftStickRight), new KeyBindingSource(Key.RightArrow), new KeyBindingSource(Key.D) }, hiddenBindings: 2, lockedBindings: 1);
    24.  
    25.        this.Escape = this.CreateAction("Escape", new BindingSource[] { new KeyBindingSource(Key.Escape), new KeyBindingSource(Key.E), new MouseBindingSource(Mouse.RightButton) }, lockedBindings: 1);
    26.        this.Player1JumpKeyboard = this.CreateAction("Player1JumpKeyboard", new BindingSource[] { new KeyBindingSource(Key.Space) }, maxBindings: 2, lockedBindings: 1, noMouse: true);
    27.        this.Player2JumpKeyboard = this.CreateAction("Player2JumpKeyboard", new BindingSource[] { new KeyBindingSource(Key.PadEnter) }, maxBindings: 2, lockedBindings: 1, noMouse: true);
    28.        this.Player1JumpMouse = this.CreateAction("Player1JumpMouse", new BindingSource[] { new MouseBindingSource(Mouse.LeftButton) }, lockedBindings: 1, noKeyboard: true);
    29.        this.Player2JumpMouse = this.CreateAction("Player2JumpMouse", new BindingSource[] { new MouseBindingSource(Mouse.Button5) }, noKeyboard: true);
    30.        this.RestartLevel = this.CreateAction("RestartLevel", new BindingSource[] { new KeyBindingSource(Key.R) }, maxBindings: 2);
    31.        this.ExitLevel = this.CreateAction("ExitLevel", new BindingSource[] { new KeyBindingSource(Key.F) }, maxBindings: 2);
    32.        this.FullScreen = this.CreateAction("FullScreen", new BindingSource[] { new KeyBindingSource(Key.F1) });
    33.  
    34.        this.Navigation = new PlayerTwoAxisAction(this.Left, this.Right, this.Down, this.Up); // (I no longer use this)
    35.    }
    36.  
    37. }
     
  24. WelcomeGamesA

    WelcomeGamesA

    Joined:
    Oct 24, 2016
    Posts:
    2
    I am sorry to say i was a very disappointed with the product as i was expecting a similar system to what is available on the unreal engine which in my opinion is the best input programming system i have come across. Its simplicity in its self you have a built in input management editor that allows you to program any input device on the market in a mater of minuets saving time without the hassle of coding a fantastic system I hope someone brings out a similar product for unity sometime perhaps this product might be the start of such a project.
     
  25. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    This:
    Code (csharp):
    1.  
    2. this.Navigation = new PlayerTwoAxisAction(this.Left, this.Right, this.Down, this.Up);
    3.  
    Should be:
    Code (csharp):
    1.  
    2. this.Navigation = CreateTwoAxisPlayerAction(this.Left, this.Right, this.Down, this.Up);
    3.  
     
  26. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    Oh... it was just a stupid mistake in fact. xD Thanks.

    I have another question : Is there a way to set dead zones for all devices sticks once and for all? Or do I have to execute the following code each time InputManager.Devices changes?

    Code (csharp):
    1. foreach(InputDevice device in InputManager.Devices) {
    2.     device.LeftStick.LowerDeadZone = JOYSTICK_DEAD_ZONE;
    3. }
     
  27. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    Yes, you currently have to set it on all devices explicitly.
     
  28. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    Ok, thanks.
     
  29. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    Hi, I'm back with a new question: On Android, touching the screen with two fingers at once seems to be equivalent to Escape; Is this a InControl feature or a Unity feature? can I disable it?
     
  30. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    I have no idea. I've not come across it on Android, maybe it's a vendor-specific feature or a system setting. Maybe check if it happens in a blank project, or in other apps.

    It's not an InControl thing by any means and I doubt it's a Unity thing.
     
  31. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    Ok, that's annoying...It doesn't seem to work this way in the other apps I checked (I don't have a lot, I don't use my mobile that much), and I couldn't find anything about that in Unity... well, I'm just gonna disable the escape key (and all other keys) on mobile platforms, and probably add an option to enable them, in case the user somehow connects a keyboard/controller.
     
  32. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    324
    Any reason why Select/Start are not bound to their respective counterparts on PS4(Options=Start) and XBone(View=Select/Menu=Start)?

    Would be nice to rely on these rather than adding 4 different bindings for Back, View, Menu, Options + Select & Start.
     
    yeisonChipud likes this.
  33. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    Because those are not the names of those buttons. Sometimes it may seem obvious which button is "start" and which is "select" but some controllers have more than two buttons (so which two are start and select?) and some only have one, or may have two but only one is functional. You might think of them as start and select but another person who grew up only with PlayStation controllers might consider them something else. That's why those controls are not considered standard and follow the convention of being named whatever the platform/vendor names them like every other non-standard control.

    They are all aliased to the standard control InputControlType.Command which you could consider a menu/pause button. There is only one alias like this because, again, not every controller has two. Following the conventions in InControl, if you want to do non-standard stuff for specific controllers and restrict your users to a specific list of supported controllers, you need to do it explicitly. InControl will not let you do it by accident.
     
    yeisonChipud and shawnblais like this.
  34. yeisonChipud

    yeisonChipud

    Joined:
    Sep 19, 2016
    Posts:
    1
    Hi, I have a problem with an Unknown InputDevice. In dont know how Unknown InputDevice works, or even if actually Unknown InputDevice works. Im wondering if I can become an Unknown InputDevice in a "Known InputDevice" because acording to the next code:

    if (JoinButtonWasPressedOnDevice( inputDevice ))
    {
    // print(inputDevice);
    if (ThereIsNoPlayerUsingDevice( inputDevice ))
    {
    CreatePlayer( inputDevice );
    }
    }
    }

    JoinButtonWasPressedOnDevice( inputDevice ) only works with "Known InputDevice".

    When I was Exploring a little the code, I try something like this:

    if (JoinButtonWasPressedOnDevice( inputDevice ) == false)
    {
    // print(inputDevice);
    if (ThereIsNoPlayerUsingDevice( inputDevice ))
    {
    CreatePlayer( inputDevice );
    }
    }
    }
    Then, Actions from this Unknown InputDevice works perfectly. Seems that JoinButtonWasPressedOnDevice( inputDevice ) returns a bool, asking if the current device is Know or Unknow. For this reason I want to know if is posible to works with Know and Unknow devices ( both at the saeme time ) or if I really need to get a joystick or device "Know". Furthermore I want to know if is posible to get a little list with Know joyksticks or devices, for example ... a Ps3 controller. Thanks and sorry but my bad english.
     
  35. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    Unknown devices generally don't work because their mappings aren't known. InControl makes them available with generic button and analog controls but cannot tell you what they are, so it's not very useful.

    Making a device known is a matter of creating a mapping profile.

    Unfortunately adding support for controllers involves actually having them physically present to create and test the mappings. You can create the profile yourself if you like. It’s not very hard but takes a little bit of manual trial and error.

    First, you’ll want to fire up the TestInputManager scene on device (or run it in the Unity editor for desktop systems). The controller should come up as an unknown controller if InControl doesn’t support it and will list a bunch of analogs and buttons. Fiddling with the controls will show you which inputs respond as which buttons and analogs.

    Then copy one of the profiles in InControl/Source/Unity/DeviceProfiles and adjust it accordingly. Other than the button/analog mappings, you have to make sure the supported platforms property has the right string in it and the joystick names property should have the name as it appears to Unity in GetJoystickNames(). This also shows up in TestInputManager.

    Similarly, if you want to add support for the native input module, the profiles are located in InControl/Source/Native/DeviceProfiles although the device matching is usually based on vendor ID and product ID values instead of names. Just look at one of the other native profiles and you’ll see what I mean. These will also show up on screen.

    To see which devices are supported you can look at the profiles in the profiles folder.
     
  36. GuillaumeAzkoaga

    GuillaumeAzkoaga

    Joined:
    Sep 7, 2016
    Posts:
    1
    Greetings!
    First, thanks so much for the plugin. It really has everything we could need.
    But I runned into a problem: for some reason, my Xbox 360 controller isn't detected. I get the following log message:
    Device 1 with name "Controller (XBOX 360 For Windows)" does not match any known profiles.

    I followed some of the advices given here and went ahead to add the device name into the Xbox360WinProfile.cs
    (I also went a bit deeper and looked the data about the corresponding profile in the UnityInputDeviceProfile.cs and I saw the variable isSupportedOnThisPlatform equal to false).

    I am working using the latest Unity version and Win10.
    Am I missing something important? Not even the examples are working.

    Thanks for your help
     
  37. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    I got your e-mail on this and will reply to that.
     
  38. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,521
    Can you add iCade support for Apple TV4?

    I like to use the iCace compatible controllers with Apple TV.
    http://www.8bitdo.com

    I know they can work because I tried them on Provenance and it works on Apple TV with the 8bitdo SNES controller
    GitHub - jasarien/Provenance: An iOS/tvOS Frontend for multiple ...

    Right now iCade is only supported on iOS.

    I looked at enabling the compilation of the pluging on apple tv and checked the box in unity.
    I think a few more steps are needed to get it to work.

    Can you help?
    Thanks
     
    Last edited: Sep 14, 2017
  39. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    I think there would be more than a few steps involved. The code will not work as-is with tvOS as there are API differences and it would need to be customized for it. tvOS != iOS

    I'm not inclined to support iCade. It's a horrible hack with really bad responsiveness and I have no idea why it's still a thing now that we've had MFi controllers for years, yet there's a tiny vocal minority of players that continue to ask for it. I think it's a reasonable response to say "sorry, only MFi controllers are supported on tvOS", a platform on which gaming has largely failed anyway.

    Provenance seems to have a fairly involved implementation that has custom support for different types of iCade controllers—no idea why they need that as all iCade controllers are supposed to work the same. Maybe layouts differ. In any event, the code is all here: https://github.com/jasarien/Provenance/tree/master/Provenance/Controller/iCade

    From a quick glance, some specific differences seem to be in: https://github.com/jasarien/Provenance/blob/master/Provenance/Controller/iCade/iCadeReaderView.m
    where insertText is not supported on tvOS but that is what I use on iOS and they use some other tvOS supported delegates instead.

    You might be able to try to copy and modify the iOS version to work on tvOS accordingly. Additionally, you'd need to either add the plugin files (InControl/Plugins/iOS) manually to the Xcode project or modify InControl/Editor/iOS/ICadePostProcessor.cs to inject the files into the project.
     
  40. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    Hi, is there an easy way to know if the last key pressed was on a controller or on keyboard+mouse?
    I want to adapt my tutorials accordingly.
     
  41. PixelEnvision

    PixelEnvision

    Joined:
    Feb 7, 2012
    Posts:
    513
    Hi @pbhogan

    I'm using InControl in my current game and it's working perfectly, thank you for the great asset.

    I wanted to ask if you would consider adding an option to InControlInputModule to allow "WasReleased" checks for Submit & Cancel states instead of "IsPressed"?

    On tvOS (and probably on some other platforms too) when you click the remote, Action1 stays ON until users let the click go. When I use a UI button click that invokes native tvOS UI (text input, permission dialog, etc.), if user didn't quickly let go (almost always the case) before the UI, Action1 still reports ON even when the focus returns to the game.

    And because of that first click won't be registered. It returns to Action1 OFF state, and only then next click works as expected.

    Easiest way that I can find to fix that was changing Submit & Cancel states in InControlInputModule to use WasReleased checks, which fixes the issue.

    I can of course use that as it is but I wanted to ask as that might benefit others too, with additional benefit of not to worry updating my hack with every new release in the future. :)

    Thanks!
     
  42. PixelEnvision

    PixelEnvision

    Joined:
    Feb 7, 2012
    Posts:
    513
    @pbhogan

    Following my previous post, I've found a code that's commented out at line 227 of InControlInputModule.cs for SubmitWasReleased check.

    Enabling that line allowed me to use PointerUp event with Event Trigger script on my buttons, which is better than my previous hack as it won't break button's down state.

    My I ask why was it commented out? are there any problems with it I should aware of?
     
  43. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
  44. pbhogan

    pbhogan

    Joined:
    Aug 17, 2012
    Posts:
    377
    The event system code is really gnarly as Unity keeps changing it even on minor point releases so it's always a tightrope walk just getting it to work at all across Unity versions. So I don't recall exactly why that was commented out, but it might have broke something on desktop builds (it seems kind of mouse related?) or possibly I was just trying to match the behavior of Unity's StandaloneInputModule more accurately. I checked and StandaloneInputModule doesn't have it so maybe it was something I was experimenting with at some point and decided not to risk breaking something. If it works for your purpose, great. Maybe wrap it in a UNITY_TVOS for safety if you're doing cross platform builds.
     
  45. PixelEnvision

    PixelEnvision

    Joined:
    Feb 7, 2012
    Posts:
    513
    Thanks for the reply, and the suggestion. Nice thinking, I'll do that... :)
     
  46. SeigneurNecron

    SeigneurNecron

    Joined:
    Sep 2, 2016
    Posts:
    10
    This solution only works if you are using one action set. In my game, there is a local co-op mode in which one player can use the keyboard while the other player can use a controller (or they can use two controllers a well). For this reason, I have a KeyboardActionSet and to instances of ControllerActionSet, so I can check actions separately for each player.

    So I was looking for something like InputManager.LastInputType or ActionSet.LastInputTime. I could add my own ActionSet.AnyActionWasPressed, with a for loop on actions, and register the last input type myself, but I wanted to be sure I was not doing something you've already done better in InControl.
     
  47. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,521
    Thanks for the pointers, The reason why iCade is still a thing is that apple has certified only a handful controllers for MFI on apple TV, and the Retro styled SNES controllers and Arcade sticks don't work with MFI.
    Basically MFI is very limited to what devices they support and it's a shame as the hardware of Apple TV has great gaming potential especially with the Apple TV 4k.


    As far as responsiveness. the bluetooth SNES controller from 8bitdo works very responsive with the iCade mode of provenance.


    None of the 8bitdo controller are MFI and they never will be, so yea there is a reason to use iCade.
     
    Last edited: Sep 27, 2017
  48. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,521
    I'm sharing my steps here, so anyone maybe able to contribute to getting iCade to work on Apple TV4
    Im trying to get it working and so far its not stopping building, but no input from the controller yet.
    Screen Shot 2017-09-26 at 23.09.48.png
    First I check the tvOS boxes on ICadeManager.h , ICadeManager.m, ICadeReaderView.h , ICadeReaderView.m

    And I copied them manually to Xcode because I did not yet have time to look into the post processing script.

    And changed
    #if UNITY_IOS || UNITY_EDITOR
    To
    #if UNITY_TVOS || UNITY_EDITOR
    Maybe it should be like this but I was not sure if that would work:
    #if UNITY_TVOS || UNITY_IOS || UNITY_EDITOR

    in the following files:
    ICadeDevice.cs
    ICadeDeviceManager.cs
    ICadeNative.cs
    ICadeState.cs

    Then added this lines to InputManager.cs
    Code (CSharp):
    1. #if UNITY_TVOS
    2.             if (EnableICade)
    3.             {
    4.                 ICadeDeviceManager.Enable();
    5.             }
    6. #endif

    Code (CSharp):
    1.  
    2.         public static bool CheckPlatformSupport( ICollection<string> errors )
    3.         {
    4.             return Application.platform == RuntimePlatform.tvOS;
    5.             //return Application.platform == RuntimePlatform.IPhonePlayer;
    6.         }
    7.  

    And changed the build target to tv
    Code (CSharp):
    1. #if UNITY_5 || UNITY_5_6_OR_NEWER
    2.             if (buildTarget == BuildTarget.tvOS)
    3.             //if (buildTarget == BuildTarget.iOS)

    And changed the file ICadeReaderview.m
    Code (CSharp):
    1.  
    2. #import "ICadeReaderView.h"
    3. #import <UIKit/UIKit.h>
    4.  
    5. static const char *ON_STATES  = "wdxayhujikol"; //wdxazhujikol for German keyboard layout
    6. static const char *OFF_STATES = "eczqtrfnmpgv"; //ecyqtrfnmpgv for German keyboard layout
    7.  
    8. @interface ICadeReaderView()
    9.  
    10. - (void) willResignActive;
    11. - (void) didBecomeActive;
    12.  
    13. @end
    14.  
    15.  
    16. @implementation ICadeReaderView
    17.  
    18. @synthesize state = _state, delegate = _delegate, active;
    19.  
    20.  
    21. - (id) initWithFrame: (CGRect) frame
    22. {
    23.     self = [super initWithFrame: frame];
    24.     inputView = [[UIView alloc] initWithFrame: CGRectZero];
    25.  
    26.     [[NSNotificationCenter defaultCenter]
    27.         addObserver: self
    28.         selector: @selector(willResignActive)
    29.         name:UIApplicationWillResignActiveNotification
    30.         object: nil
    31.     ];
    32.  
    33.     [[NSNotificationCenter defaultCenter]
    34.         addObserver: self
    35.         selector: @selector(didBecomeActive)
    36.         name: UIApplicationDidBecomeActiveNotification
    37.         object: nil
    38.      ];
    39.  
    40.     return self;
    41. }
    42.  
    43.  
    44. - (void) dealloc
    45. {
    46.     [[NSNotificationCenter defaultCenter]
    47.         removeObserver: self
    48.         name: UIApplicationWillResignActiveNotification
    49.         object: nil
    50.     ];
    51.  
    52.     [[NSNotificationCenter defaultCenter]
    53.         removeObserver: self
    54.         name: UIApplicationDidBecomeActiveNotification
    55.         object: nil
    56.     ];
    57.  
    58.     #if !__has_feature(objc_arc)
    59.     [super dealloc];
    60.     #endif
    61. }
    62.  
    63.  
    64. - (void) willResignActive
    65. {
    66.     if (self.active)
    67.     {
    68.         [self resignFirstResponder];
    69.     }
    70. }
    71.  
    72.  
    73. - (void) didBecomeActive
    74. {
    75.     if (self.active)
    76.     {
    77.         [self becomeFirstResponder];
    78.     }
    79. }
    80.  
    81.  
    82. - (BOOL) canBecomeFirstResponder
    83. {
    84.     return YES;
    85. }
    86.  
    87.  
    88. - (void)setActive:(BOOL)value {
    89.     if (active == value) {
    90.         if (value) {
    91.             [self resignFirstResponder];
    92.         } else {
    93.             return;
    94.         }
    95.     }
    96.  
    97.     active = value;
    98.     if (active) {
    99.         if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) {
    100.             [self becomeFirstResponder];
    101.         }
    102.     } else {
    103.         [self resignFirstResponder];
    104.     }
    105. }
    106.  
    107. - (UIView*) inputView {
    108.     return inputView;
    109. }
    110.  
    111. - (void)setDelegate:(id<ICadeEventDelegate>)delegate {
    112.     _delegate = delegate;
    113.     if (!_delegate) return;
    114.  
    115.     _delegateFlags.stateChanged = [_delegate respondsToSelector:@selector(stateChanged:)];
    116.     _delegateFlags.buttonDown = [_delegate respondsToSelector:@selector(buttonDown:)];
    117.     _delegateFlags.buttonUp = [_delegate respondsToSelector:@selector(buttonUp:)];
    118. }
    119.  
    120. #pragma mark -
    121. #pragma mark UIKeyInput Protocol Methods
    122.  
    123. - (BOOL)hasText {
    124.     return NO;
    125. }
    126.  
    127. - (void)insertText:(NSString *)text {
    128.     // does not to work on tvOS, use keyCommands + keyPressed instead
    129. }
    130.  
    131. - (void)deleteBackward {
    132.     // This space intentionally left blank to complete protocol
    133. }
    134.  
    135. #pragma mark - keys
    136.  
    137. - (NSArray * )keyCommands {
    138.     NSMutableArray *keys = [NSMutableArray array];
    139.  
    140.     int numberOfStates = (int)(strlen(ON_STATES)+strlen(OFF_STATES));
    141.     char states[numberOfStates+1]; //+1 for crash on release
    142.     strcpy(states,ON_STATES);
    143.     strcat(states,OFF_STATES);
    144.  
    145.     for (int i=0; i<numberOfStates; i++) {
    146.         UIKeyCommand *keyCommand = [UIKeyCommand keyCommandWithInput: [NSString stringWithFormat:@"%c" , states[i]] modifierFlags: 0 action: @selector(keyPressed:)];
    147.         [keys addObject:keyCommand];
    148.     }
    149.  
    150.     return keys;
    151. }
    152.  
    153. - (void)keyPressed:(UIKeyCommand *)keyCommand {
    154.     char ch = [keyCommand.input characterAtIndex:0];
    155.     char *p = strchr(ON_STATES, ch);
    156.     bool stateChanged = false;
    157.     if (p) {
    158.         long index = p-ON_STATES;
    159.         _state |= (1 << index);
    160.         stateChanged = true;
    161.         if (_delegateFlags.buttonDown) {
    162.             [_delegate buttonDown:(1 << index)];
    163.         }
    164.     } else {
    165.         p = strchr(OFF_STATES, ch);
    166.         if (p) {
    167.             long index = p-OFF_STATES;
    168.             _state &= ~(1 << index);
    169.             stateChanged = true;
    170.             if (_delegateFlags.buttonUp) {
    171.                 [_delegate buttonUp:(1 << index)];
    172.             }
    173.         }
    174.     }
    175.  
    176.     if (stateChanged && _delegateFlags.stateChanged) {
    177.         [_delegate stateChanged:_state];
    178.     }
    179.  
    180.     static int cycleResponder = 0;
    181.     if (++cycleResponder > 20) {
    182.         // necessary to clear a buffer that accumulates internally
    183.         cycleResponder = 0;
    184.         [self resignFirstResponder];
    185.         [self becomeFirstResponder];
    186.     }
    187. }
    188.  
    189. @end
    190.  
    191.  


    I think there is something missing in the last file, it seems you have all the ICade names vs iCade in the others, I tried to make them all with capital I

    Just to confirm I checked if this change to ICadeReaderview.m worked in iPhone and it does, just not yet on Apple TV
     
    Last edited: Sep 27, 2017
  49. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,521
    icade apple tv.jpg

    IT WORKS!!!
    ICadeDeviceManager.cs line 120 was the last missing part.
     
  50. PixelEnvision

    PixelEnvision

    Joined:
    Feb 7, 2012
    Posts:
    513
    @pbhogan

    I've tried upgrading my project to Unity 2017.1.1p3 (from 5.6.3p4) and Unity UI is not working on the iOS devices but still working fine on editor.

    I'm using InControlInputModule in my EventSystem, if I replace it with Standalone Input Module it works.

    Any thoughts?

    Edit: Updating line 82, 159 & 175 with (UNITY_5 || UNITY_5_6_OR_NEWER) seems to fix it.

    Edit2: InControlInputModuleEditor.cs line 1 also needs to be updated.
     
    Last edited: Oct 2, 2017