Controller Input in Unity for Different OS's
Input in unity is shit.
So, my problem is that I'm working on projects where I use Mac OSX and my partner uses Windows. Thus the bindings for a XBOX360 controller are different.
Windows
A - joystick button 0
Mac OSX
A - joystick button 16
I'd like to know how you guys deal with an issue like this. I've seen some assets on the asset store but they cost money and they don't explicitly say they handle this issue.
I've thought of just mapping each OS in the InputManager and then using preprocessor directives to choose the string I use in Input.GetAxis(<string chosen>). This seems tedious when it comes to say more than 1 controller.
http://wiki.unity3d.com/index.php?title=Xbox360Controller
has an InputManger file that maps 4 controllers but it's for Windows only.
Any suggestions? Asset?
Thanks
So, my problem is that I'm working on projects where I use Mac OSX and my partner uses Windows. Thus the bindings for a XBOX360 controller are different.
Windows
A - joystick button 0
Mac OSX
A - joystick button 16
I'd like to know how you guys deal with an issue like this. I've seen some assets on the asset store but they cost money and they don't explicitly say they handle this issue.
I've thought of just mapping each OS in the InputManager and then using preprocessor directives to choose the string I use in Input.GetAxis(<string chosen>). This seems tedious when it comes to say more than 1 controller.
http://wiki.unity3d.com/index.php?title=Xbox360Controller
has an InputManger file that maps 4 controllers but it's for Windows only.
Any suggestions? Asset?
Thanks
Comments
For Montez (and my games going forward) we will be using a input mapping system.
So perhaps you already are doing this but it is generally a good idea to abstract your input into one class. You would have a class named CustomInput and you would have a public static method like Jump() that checks if the jump button is being pressed. This is cool cause you can add multiple jump inputs and stuff like that.
For example :
To take this further you can store the keycode in a variable. Which will allow you to change the variable at runtime.
This isn't necessarily the best solution but so far it has worked.
*Edit*
For axis you can have a string variable that refers to a InputManager axis. You would have to define every axis you would ever want to use in unitys InputManager and then you can just change that string variable at runtime.
There are actually even more complications than just differing mappings on different platforms. For example:
- Dual shock controllers have different mappings too (also on a per platform basis), so do MOGAs, and so do most other controller brands.
- Different controllers treat the triggers differently. Some treat them as buttons, some treat them as separate axes, some treat both triggers as a single axis.
- Triggers (and possibly other axes) are also treated differently depending on which driver you use. Ever noticed that the standard 360 triggers are a single axis in Unity? ie. holding both is the same as holding nothing at all? Well, the way you solve that is by using XInput instead of DirectInput. Not all controllers support XInput.
- Vibration is not supported (unless you use XInput, which again, won't work for all controllers).
- Different controllers treat DPads differently. Some treat them as buttons, some treat them as axes.
- There is no convenient way to figure what type of controller is attached. All you get is a string name, so you have to do some pattern matching.
- Joystick IDs don't always align with XInput player ids. You usually have to do some finagling to make sure the controller that is lighting up as player 1 is also player 1 in the game.
This is a ridiculous amount of stuff to have to care about in order to get gamepads to "just work" in a commercial game engine. Anyway, rant over. What can we do about it? The easiest solution is probably some kind of package, I've also heard good things about InControl.
I have a custom solution that we use, which evolved from the solution that was used at Tasty Poison Games.
Basically the idea is to have a standard InputManager.asset file that contains all your InputManager settings ready to be copied into any project. The file should contain axes set up for lots of axes on lots of joysticks. Eg. "Joystick1Axis1", "Joystick1Axis2", "Joystick1Axis3" etc. You probably want at least 8 axes for at least 4 joysticks.
Then you store mappings for your buttons and axes on a per gamepad and per OS basis. Eg. "XBOX360Windows". You could easily serialize these mappings somewhere, but we just hardcode them into a dictionary in static memory.
Then finally, you can wrap everything up in a nice static Gamepad class that will check your runtime platform and your controller names and match you with the correct mapping. You can add more complex functionality when you need it. Our gamepad API ends up looking something like this:
Gamepad.GetGamepad(0).GetButtonDown(Gamepad.Button.A);
I'm going to check out InControl and see how that works otherwise I may just end up writing a small custom solution similar to the one @squidcor mentioned.
It's a really expansive asset. Had PS3 and XBOX360 controllers connected simultaneously and they just worked in harmony.
https://www.assetstore.unity3d.com/en/#!/content/21676
When I chimed in about asset store opportunities, it got me thinking about how I would solve this mess - by going to the native level directly. And it seems that exactly what Rewired does - which gives you support for hot swapping a various other features I wasn't able to spot in InControl. ;)
So, I just stuck with the free open source InControl and I recently made small teeny weeny changes:
When the InControlManager component is marked Don't Destroy On Load, it will destroy any other instances of the object when navigating to another scene.
Due to the controller assignment problem, I also included a Join Screen example to the project.
The join screen implementation is the one we're using in Raptor Polo and Boxer.
In terms of code, I never write against these libraries directly, always proxy through interface so I can switch out later.