Sin descripción
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.

SteamSupport.cs 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #if (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
  2. using System;
  3. using UnityEngine.InputSystem.Layouts;
  4. using UnityEngine.InputSystem.Utilities;
  5. namespace UnityEngine.InputSystem.Steam
  6. {
  7. /// <summary>
  8. /// Adds support for Steam controllers.
  9. /// </summary>
  10. #if UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
  11. public
  12. #else
  13. internal
  14. #endif
  15. static class SteamSupport
  16. {
  17. /// <summary>
  18. /// Wrapper around the Steam controller API.
  19. /// </summary>
  20. /// <remarks>
  21. /// This must be set by user code for Steam controller support to become functional.
  22. /// </remarks>
  23. public static ISteamControllerAPI api
  24. {
  25. get { return s_API; }
  26. set
  27. {
  28. s_API = value;
  29. InstallHooks(s_API != null);
  30. }
  31. }
  32. internal static ISteamControllerAPI GetAPIAndRequireItToBeSet()
  33. {
  34. if (s_API == null)
  35. throw new InvalidOperationException("ISteamControllerAPI implementation has not been set on SteamSupport");
  36. return s_API;
  37. }
  38. internal static SteamHandle<SteamController>[] s_ConnectedControllers;
  39. internal static SteamController[] s_InputDevices;
  40. internal static int s_InputDeviceCount;
  41. internal static bool s_HooksInstalled;
  42. internal static ISteamControllerAPI s_API;
  43. private const int STEAM_CONTROLLER_MAX_COUNT = 16;
  44. /// <summary>
  45. /// Enable support for the Steam controller API.
  46. /// </summary>
  47. public static void Initialize()
  48. {
  49. // We use this as a base layout.
  50. InputSystem.RegisterLayout<SteamController>();
  51. if (api != null)
  52. InstallHooks(true);
  53. }
  54. private static void InstallHooks(bool state)
  55. {
  56. Debug.Assert(api != null);
  57. if (state && !s_HooksInstalled)
  58. {
  59. InputSystem.onBeforeUpdate += OnUpdate;
  60. InputSystem.onActionChange += OnActionChange;
  61. }
  62. else if (!state && s_HooksInstalled)
  63. {
  64. InputSystem.onBeforeUpdate -= OnUpdate;
  65. InputSystem.onActionChange -= OnActionChange;
  66. }
  67. }
  68. private static void OnActionChange(object mapOrAction, InputActionChange change)
  69. {
  70. // We only care about action map activations. Steam has no support for enabling or disabling
  71. // individual actions and also has no support disabling sets once enabled (can only switch
  72. // to different set).
  73. if (change != InputActionChange.ActionMapEnabled)
  74. return;
  75. // See if the map has any bindings to SteamControllers.
  76. // NOTE: We only support a single SteamController on any action map here. The first SteamController
  77. // we find is the one we're doing all the work on.
  78. var actionMap = (InputActionMap)mapOrAction;
  79. foreach (var action in actionMap.actions)
  80. {
  81. foreach (var control in action.controls)
  82. {
  83. var steamController = control.device as SteamController;
  84. if (steamController == null)
  85. continue;
  86. // Yes, there's active bindings to a SteamController on the map. Look through the Steam action
  87. // sets on the controller for a name match on the action map. If we have one, sync the enable/
  88. // disable status of the set.
  89. var actionMapName = actionMap.name;
  90. foreach (var set in steamController.steamActionSets)
  91. {
  92. if (string.Compare(set.name, actionMapName, StringComparison.InvariantCultureIgnoreCase) != 0)
  93. continue;
  94. // Nothing to do if the Steam controller has auto-syncing disabled.
  95. if (!steamController.autoActivateSets)
  96. return;
  97. // Sync status.
  98. steamController.ActivateSteamActionSet(set.handle);
  99. // Done.
  100. return;
  101. }
  102. }
  103. }
  104. }
  105. private static void OnUpdate()
  106. {
  107. if (api == null)
  108. return;
  109. // Update controller state.
  110. api.RunFrame();
  111. // Check if we have any new controllers have appeared.
  112. if (s_ConnectedControllers == null)
  113. s_ConnectedControllers = new SteamHandle<SteamController>[STEAM_CONTROLLER_MAX_COUNT];
  114. var numConnectedControllers = api.GetConnectedControllers(s_ConnectedControllers);
  115. for (var i = 0; i < numConnectedControllers; ++i)
  116. {
  117. var handle = s_ConnectedControllers[i];
  118. // See if we already have a device for this one.
  119. if (s_InputDevices != null)
  120. {
  121. SteamController existingDevice = null;
  122. for (var n = 0; n < s_InputDeviceCount; ++n)
  123. {
  124. if (s_InputDevices[n].steamControllerHandle == handle)
  125. {
  126. existingDevice = s_InputDevices[n];
  127. break;
  128. }
  129. }
  130. // Yes, we do.
  131. if (existingDevice != null)
  132. continue;
  133. }
  134. ////FIXME: this should not create garbage
  135. // No, so create a new device.
  136. var controllerLayouts = InputSystem.ListLayoutsBasedOn("SteamController");
  137. foreach (var layout in controllerLayouts)
  138. {
  139. // Rather than directly creating a device with the layout, let it go through
  140. // the usual matching process.
  141. var device = InputSystem.AddDevice(new InputDeviceDescription
  142. {
  143. interfaceName = SteamController.kSteamInterface,
  144. product = layout
  145. });
  146. // Make sure it's a SteamController we got.
  147. var steamDevice = device as SteamController;
  148. if (steamDevice == null)
  149. {
  150. Debug.LogError(string.Format(
  151. "InputDevice created from layout '{0}' based on the 'SteamController' layout is not a SteamController",
  152. device.layout));
  153. continue;
  154. }
  155. // Resolve the controller's actions.
  156. steamDevice.InvokeResolveSteamActions();
  157. // Assign it the Steam controller handle.
  158. steamDevice.steamControllerHandle = handle;
  159. ArrayHelpers.AppendWithCapacity(ref s_InputDevices, ref s_InputDeviceCount, steamDevice);
  160. }
  161. }
  162. // Update all controllers we have.
  163. for (var i = 0; i < s_InputDeviceCount; ++i)
  164. {
  165. var device = s_InputDevices[i];
  166. var handle = device.steamControllerHandle;
  167. // Check if the device still exists.
  168. var stillExists = false;
  169. for (var n = 0; n < numConnectedControllers; ++n)
  170. if (s_ConnectedControllers[n] == handle)
  171. {
  172. stillExists = true;
  173. break;
  174. }
  175. // If not, remove it.
  176. if (!stillExists)
  177. {
  178. ArrayHelpers.EraseAtByMovingTail(s_InputDevices, ref s_InputDeviceCount, i);
  179. ////REVIEW: should this rather queue a device removal event?
  180. InputSystem.RemoveDevice(device);
  181. --i;
  182. continue;
  183. }
  184. ////TODO: support polling Steam controllers on an async polling thread adhering to InputSystem.pollingFrequency
  185. // Otherwise, update it.
  186. device.InvokeUpdate();
  187. }
  188. }
  189. }
  190. }
  191. #endif // (UNITY_STANDALONE || UNITY_EDITOR) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT