No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AndroidGameController.cs 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #if UNITY_EDITOR || UNITY_ANDROID || PACKAGE_DOCS_GENERATION
  2. using System;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using UnityEngine.InputSystem.Layouts;
  6. using UnityEngine.InputSystem.LowLevel;
  7. using UnityEngine.InputSystem.Android.LowLevel;
  8. using UnityEngine.InputSystem.DualShock;
  9. using UnityEngine.InputSystem.Utilities;
  10. namespace UnityEngine.InputSystem.Android.LowLevel
  11. {
  12. /// <summary>
  13. /// Default state layout for Android game controller.
  14. /// </summary>
  15. [StructLayout(LayoutKind.Sequential)]
  16. public unsafe struct AndroidGameControllerState : IInputStateTypeInfo
  17. {
  18. public const int MaxAxes = 48;
  19. public const int MaxButtons = 220;
  20. public class Variants
  21. {
  22. public const string Gamepad = "Gamepad";
  23. public const string Joystick = "Joystick";
  24. public const string DPadAxes = "DpadAxes";
  25. public const string DPadButtons = "DpadButtons";
  26. }
  27. internal const uint kAxisOffset = sizeof(uint) * (uint)((MaxButtons + 31) / 32);
  28. public static FourCC kFormat = new FourCC('A', 'G', 'C', ' ');
  29. [InputControl(name = "dpad", layout = "Dpad", bit = (uint)AndroidKeyCode.DpadUp, sizeInBits = 4, variants = Variants.DPadButtons)]
  30. [InputControl(name = "dpad/up", bit = (uint)AndroidKeyCode.DpadUp, variants = Variants.DPadButtons)]
  31. [InputControl(name = "dpad/down", bit = (uint)AndroidKeyCode.DpadDown, variants = Variants.DPadButtons)]
  32. [InputControl(name = "dpad/left", bit = (uint)AndroidKeyCode.DpadLeft, variants = Variants.DPadButtons)]
  33. [InputControl(name = "dpad/right", bit = (uint)AndroidKeyCode.DpadRight, variants = Variants.DPadButtons)]
  34. [InputControl(name = "buttonSouth", bit = (uint)AndroidKeyCode.ButtonA, variants = Variants.Gamepad)]
  35. [InputControl(name = "buttonWest", bit = (uint)AndroidKeyCode.ButtonX, variants = Variants.Gamepad)]
  36. [InputControl(name = "buttonNorth", bit = (uint)AndroidKeyCode.ButtonY, variants = Variants.Gamepad)]
  37. [InputControl(name = "buttonEast", bit = (uint)AndroidKeyCode.ButtonB, variants = Variants.Gamepad)]
  38. [InputControl(name = "leftStickPress", bit = (uint)AndroidKeyCode.ButtonThumbl, variants = Variants.Gamepad)]
  39. [InputControl(name = "rightStickPress", bit = (uint)AndroidKeyCode.ButtonThumbr, variants = Variants.Gamepad)]
  40. [InputControl(name = "leftShoulder", bit = (uint)AndroidKeyCode.ButtonL1, variants = Variants.Gamepad)]
  41. [InputControl(name = "rightShoulder", bit = (uint)AndroidKeyCode.ButtonR1, variants = Variants.Gamepad)]
  42. [InputControl(name = "start", bit = (uint)AndroidKeyCode.ButtonStart, variants = Variants.Gamepad)]
  43. [InputControl(name = "select", bit = (uint)AndroidKeyCode.ButtonSelect, variants = Variants.Gamepad)]
  44. public fixed uint buttons[(MaxButtons + 31) / 32];
  45. [InputControl(name = "dpad", layout = "Dpad", offset = (uint)AndroidAxis.HatX * sizeof(float) + kAxisOffset, format = "VEC2", sizeInBits = 64, variants = Variants.DPadAxes)]
  46. [InputControl(name = "dpad/right", offset = 0, bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=0,clampMax=1", variants = Variants.DPadAxes)]
  47. [InputControl(name = "dpad/left", offset = 0, bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=-1,clampMax=0,invert", variants = Variants.DPadAxes)]
  48. [InputControl(name = "dpad/down", offset = ((uint)AndroidAxis.HatY - (uint)AndroidAxis.HatX) * sizeof(float), bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=0,clampMax=1", variants = Variants.DPadAxes)]
  49. [InputControl(name = "dpad/up", offset = ((uint)AndroidAxis.HatY - (uint)AndroidAxis.HatX) * sizeof(float), bit = 0, sizeInBits = 32, format = "FLT", parameters = "clamp=3,clampConstant=0,clampMin=-1,clampMax=0,invert", variants = Variants.DPadAxes)]
  50. [InputControl(name = "leftTrigger", offset = (uint)AndroidAxis.Brake * sizeof(float) + kAxisOffset, parameters = "clamp=1,clampMin=0,clampMax=1.0", variants = Variants.Gamepad)]
  51. [InputControl(name = "rightTrigger", offset = (uint)AndroidAxis.Gas * sizeof(float) + kAxisOffset, parameters = "clamp=1,clampMin=0,clampMax=1.0", variants = Variants.Gamepad)]
  52. [InputControl(name = "leftStick", variants = Variants.Gamepad)]
  53. [InputControl(name = "leftStick/y", variants = Variants.Gamepad, parameters = "invert")]
  54. [InputControl(name = "leftStick/up", variants = Variants.Gamepad, parameters = "invert,clamp=1,clampMin=-1.0,clampMax=0.0")]
  55. [InputControl(name = "leftStick/down", variants = Variants.Gamepad, parameters = "invert=false,clamp=1,clampMin=0,clampMax=1.0")]
  56. ////FIXME: state for this control is not contiguous
  57. [InputControl(name = "rightStick", offset = (uint)AndroidAxis.Z * sizeof(float) + kAxisOffset, sizeInBits = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z + 1) * sizeof(float) * 8, variants = Variants.Gamepad)]
  58. [InputControl(name = "rightStick/x", variants = Variants.Gamepad)]
  59. [InputControl(name = "rightStick/y", offset = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z) * sizeof(float), variants = Variants.Gamepad, parameters = "invert")]
  60. [InputControl(name = "rightStick/up", offset = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z) * sizeof(float), variants = Variants.Gamepad, parameters = "invert,clamp=1,clampMin=-1.0,clampMax=0.0")]
  61. [InputControl(name = "rightStick/down", offset = ((uint)AndroidAxis.Rz - (uint)AndroidAxis.Z) * sizeof(float), variants = Variants.Gamepad, parameters = "invert=false,clamp=1,clampMin=0,clampMax=1.0")]
  62. public fixed float axis[MaxAxes];
  63. public FourCC format
  64. {
  65. get { return kFormat; }
  66. }
  67. public AndroidGameControllerState WithButton(AndroidKeyCode code, bool value = true)
  68. {
  69. fixed(uint* buttonsPtr = buttons)
  70. {
  71. if (value)
  72. buttonsPtr[(int)code / 32] |= 1U << ((int)code % 32);
  73. else
  74. buttonsPtr[(int)code / 32] &= ~(1U << ((int)code % 32));
  75. }
  76. return this;
  77. }
  78. public AndroidGameControllerState WithAxis(AndroidAxis axis, float value)
  79. {
  80. fixed(float* axisPtr = this.axis)
  81. {
  82. axisPtr[(int)axis] = value;
  83. }
  84. return this;
  85. }
  86. }
  87. // See https://developer.android.com/reference/android/view/InputDevice.html for input source values
  88. internal enum AndroidInputSource
  89. {
  90. Keyboard = 257,
  91. Dpad = 513,
  92. Gamepad = 1025,
  93. Touchscreen = 4098,
  94. Mouse = 8194,
  95. Stylus = 16386,
  96. Trackball = 65540,
  97. Touchpad = 1048584,
  98. Joystick = 16777232
  99. }
  100. [Serializable]
  101. internal struct AndroidDeviceCapabilities
  102. {
  103. public string deviceDescriptor;
  104. public int productId;
  105. public int vendorId;
  106. public bool isVirtual;
  107. public AndroidAxis[] motionAxes;
  108. public AndroidInputSource inputSources;
  109. public string ToJson()
  110. {
  111. return JsonUtility.ToJson(this);
  112. }
  113. public static AndroidDeviceCapabilities FromJson(string json)
  114. {
  115. if (json == null)
  116. throw new ArgumentNullException(nameof(json));
  117. return JsonUtility.FromJson<AndroidDeviceCapabilities>(json);
  118. }
  119. public override string ToString()
  120. {
  121. return
  122. $"deviceDescriptor = {deviceDescriptor}, productId = {productId}, vendorId = {vendorId}, isVirtual = {isVirtual}, motionAxes = {(motionAxes == null ? "<null>" : String.Join(",", motionAxes.Select(i => i.ToString()).ToArray()))}, inputSources = {inputSources}";
  123. }
  124. }
  125. }
  126. namespace UnityEngine.InputSystem.Android
  127. {
  128. ////FIXME: This is messy! Clean up!
  129. /// <summary>
  130. /// Gamepad on Android. Comprises all types of gamepads supported on Android.
  131. /// </summary>
  132. /// <remarks>
  133. /// Most of the gamepads:
  134. /// - ELAN PLAYSTATION(R)3 Controller
  135. /// - My-Power CO.,LTD. PS(R) Controller Adaptor
  136. /// (Following tested and work with: Nvidia Shield TV (OS 9); Galaxy Note 20 Ultra (OS 11); Galaxy S9+ (OS 10.0))
  137. /// - Sony Interactive Entertainment Wireless (PS4 DualShock) (Also tested and DOES NOT WORK with Galaxy S9 (OS 8.0); Galaxy S8 (OS 7.0); Xiaomi Mi Note2 (OS 8.0))
  138. /// - Xbox Wireless Controller (Xbox One) (Also tested and works on Samsung Galaxy S8 (OS 7.0); Xiaomi Mi Note2 (OS 8.0))
  139. /// - NVIDIA Controller v01.03/v01.04
  140. /// - (Add more)
  141. /// map buttons in the following way:
  142. /// Left Stick -> AXIS_X(0) / AXIS_Y(1)
  143. /// Right Stick -> AXIS_Z (11) / AXIS_RZ(14)
  144. /// Right Thumb -> KEYCODE_BUTTON_THUMBR(107)
  145. /// Left Thumb -> KEYCODE_BUTTON_THUMBL(106)
  146. /// L1 (Left shoulder) -> KEYCODE_BUTTON_L1(102)
  147. /// R1 (Right shoulder) -> KEYCODE_BUTTON_R1(103)
  148. /// L2 (Left trigger) -> AXIS_BRAKE(23)
  149. /// R2 (Right trigger) -> AXIS_GAS(22)
  150. /// X -> KEYCODE_BUTTON_X(99)
  151. /// Y -> KEYCODE_BUTTON_Y(100)
  152. /// B -> KEYCODE_BUTTON_B(97)
  153. /// A -> KEYCODE_BUTTON_A(96)
  154. /// DPAD -> AXIS_HAT_X(15),AXIS_HAT_Y(16) or KEYCODE_DPAD_LEFT(21), KEYCODE_DPAD_RIGHT(22), KEYCODE_DPAD_UP(19), KEYCODE_DPAD_DOWN(20),
  155. /// Note: On Nvidia Shield Console, L2/R2 additionally invoke key events for AXIS_LTRIGGER, AXIS_RTRIGGER (in addition to AXIS_BRAKE, AXIS_GAS)
  156. /// If you connect gamepad to a phone for L2/R2 only AXIS_BRAKE/AXIS_GAS come. AXIS_LTRIGGER, AXIS_RTRIGGER are not invoked.
  157. /// That's why we map triggers only to AXIS_BRAKE/AXIS_GAS
  158. /// Nvidia Shield also reports KEYCODE_BACK instead of KEYCODE_BUTTON_SELECT, so Options(XboxOne Controller)/View(DualShock)/Select buttons do not work
  159. /// PS4 controller is officially supported from Android 10 and higher (https://playstation.com/en-us/support/hardware/ps4-pair-dualshock-4-wireless-with-sony-xperia-and-android)
  160. /// However, some devices with older OS have fixed PS4 controller support on their drivers this leads to following situation:
  161. /// Some gamepads on Android devices (with same Android number version) might have different mappings
  162. /// For ex., Dualshock, on NVidia Shield Console (OS 8.0) all buttons correctly map according to rules in AndroidGameControllerState
  163. /// when clicking left shoulder it will go to AndroidKeyCode.ButtonL1, rightShoulder -> AndroidKeyCode.ButtonR1, etc
  164. /// But, on Samsung Galaxy S9 (OS 8.0), the mapping is different (Same for Xiaomi Mi Note2 (OS 8.0), Samsung Galaxy S8 (OS 7.0))
  165. /// when clicking left shoulder it will go to AndroidKeyCode.ButtonY, rightShoulder -> AndroidKeyCode.ButtonZ, etc
  166. /// So even though Android version is 8.0 in both cases, Dualshock will only correctly work on NVidia Shield Console
  167. /// It's obvious that this depends on the driver and not Android OS, thus we can only assume Samsung in this case doesn't properly support Dualshock in their drivers
  168. /// While we can do custom mapping for Samsung, we can never now when will they try to update the driver for Dualshock or some other gamepad
  169. /// </remarks>
  170. [InputControlLayout(stateType = typeof(AndroidGameControllerState), variants = AndroidGameControllerState.Variants.Gamepad)]
  171. public class AndroidGamepad : Gamepad
  172. {
  173. }
  174. /// <summary>
  175. /// Generic controller with Dpad axes
  176. /// </summary>
  177. [InputControlLayout(stateType = typeof(AndroidGameControllerState), hideInUI = true,
  178. variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadAxes)]
  179. public class AndroidGamepadWithDpadAxes : AndroidGamepad
  180. {
  181. }
  182. /// <summary>
  183. /// Generic controller with Dpad buttons
  184. /// </summary>
  185. [InputControlLayout(stateType = typeof(AndroidGameControllerState), hideInUI = true,
  186. variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadButtons)]
  187. public class AndroidGamepadWithDpadButtons : AndroidGamepad
  188. {
  189. }
  190. /// <summary>
  191. /// Joystick on Android.
  192. /// </summary>
  193. [InputControlLayout(stateType = typeof(AndroidGameControllerState), variants = AndroidGameControllerState.Variants.Joystick)]
  194. public class AndroidJoystick : Joystick
  195. {
  196. }
  197. /// <summary>
  198. /// A PlayStation DualShock 4 controller connected to an Android device.
  199. /// </summary>
  200. [InputControlLayout(stateType = typeof(AndroidGameControllerState), displayName = "Android DualShock 4 Gamepad",
  201. variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadAxes)]
  202. public class DualShock4GamepadAndroid : DualShockGamepad
  203. {
  204. }
  205. /// <summary>
  206. /// A PlayStation DualShock 4 controller connected to an Android device.
  207. /// </summary>
  208. [InputControlLayout(stateType = typeof(AndroidGameControllerState), displayName = "Android Xbox One Controller",
  209. variants = AndroidGameControllerState.Variants.Gamepad + InputControlLayout.VariantSeparator + AndroidGameControllerState.Variants.DPadAxes)]
  210. public class XboxOneGamepadAndroid : XInput.XInputController
  211. {
  212. }
  213. }
  214. #endif // UNITY_EDITOR || UNITY_ANDROID