InputActionMap.cs 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEngine.InputSystem.Utilities;
  6. ////REVIEW: given we have the global ActionPerformed callback, do we really need the per-map callback?
  7. ////TODO: remove constraint of not being able to modify bindings while enabled from both actions and maps
  8. //// (because of the sharing of state between multiple maps in an asset, we'd have to extend that constraint
  9. //// to all maps in an asset in order to uphold it properly)
  10. namespace UnityEngine.InputSystem
  11. {
  12. /// <summary>
  13. /// A mechanism for collecting a series of input actions (see <see cref="InputAction"/>)
  14. /// and treating them as a group.
  15. /// </summary>
  16. /// <remarks>
  17. /// Each action map is a named collection of bindings and actions. Both are stored
  18. /// as a flat list. The bindings are available through the <see cref="bindings"/>
  19. /// property and the actions are available through the <see cref="actions"/> property.
  20. ///
  21. /// The actions in a map are owned by the map. No action can appear in two maps
  22. /// at the same time. To find the action map an action belongs to, use the
  23. /// <see cref="InputAction.actionMap"/> property. Note that actions can also stand
  24. /// on their own and thus do not necessarily need to belong to a map (in which case
  25. /// the <see cref="InputAction.actionMap"/> property is <c>null</c>).
  26. ///
  27. /// Within a map, all actions have to have names and each action name must
  28. /// be unique. The <see cref="InputBinding.action"/> property of bindings in a map
  29. /// are resolved within the <see cref="actions"/> in the map. Looking up actions
  30. /// by name can be done through <see cref="FindAction(string,bool)"/>.
  31. ///
  32. /// The <see cref="name"/> of the map itself can be empty, except if the map is part of
  33. /// an <see cref="InputActionAsset"/> in which case it is required to have a name
  34. /// which also must be unique within the asset.
  35. ///
  36. /// Action maps are most useful for grouping actions that contextually
  37. /// belong together. For example, one common usage is to separate the actions
  38. /// that can be performed in the UI or in the main menu from those that can
  39. /// be performed during gameplay. However, even within gameplay, multiple action
  40. /// maps can be employed. For example, one could have different action maps for
  41. /// driving and for walking plus one more map for the actions shared between
  42. /// the two modes.
  43. ///
  44. /// Action maps are usually created in the <a href="../manual/ActionAssets.html">action
  45. /// editor</a> as part of <see cref="InputActionAsset"/>s. However, they can also be
  46. /// created standing on their own directly in code or from JSON (see <see cref="FromJson"/>).
  47. ///
  48. /// <example>
  49. /// <code>
  50. /// // Create a free-standing action map.
  51. /// var map = new InputActionMap();
  52. ///
  53. /// // Add some actions and bindings to it.
  54. /// map.AddAction("action1", binding: "&lt;Keyboard&gt;/space");
  55. /// map.AddAction("action2", binding: "&lt;Gamepad&gt;/buttonSouth");
  56. /// </code>
  57. /// </example>
  58. ///
  59. /// Actions in action maps, like actions existing by themselves outside of action
  60. /// maps, do not actively process input except if enabled. Actions can either
  61. /// be enabled individually (see <see cref="InputAction.Enable"/> and <see
  62. /// cref="InputAction.Disable"/>) or in bulk by enabling and disabling the
  63. /// entire map (see <see cref="Enable"/> and <see cref="Disable"/>).
  64. /// </remarks>
  65. /// <seealso cref="InputActionAsset"/>
  66. /// <seealso cref="InputAction"/>
  67. [Serializable]
  68. public sealed class InputActionMap : ICloneable, ISerializationCallbackReceiver, IInputActionCollection, IDisposable
  69. {
  70. /// <summary>
  71. /// Name of the action map.
  72. /// </summary>
  73. /// <value>Name of the action map.</value>
  74. /// <remarks>
  75. /// For action maps that are part of <see cref="InputActionAsset"/>s, this will always be
  76. /// a non-null, non-empty string that is unique within the maps in the asset. For action maps
  77. /// that are standing on their own, this can be null or empty.
  78. /// </remarks>
  79. public string name => m_Name;
  80. /// <summary>
  81. /// If the action map is part of an asset, this refers to the asset. Otherwise it is <c>null</c>.
  82. /// </summary>
  83. /// <value>Asset to which the action map belongs.</value>
  84. public InputActionAsset asset => m_Asset;
  85. /// <summary>
  86. /// A stable, unique identifier for the map.
  87. /// </summary>
  88. /// <value>Unique ID for the action map.</value>
  89. /// <remarks>
  90. /// This can be used instead of the name to refer to the action map. Doing so allows referring to the
  91. /// map such that renaming it does not break references.
  92. /// </remarks>
  93. /// <seealso cref="InputAction.id"/>
  94. public Guid id
  95. {
  96. get
  97. {
  98. if (string.IsNullOrEmpty(m_Id))
  99. GenerateId();
  100. return new Guid(m_Id);
  101. }
  102. }
  103. internal Guid idDontGenerate
  104. {
  105. get
  106. {
  107. if (string.IsNullOrEmpty(m_Id))
  108. return default;
  109. return new Guid(m_Id);
  110. }
  111. }
  112. /// <summary>
  113. /// Whether any action in the map is currently enabled.
  114. /// </summary>
  115. /// <value>True if any action in <see cref="actions"/> is currently enabled.</value>
  116. /// <seealso cref="InputAction.enabled"/>
  117. /// <seealso cref="Enable"/>
  118. /// <seealso cref="InputAction.Enable"/>
  119. public bool enabled => m_EnabledActionsCount > 0;
  120. /// <summary>
  121. /// List of actions contained in the map.
  122. /// </summary>
  123. /// <value>Collection of actions belonging to the map.</value>
  124. /// <remarks>
  125. /// Actions are owned by their map. The same action cannot appear in multiple maps.
  126. ///
  127. /// Accessing this property. Note that values returned by the property become invalid if
  128. /// the setup of actions in a map is changed.
  129. /// </remarks>
  130. /// <seealso cref="InputAction.actionMap"/>
  131. public ReadOnlyArray<InputAction> actions => new ReadOnlyArray<InputAction>(m_Actions);
  132. /// <summary>
  133. /// List of bindings contained in the map.
  134. /// </summary>
  135. /// <value>Collection of bindings in the map.</value>
  136. /// <remarks>
  137. /// <see cref="InputBinding"/>s are owned by action maps and not by individual actions.
  138. ///
  139. /// Bindings that trigger actions refer to the action by <see cref="InputAction.name"/>
  140. /// or <see cref="InputAction.id"/>.
  141. ///
  142. /// Accessing this property does not allocate. Note that values returned by the property
  143. /// become invalid if the setup of bindings in a map is changed.
  144. /// </remarks>
  145. /// <seealso cref="InputAction.bindings"/>
  146. public ReadOnlyArray<InputBinding> bindings => new ReadOnlyArray<InputBinding>(m_Bindings);
  147. /// <summary>
  148. /// Control schemes defined for the action map.
  149. /// </summary>
  150. /// <value>List of available control schemes.</value>
  151. /// <remarks>
  152. /// Control schemes can only be defined at the level of <see cref="InputActionAsset"/>s.
  153. /// For action maps that are part of assets, this property will return the control schemes
  154. /// from the asset. For free-standing action maps, this will return an empty list.
  155. /// </remarks>
  156. /// <seealso cref="InputActionAsset.controlSchemes"/>
  157. public ReadOnlyArray<InputControlScheme> controlSchemes
  158. {
  159. get
  160. {
  161. if (m_Asset == null)
  162. return new ReadOnlyArray<InputControlScheme>();
  163. return m_Asset.controlSchemes;
  164. }
  165. }
  166. /// <summary>
  167. /// Binding mask to apply to all actions in the asset.
  168. /// </summary>
  169. /// <value>Optional mask that determines which bindings in the action map to enable.</value>
  170. /// <remarks>
  171. /// Binding masks can be applied at three different levels: for an entire asset through
  172. /// <see cref="InputActionAsset.bindingMask"/>, for a specific map through this property,
  173. /// and for single actions through <see cref="InputAction.bindingMask"/>. By default,
  174. /// none of the masks will be set (i.e. they will be <c>null</c>).
  175. ///
  176. /// When an action is enabled, all the binding masks that apply to it are taken into
  177. /// account. Specifically, this means that any given binding on the action will be
  178. /// enabled only if it matches the mask applied to the asset, the mask applied
  179. /// to the map that contains the action, and the mask applied to the action itself.
  180. /// All the masks are individually optional.
  181. ///
  182. /// Masks are matched against bindings using <see cref="InputBinding.Matches"/>.
  183. ///
  184. /// Note that if you modify the masks applicable to an action while it is
  185. /// enabled, the action's <see cref="InputAction.controls"/> will get updated immediately to
  186. /// respect the mask. To avoid repeated binding resolution, it is most efficient
  187. /// to apply binding masks before enabling actions.
  188. ///
  189. /// Binding masks are non-destructive. All the bindings on the action are left
  190. /// in place. Setting a mask will not affect the value of the <see cref="InputAction.bindings"/>
  191. /// and <see cref="bindings"/> properties.
  192. /// </remarks>
  193. /// <seealso cref="InputBinding.MaskByGroup"/>
  194. /// <seealso cref="InputAction.bindingMask"/>
  195. /// <seealso cref="InputActionAsset.bindingMask"/>
  196. public InputBinding? bindingMask
  197. {
  198. get => m_BindingMask;
  199. set
  200. {
  201. if (m_BindingMask == value)
  202. return;
  203. m_BindingMask = value;
  204. LazyResolveBindings();
  205. }
  206. }
  207. /// <summary>
  208. /// Set of devices that bindings in the action map can bind to.
  209. /// </summary>
  210. /// <value>Optional set of devices to use by bindings in the map.</value>
  211. /// <remarks>
  212. /// By default (with this property being <c>null</c>), bindings will bind to any of the
  213. /// controls available through <see cref="InputSystem.devices"/>, i.e. controls from all
  214. /// devices in the system will be used.
  215. ///
  216. /// By setting this property, binding resolution can instead be restricted to just specific
  217. /// devices. This restriction can either be applied to an entire asset using <see
  218. /// cref="InputActionMap.devices"/> or to specific action maps by using this property. Note that
  219. /// if both this property and <see cref="InputActionAsset.devices"/> is set for a specific action
  220. /// map, the list of devices on the action map will take precedence and the list on the
  221. /// asset will be ignored for bindings in that action map.
  222. ///
  223. /// <example>
  224. /// <code>
  225. /// // Create an action map containing a single action with a gamepad binding.
  226. /// var actionMap = new InputActionMap();
  227. /// var fireAction = actionMap.AddAction("Fire", binding: "&lt;Gamepad&gt;/buttonSouth");
  228. /// asset.AddActionMap(actionMap);
  229. ///
  230. /// // Let's assume we have two gamepads connected. If we enable the
  231. /// // action map now, the 'Fire' action will bind to both.
  232. /// actionMap.Enable();
  233. ///
  234. /// // This will print two controls.
  235. /// Debug.Log(string.Join("\n", fireAction.controls));
  236. ///
  237. /// // To restrict the setup to just the first gamepad, we can assign
  238. /// // to the 'devices' property.
  239. /// actionMap.devices = new InputDevice[] { Gamepad.all[0] };
  240. ///
  241. /// // Now this will print only one control.
  242. /// Debug.Log(string.Join("\n", fireAction.controls));
  243. /// </code>
  244. /// </example>
  245. /// </remarks>
  246. /// <seealso cref="InputActionAsset.devices"/>
  247. public ReadOnlyArray<InputDevice>? devices
  248. {
  249. get
  250. {
  251. if (m_DevicesCount < 0)
  252. {
  253. // Return asset's device list if we have none (only if we're part of an asset).
  254. if (asset != null)
  255. return asset.devices;
  256. return null;
  257. }
  258. return new ReadOnlyArray<InputDevice>(m_DevicesArray, 0, m_DevicesCount);
  259. }
  260. set
  261. {
  262. if (value == null)
  263. {
  264. if (m_DevicesCount < 0)
  265. return; // No change.
  266. if (m_DevicesArray != null & m_DevicesCount > 0)
  267. Array.Clear(m_DevicesArray, 0, m_DevicesCount);
  268. m_DevicesCount = -1;
  269. }
  270. else
  271. {
  272. // See if the array actually changes content. Avoids re-resolving when there
  273. // is no need to.
  274. if (m_DevicesCount == value.Value.Count)
  275. {
  276. var noChange = true;
  277. for (var i = 0; i < m_DevicesCount; ++i)
  278. {
  279. if (!ReferenceEquals(m_DevicesArray[i], value.Value[i]))
  280. {
  281. noChange = false;
  282. break;
  283. }
  284. }
  285. if (noChange)
  286. return;
  287. }
  288. if (m_DevicesCount > 0)
  289. m_DevicesArray.Clear(ref m_DevicesCount);
  290. m_DevicesCount = 0;
  291. ArrayHelpers.AppendListWithCapacity(ref m_DevicesArray, ref m_DevicesCount, value.Value);
  292. }
  293. LazyResolveBindings();
  294. }
  295. }
  296. /// <summary>
  297. /// Look up an action by name or ID.
  298. /// </summary>
  299. /// <param name="actionNameOrId">Name (as in <see cref="InputAction.name"/>) or ID (as in <see cref="InputAction.id"/>)
  300. /// of the action. Note that matching of names is case-insensitive.</param>
  301. /// <exception cref="ArgumentNullException"><paramref name="actionNameOrId"/> is <c>null</c>.</exception>
  302. /// <exception cref="KeyNotFoundException">No action with the name or ID of <paramref name="actionNameOrId"/>
  303. /// was found in the action map.</exception>
  304. /// <remarks>
  305. /// This method is equivalent to <see cref="FindAction(string,bool)"/> except it throws <c>KeyNotFoundException</c>
  306. /// if no action with the given name or ID can be found.
  307. /// </remarks>
  308. /// <seealso cref="FindAction(string,bool)"/>
  309. /// <seealso cref="FindAction(Guid)"/>
  310. /// <see cref="actions"/>
  311. public InputAction this[string actionNameOrId]
  312. {
  313. get
  314. {
  315. if (actionNameOrId == null)
  316. throw new ArgumentNullException(nameof(actionNameOrId));
  317. var action = FindAction(actionNameOrId);
  318. if (action == null)
  319. throw new KeyNotFoundException($"Cannot find action '{actionNameOrId}'");
  320. return action;
  321. }
  322. }
  323. ////REVIEW: inconsistent naming; elsewhere we use "onActionTriggered" (which in turn is inconsistent with InputAction.started etc)
  324. /// <summary>
  325. /// Add or remove a callback that is triggered when an action in the map changes its <see cref="InputActionPhase">
  326. /// phase</see>.
  327. /// </summary>
  328. /// <seealso cref="InputAction.started"/>
  329. /// <seealso cref="InputAction.performed"/>
  330. /// <seealso cref="InputAction.canceled"/>
  331. public event Action<InputAction.CallbackContext> actionTriggered
  332. {
  333. add => m_ActionCallbacks.AppendWithCapacity(value);
  334. remove => m_ActionCallbacks.RemoveByMovingTailWithCapacity(value); ////FIXME: Changes callback ordering.
  335. }
  336. public InputActionMap()
  337. {
  338. // For some reason, when using UnityEngine.Object.Instantiate the -1 initialization
  339. // does not come through except if explicitly done here in the default constructor.
  340. m_DevicesCount = -1;
  341. }
  342. /// <summary>
  343. /// Construct an action map with the given name.
  344. /// </summary>
  345. /// <param name="name">Name to give to the action map. By default <c>null</c>, i.e. does
  346. /// not assign a name to the map.</param>
  347. public InputActionMap(string name)
  348. : this()
  349. {
  350. m_Name = name;
  351. m_DevicesCount = -1;
  352. }
  353. /// <summary>
  354. /// Release internal state held on to by the action map.
  355. /// </summary>
  356. /// <remarks>
  357. /// Once actions in a map are enabled, the map will allocate a block of state internally that
  358. /// it will hold on to until disposed of. All actions in the map will share the same internal
  359. /// state. Also, if the map is part of an <see cref="InputActionAsset"/> all maps and actions
  360. /// in the same asset will share the same internal state.
  361. ///
  362. /// Note that the internal state holds on to GC heap memory as well as memory from the
  363. /// unmanaged, C++ heap.
  364. /// </remarks>
  365. public void Dispose()
  366. {
  367. m_State?.Dispose();
  368. }
  369. internal int FindActionIndex(string nameOrId)
  370. {
  371. ////REVIEW: have transient lookup table? worth optimizing this?
  372. //// Ideally, this should at least be an InternedString comparison but due to serialization,
  373. //// that's quite tricky.
  374. if (string.IsNullOrEmpty(nameOrId))
  375. return -1;
  376. if (m_Actions == null)
  377. return -1;
  378. var actionCount = m_Actions.Length;
  379. var isOldBracedFormat = nameOrId.StartsWith("{") && nameOrId.EndsWith("}");
  380. if (isOldBracedFormat)
  381. {
  382. var length = nameOrId.Length - 2;
  383. for (var i = 0; i < actionCount; ++i)
  384. {
  385. if (string.Compare(m_Actions[i].m_Id, 0, nameOrId, 1, length) == 0)
  386. return i;
  387. }
  388. }
  389. for (var i = 0; i < actionCount; ++i)
  390. {
  391. var action = m_Actions[i];
  392. if (action.m_Id == nameOrId || string.Compare(m_Actions[i].m_Name, nameOrId, StringComparison.InvariantCultureIgnoreCase) == 0)
  393. return i;
  394. }
  395. return InputActionState.kInvalidIndex;
  396. }
  397. private int FindActionIndex(Guid id)
  398. {
  399. if (m_Actions == null)
  400. return InputActionState.kInvalidIndex;
  401. var actionCount = m_Actions.Length;
  402. for (var i = 0; i < actionCount; ++i)
  403. if (m_Actions[i].idDontGenerate == id)
  404. return i;
  405. return InputActionState.kInvalidIndex;
  406. }
  407. /// <summary>
  408. /// Find an action in the map by name or ID.
  409. /// </summary>
  410. /// <param name="nameOrId">Name (as in <see cref="InputAction.name"/>) or ID (as in <see cref="InputAction.id"/>)
  411. /// of the action. Note that matching of names is case-insensitive.</param>
  412. /// <returns>The action with the given name or ID or <c>null</c> if no matching action
  413. /// was found.</returns>
  414. /// <exception cref="ArgumentNullException"><paramref name="nameOrId"/> is <c>null</c>.</exception>
  415. /// <seealso cref="FindAction(Guid)"/>
  416. public InputAction FindAction(string nameOrId, bool throwIfNotFound = false)
  417. {
  418. if (nameOrId == null)
  419. throw new ArgumentNullException(nameof(nameOrId));
  420. var index = FindActionIndex(nameOrId);
  421. if (index == -1)
  422. {
  423. if (throwIfNotFound)
  424. throw new ArgumentException($"No action '{nameOrId}' in '{this}'", nameof(nameOrId));
  425. return null;
  426. }
  427. return m_Actions[index];
  428. }
  429. /// <summary>
  430. /// Find an action by ID.
  431. /// </summary>
  432. /// <param name="id">ID (as in <see cref="InputAction.id"/>) of the action.</param>
  433. /// <returns>The action with the given ID or null if no action in the map has
  434. /// the given ID.</returns>
  435. /// <seealso cref="FindAction(string)"/>
  436. public InputAction FindAction(Guid id)
  437. {
  438. var index = FindActionIndex(id);
  439. if (index == -1)
  440. return null;
  441. return m_Actions[index];
  442. }
  443. /// <summary>
  444. /// Check whether there are any bindings in the action map that can bind to
  445. /// controls on the given device.
  446. /// </summary>
  447. /// <param name="device">An input device.</param>
  448. /// <returns>True if any of the bindings in the map can resolve to controls on the device, false otherwise.</returns>
  449. /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
  450. /// <remarks>
  451. /// The logic is entirely based on the contents of <see cref="bindings"/> and, more specifically,
  452. /// <see cref="InputBinding.effectivePath"/> of each binding. Each path is checked using <see
  453. /// cref="InputControlPath.Matches"/>. If any path matches, the method returns <c>true</c>.
  454. ///
  455. /// Properties such as <see cref="devices"/> and <see cref="bindingMask"/> are ignored.
  456. ///
  457. /// <example>
  458. /// <code>
  459. /// // Create action map with two actions and bindings.
  460. /// var actionMap = new InputActionMap();
  461. /// actionMap.AddAction("action1", binding: "&lt;Gamepad&gt;/buttonSouth");
  462. /// actionMap.AddAction("action2", binding: "&lt;XRController{LeftHand}&gt;/{PrimaryAction}");
  463. ///
  464. /// //
  465. /// var gamepad = InputSystem.AddDevice&lt;Gamepad&gt;();
  466. /// var xrController = InputSystem.AddDevice&lt;XRController&gt;();
  467. ///
  468. /// // Returns true:
  469. /// actionMap.IsUsableWith(gamepad);
  470. ///
  471. /// // Returns false: (the XRController does not have the LeftHand usage assigned to it)
  472. /// actionMap.IsUsableWith(xrController);
  473. /// </code>
  474. /// </example>
  475. /// </remarks>
  476. public bool IsUsableWithDevice(InputDevice device)
  477. {
  478. if (device == null)
  479. throw new ArgumentNullException(nameof(device));
  480. if (m_Bindings == null)
  481. return false;
  482. foreach (var binding in m_Bindings)
  483. {
  484. var path = binding.effectivePath;
  485. if (string.IsNullOrEmpty(path))
  486. continue;
  487. if (InputControlPath.Matches(path, device))
  488. return true;
  489. }
  490. return false;
  491. }
  492. /// <summary>
  493. /// Enable all the actions in the map.
  494. /// </summary>
  495. /// <remarks>
  496. /// This is equivalent to calling <see cref="InputAction.Enable"/> on each
  497. /// action in <see cref="actions"/>, but is more efficient as the actions
  498. /// will get enabled in bulk.
  499. /// </remarks>
  500. /// <seealso cref="Disable"/>
  501. /// <seealso cref="enabled"/>
  502. public void Enable()
  503. {
  504. if (m_Actions == null || m_EnabledActionsCount == m_Actions.Length)
  505. return;
  506. ResolveBindingsIfNecessary();
  507. m_State.EnableAllActions(this);
  508. }
  509. /// <summary>
  510. /// Disable all the actions in the map.
  511. /// </summary>
  512. /// <remarks>
  513. /// This is equivalent to calling <see cref="InputAction.Disable"/> on each
  514. /// action in <see cref="actions"/>, but is more efficient as the actions
  515. /// will get disabled in bulk.
  516. /// </remarks>
  517. /// <seealso cref="Enable"/>
  518. /// <seealso cref="enabled"/>
  519. public void Disable()
  520. {
  521. if (!enabled)
  522. return;
  523. m_State.DisableAllActions(this);
  524. }
  525. /// <summary>
  526. /// Produce an identical copy of the action map with its actions and bindings.
  527. /// </summary>
  528. /// <returns>A copy of the action map.</returns>
  529. /// <remarks>
  530. /// If the action map is part of an <see cref="InputActionAsset"/>, the clone will <em>not</em>
  531. /// be. It will be a free-standing action map and <see cref="asset"/> will be <c>null</c>.
  532. ///
  533. /// Note that the IDs for the map itself as well as for its <see cref="actions"/> and
  534. /// <see cref="bindings"/> are not copied. Instead, new IDs will be assigned. Also, callbacks
  535. /// installed on actions or on the map itself will not be copied over.
  536. /// </remarks>
  537. public InputActionMap Clone()
  538. {
  539. Debug.Assert(m_SingletonAction == null, "Internal (hidden) action maps of singleton actions should not be cloned");
  540. var clone = new InputActionMap
  541. {
  542. m_Name = m_Name
  543. };
  544. // Clone actions.
  545. if (m_Actions != null)
  546. {
  547. var actionCount = m_Actions.Length;
  548. var actions = new InputAction[actionCount];
  549. for (var i = 0; i < actionCount; ++i)
  550. {
  551. var original = m_Actions[i];
  552. actions[i] = new InputAction
  553. {
  554. m_Name = original.m_Name,
  555. m_ActionMap = clone,
  556. m_Type = original.m_Type,
  557. m_Interactions = original.m_Interactions,
  558. m_Processors = original.m_Processors,
  559. m_ExpectedControlType = original.m_ExpectedControlType,
  560. };
  561. }
  562. clone.m_Actions = actions;
  563. }
  564. // Clone bindings.
  565. if (m_Bindings != null)
  566. {
  567. var bindingCount = m_Bindings.Length;
  568. var bindings = new InputBinding[bindingCount];
  569. Array.Copy(m_Bindings, 0, bindings, 0, bindingCount);
  570. for (var i = 0; i < bindingCount; ++i)
  571. bindings[i].m_Id = default;
  572. clone.m_Bindings = bindings;
  573. }
  574. return clone;
  575. }
  576. object ICloneable.Clone()
  577. {
  578. return Clone();
  579. }
  580. /// <summary>
  581. /// Return <c>true</c> if the action map contains the given action.
  582. /// </summary>
  583. /// <param name="action">An input action. Can be <c>null</c>.</param>
  584. /// <returns>True if the action map contains <paramref name="action"/>, false otherwise.</returns>
  585. public bool Contains(InputAction action)
  586. {
  587. if (action == null)
  588. return false;
  589. return action.actionMap == this;
  590. }
  591. /// <summary>
  592. /// Return a string representation of the action map useful for debugging.
  593. /// </summary>
  594. /// <returns>A string representation of the action map.</returns>
  595. /// <remarks>
  596. /// For unnamed action maps, this will always be <c>"&lt;Unnamed Action Map&gt;"</c>.
  597. /// </remarks>
  598. public override string ToString()
  599. {
  600. if (m_Asset != null)
  601. return $"{m_Asset}:{m_Name}";
  602. if (!string.IsNullOrEmpty(m_Name))
  603. return m_Name;
  604. return "<Unnamed Action Map>";
  605. }
  606. /// <summary>
  607. /// Enumerate the actions in the map.
  608. /// </summary>
  609. /// <returns>An enumerator going over the actions in the map.</returns>
  610. /// <remarks>
  611. /// This method supports to generically iterate over the actions in a map. However, it will usually
  612. /// lead to GC allocation. Iterating directly over <see cref="actions"/> avoids allocating GC memory.
  613. /// </remarks>
  614. public IEnumerator<InputAction> GetEnumerator()
  615. {
  616. return actions.GetEnumerator();
  617. }
  618. IEnumerator IEnumerable.GetEnumerator()
  619. {
  620. return GetEnumerator();
  621. }
  622. // The state we persist is pretty much just a name, a flat list of actions, and a flat
  623. // list of bindings. The rest is state we keep at runtime when a map is in use.
  624. [SerializeField] internal string m_Name;
  625. [SerializeField] internal string m_Id; // Can't serialize System.Guid and Unity's GUID is editor only.
  626. [SerializeField] internal InputActionAsset m_Asset;
  627. /// <summary>
  628. /// List of actions in this map.
  629. /// </summary>
  630. [SerializeField] internal InputAction[] m_Actions;
  631. /// <summary>
  632. /// List of bindings in this map.
  633. /// </summary>
  634. /// <remarks>
  635. /// For singleton actions, we ensure this is always the same as <see cref="InputAction.m_SingletonActionBindings"/>.
  636. /// </remarks>
  637. [SerializeField] internal InputBinding[] m_Bindings;
  638. // These fields are caches. If m_Bindings is modified, these are thrown away
  639. // and re-computed only if needed.
  640. // NOTE: Because InputBindings are structs, m_BindingsForEachAction actually duplicates each binding
  641. // (only in the case where m_Bindings has scattered references to actions).
  642. ////REVIEW: this will lead to problems when overrides are thrown into the mix
  643. /// <summary>
  644. /// For each entry in <see cref="m_Actions"/>, a slice of this array corresponds to the
  645. /// action's bindings.
  646. /// </summary>
  647. /// <remarks>
  648. /// Ideally, this array is the same as <see cref="m_Bindings"/> (the same as in literally reusing the
  649. /// same array). However, we have no guarantee that <see cref="m_Bindings"/> is sorted by actions. In case it
  650. /// isn't, we create a separate array with the bindings sorted by action and have each action reference
  651. /// a slice through <see cref="InputAction.m_BindingsStartIndex"/> and <see cref="InputAction.m_BindingsCount"/>.
  652. /// </remarks>
  653. /// <seealso cref="SetUpPerActionCachedBindingData"/>
  654. [NonSerialized] private InputBinding[] m_BindingsForEachAction;
  655. [NonSerialized] private InputControl[] m_ControlsForEachAction;
  656. /// <summary>
  657. /// Number of actions currently enabled in the map.
  658. /// </summary>
  659. /// <remarks>
  660. /// This should only be written to by <see cref="InputActionState"/>.
  661. /// </remarks>
  662. [NonSerialized] internal int m_EnabledActionsCount;
  663. // Action maps that are created internally by singleton actions to hold their data
  664. // are never exposed and never serialized so there is no point allocating an m_Actions
  665. // array.
  666. [NonSerialized] internal InputAction m_SingletonAction;
  667. [NonSerialized] internal int m_MapIndexInState = InputActionState.kInvalidIndex;
  668. /// <summary>
  669. /// Current execution state.
  670. /// </summary>
  671. /// <remarks>
  672. /// Initialized when map (or any action in it) is first enabled.
  673. /// </remarks>
  674. [NonSerialized] internal InputActionState m_State;
  675. [NonSerialized] private bool m_NeedToResolveBindings;
  676. [NonSerialized] internal InputBinding? m_BindingMask;
  677. [NonSerialized] private int m_DevicesCount = -1;
  678. [NonSerialized] private InputDevice[] m_DevicesArray;
  679. [NonSerialized] internal InlinedArray<Action<InputAction.CallbackContext>> m_ActionCallbacks;
  680. internal static int s_DeferBindingResolution;
  681. /// <summary>
  682. /// Return the list of bindings for just the given actions.
  683. /// </summary>
  684. /// <param name="action"></param>
  685. /// <returns></returns>
  686. /// <remarks>
  687. /// The bindings for a single action may be contiguous in <see cref="m_Bindings"/> or may be scattered
  688. /// around. We don't keep persistent storage for these and instead set up a transient
  689. /// array if and when bindings are queried directly from an action. In the simple case,
  690. /// we don't even need a separate array but rather just need to find out which slice in the
  691. /// bindings array corresponds to which action.
  692. ///
  693. /// NOTE: Bindings for individual actions aren't queried by the system itself during normal
  694. /// runtime operation so we only do this for cases where the user asks for the
  695. /// information. If the user never asks for bindings or controls on a per-action basis,
  696. /// none of this data gets initialized.
  697. /// </remarks>
  698. internal ReadOnlyArray<InputBinding> GetBindingsForSingleAction(InputAction action)
  699. {
  700. Debug.Assert(action != null, "Action cannot be null");
  701. Debug.Assert(action.m_ActionMap == this, "Action must be in action map");
  702. Debug.Assert(!action.isSingletonAction || m_SingletonAction == action, "Action is not a singleton action");
  703. // See if we need to refresh.
  704. if (m_BindingsForEachAction == null)
  705. SetUpPerActionCachedBindingData();
  706. return new ReadOnlyArray<InputBinding>(m_BindingsForEachAction, action.m_BindingsStartIndex,
  707. action.m_BindingsCount);
  708. }
  709. internal ReadOnlyArray<InputControl> GetControlsForSingleAction(InputAction action)
  710. {
  711. Debug.Assert(m_State != null);
  712. Debug.Assert(m_MapIndexInState != InputActionState.kInvalidIndex);
  713. Debug.Assert(m_Actions != null);
  714. Debug.Assert(action != null);
  715. Debug.Assert(action.m_ActionMap == this);
  716. Debug.Assert(!action.isSingletonAction || m_SingletonAction == action);
  717. if (m_ControlsForEachAction == null)
  718. SetUpPerActionCachedBindingData();
  719. return new ReadOnlyArray<InputControl>(m_ControlsForEachAction, action.m_ControlStartIndex,
  720. action.m_ControlCount);
  721. }
  722. /// <summary>
  723. /// Collect data from <see cref="m_Bindings"/> and <see cref="m_Actions"/> such that we can
  724. /// we can cleanly expose it from <see cref="InputAction.bindings"/> and <see cref="InputAction.controls"/>.
  725. /// </summary>
  726. /// <remarks>
  727. /// We set up per-action caches the first time their information is requested. Internally, we do not
  728. /// use those arrays and thus they will not get set up by default.
  729. ///
  730. /// Note that it is important to allow to call this method at a point where we have not resolved
  731. /// controls yet (i.e. <see cref="m_State"/> is <c>null</c>). Otherwise, using <see cref="InputAction.bindings"/>
  732. /// may trigger a control resolution which would be surprising.
  733. /// </remarks>
  734. private unsafe void SetUpPerActionCachedBindingData()
  735. {
  736. // Handle case where we don't have any bindings.
  737. if (m_Bindings == null)
  738. return;
  739. if (m_SingletonAction != null)
  740. {
  741. // Dead simple case: map is internally owned by action. The entire
  742. // list of bindings is specific to the action.
  743. Debug.Assert(m_Bindings == m_SingletonAction.m_SingletonActionBindings);
  744. m_BindingsForEachAction = m_Bindings;
  745. m_ControlsForEachAction = m_State?.controls;
  746. m_SingletonAction.m_BindingsStartIndex = 0;
  747. m_SingletonAction.m_BindingsCount = m_Bindings.Length;
  748. m_SingletonAction.m_ControlStartIndex = 0;
  749. m_SingletonAction.m_ControlCount = m_State?.totalControlCount ?? 0;
  750. }
  751. else
  752. {
  753. ////REVIEW: now that we have per-action binding information in UnmanagedMemory, this here can likely be done more easily
  754. // Go through all bindings and slice them out to individual actions.
  755. Debug.Assert(m_Actions != null); // Action isn't a singleton so this has to be true.
  756. var mapIndices = m_State?.FetchMapIndices(this) ?? new InputActionState.ActionMapIndices();
  757. // Reset state on each action. Important if we have actions that are no longer
  758. // referred to by bindings.
  759. for (var i = 0; i < m_Actions.Length; ++i)
  760. {
  761. var action = m_Actions[i];
  762. action.m_BindingsCount = 0;
  763. action.m_BindingsStartIndex = -1;
  764. action.m_ControlCount = 0;
  765. action.m_ControlStartIndex = -1;
  766. }
  767. // Count bindings on each action.
  768. // After this loop, we can have one of two situations:
  769. // 1) The bindings for any action X start at some index N and occupy the next m_BindingsCount slots.
  770. // 2) The bindings for some or all actions are scattered across non-contiguous chunks of the array.
  771. var bindingCount = m_Bindings.Length;
  772. for (var i = 0; i < bindingCount; ++i)
  773. {
  774. var action = FindAction(m_Bindings[i].action);
  775. if (action != null)
  776. ++action.m_BindingsCount;
  777. }
  778. // Collect the bindings and controls and bundle them into chunks.
  779. var newBindingsArrayIndex = 0;
  780. if (m_State != null && (m_ControlsForEachAction == null || m_ControlsForEachAction.Length != mapIndices.controlCount))
  781. {
  782. if (mapIndices.controlCount == 0)
  783. m_ControlsForEachAction = null;
  784. else
  785. m_ControlsForEachAction = new InputControl[mapIndices.controlCount];
  786. }
  787. InputBinding[] newBindingsArray = null;
  788. var currentControlIndex = 0;
  789. for (var currentBindingIndex = 0; currentBindingIndex < m_Bindings.Length;)
  790. {
  791. var currentAction = FindAction(m_Bindings[currentBindingIndex].action);
  792. if (currentAction == null || currentAction.m_BindingsStartIndex != -1)
  793. {
  794. // Skip bindings not targeting an action or bindings we have already processed
  795. // (when gathering bindings for a single actions scattered across the array we may have
  796. // skipping ahead).
  797. ++currentBindingIndex;
  798. continue;
  799. }
  800. // Bindings for current action start at current index.
  801. currentAction.m_BindingsStartIndex = newBindingsArray != null
  802. ? newBindingsArrayIndex
  803. : currentBindingIndex;
  804. currentAction.m_ControlStartIndex = currentControlIndex;
  805. // Collect all bindings for the action. As part of that, also copy the controls
  806. // for each binding over to m_ControlsForEachAction.
  807. var bindingCountForCurrentAction = currentAction.m_BindingsCount;
  808. Debug.Assert(bindingCountForCurrentAction > 0);
  809. var sourceBindingToCopy = currentBindingIndex;
  810. for (var i = 0; i < bindingCountForCurrentAction; ++i)
  811. {
  812. // See if we've come across a binding that doesn't belong to our currently looked at action.
  813. if (FindAction(m_Bindings[sourceBindingToCopy].action) != currentAction)
  814. {
  815. // Yes, we have. Means the bindings for our actions are scattered in m_Bindings and
  816. // we need to collect them.
  817. // If this is the first action that has its bindings scattered around, switch to
  818. // having a separate bindings array and copy whatever bindings we already processed
  819. // over to it.
  820. if (newBindingsArray == null)
  821. {
  822. newBindingsArray = new InputBinding[m_Bindings.Length];
  823. newBindingsArrayIndex = sourceBindingToCopy;
  824. Array.Copy(m_Bindings, 0, newBindingsArray, 0, sourceBindingToCopy);
  825. }
  826. // Find the next binding belonging to the action. We've counted bindings for
  827. // the action in the previous pass so we know exactly how many bindings we
  828. // can expect.
  829. do
  830. {
  831. ++sourceBindingToCopy;
  832. Debug.Assert(sourceBindingToCopy < m_Bindings.Length);
  833. }
  834. while (FindAction(m_Bindings[sourceBindingToCopy].action) != currentAction);
  835. }
  836. else if (currentBindingIndex == sourceBindingToCopy)
  837. ++currentBindingIndex;
  838. // Copy binding over to new bindings array, if need be.
  839. if (newBindingsArray != null)
  840. newBindingsArray[newBindingsArrayIndex++] = m_Bindings[sourceBindingToCopy];
  841. // Copy controls for binding, if we have resolved controls already and if the
  842. // binding isn't a composite (they refer to the controls from all of their part bindings
  843. // but do not really resolve to controls themselves).
  844. if (m_State != null && !m_Bindings[sourceBindingToCopy].isComposite)
  845. {
  846. var controlCountForBinding = m_State
  847. .bindingStates[mapIndices.bindingStartIndex + sourceBindingToCopy].controlCount;
  848. if (controlCountForBinding > 0)
  849. {
  850. Array.Copy(m_State.controls,
  851. m_State.bindingStates[mapIndices.bindingStartIndex + sourceBindingToCopy]
  852. .controlStartIndex,
  853. m_ControlsForEachAction, currentControlIndex, controlCountForBinding);
  854. currentControlIndex += controlCountForBinding;
  855. currentAction.m_ControlCount += controlCountForBinding;
  856. }
  857. }
  858. ++sourceBindingToCopy;
  859. }
  860. }
  861. if (newBindingsArray == null)
  862. {
  863. // Bindings are already clustered by action in m_Bindings
  864. // so we can just stick to having one array only.
  865. m_BindingsForEachAction = m_Bindings;
  866. }
  867. else
  868. {
  869. // Bindings are not clustered by action in m_Bindings so
  870. // we had to allocate a separate array where the bindings are sorted.
  871. m_BindingsForEachAction = newBindingsArray;
  872. }
  873. }
  874. }
  875. ////TODO: re-use allocations such that only grow the arrays and hit zero GC allocs when we already have enough memory
  876. internal void ClearPerActionCachedBindingData()
  877. {
  878. m_BindingsForEachAction = null;
  879. m_ControlsForEachAction = null;
  880. }
  881. internal void GenerateId()
  882. {
  883. m_Id = Guid.NewGuid().ToString();
  884. }
  885. /// <summary>
  886. /// Resolve bindings right away if we have to. Otherwise defer it to when we next need
  887. /// the bindings.
  888. /// </summary>
  889. internal bool LazyResolveBindings()
  890. {
  891. // Clear cached controls for actions. Don't need to necessarily clear m_BindingsForEachAction.
  892. m_ControlsForEachAction = null;
  893. // If we haven't had to resolve bindings yet, we can wait until when we
  894. // actually have to.
  895. if (m_State == null)
  896. return false;
  897. // We used to defer binding resolution here in case the map had no enabled actions. That behavior,
  898. // however, leads to rather unpredictable BoundControlsChanged notifications (especially for
  899. // rebinding UIs), so now we just always re-resolve anything that ever had an InputActionState
  900. // created. Unfortunately, this can lead to some unnecessary re-resolving.
  901. if (s_DeferBindingResolution > 0)
  902. {
  903. m_NeedToResolveBindings = true;
  904. return false;
  905. }
  906. // Have to do it straight away.
  907. ResolveBindings();
  908. return true;
  909. }
  910. internal void ResolveBindingsIfNecessary()
  911. {
  912. // NOTE: We only check locally for the current map here. When there are multiple maps
  913. // in an asset, we may have maps that require re-resolution while others don't.
  914. // We only resolve if a map is used that needs resolution to happen. Note that
  915. // this will still resolve bindings for *all* maps in the asset.
  916. if (m_State == null || m_NeedToResolveBindings)
  917. ResolveBindings();
  918. }
  919. /// <summary>
  920. /// Resolve all bindings to their controls and also add any action interactions
  921. /// from the bindings.
  922. /// </summary>
  923. /// <remarks>
  924. /// This is the core method of action binding resolution. All binding resolution goes through here.
  925. ///
  926. /// The best way is for binding resolution to happen once for each action map at the beginning of the game
  927. /// and to then enable and disable the maps as needed. However, the system will also re-resolve
  928. /// bindings if the control setup in the system changes (i.e. if devices are added or removed
  929. /// or if layouts in the system are changed).
  930. ///
  931. /// Bindings can be re-resolved while actions are enabled. This happens changing device or binding
  932. /// masks on action maps or assets (<see cref="devices"/>, <see cref="bindingMask"/>, <see cref="InputAction.bindingMask"/>,
  933. /// <see cref="InputActionAsset.devices"/>, <see cref="InputActionAsset.bindingMask"/>). When this happens,
  934. /// we temporarily disable and then reenable actions. Note that this is visible to observers.
  935. /// </remarks>
  936. internal void ResolveBindings()
  937. {
  938. // In case we have actions that are currently enabled, we temporarily retain the
  939. // UnmanagedMemory of our InputActionState so that we can sync action states after
  940. // we have re-resolved bindings.
  941. var tempMemory = new InputActionState.UnmanagedMemory();
  942. try
  943. {
  944. OneOrMore<InputActionMap, ReadOnlyArray<InputActionMap>> actionMaps;
  945. // Start resolving.
  946. var resolver = new InputBindingResolver();
  947. // If we're part of an asset, we share state and thus binding resolution with
  948. // all maps in the asset.
  949. if (m_Asset != null)
  950. {
  951. actionMaps = m_Asset.actionMaps;
  952. Debug.Assert(actionMaps.Count > 0, "Asset referred to by action map does not have action maps");
  953. // If there's a binding mask set on the asset, apply it.
  954. resolver.bindingMask = m_Asset.m_BindingMask;
  955. }
  956. else
  957. {
  958. // Standalone action map (possibly a hidden one created for a singleton action).
  959. // Gets its own private state.
  960. actionMaps = this;
  961. }
  962. // If we already have a state, re-use the arrays we have already allocated.
  963. // NOTE: We will install the arrays on the very same InputActionState instance below. In the
  964. // case where we didn't have to grow the arrays, we should end up with zero GC allocations
  965. // here.
  966. var hasEnabledActions = false;
  967. if (m_State != null)
  968. {
  969. // Grab a clone of the current memory. We clone because disabling all the actions
  970. // in the map will alter the memory state and we want the state before we start
  971. // touching it.
  972. //
  973. // Technically, ATM we only need the phase values in the action states but duplicating
  974. // the unmanaged memory is cheap and avoids having to add yet more complication to the
  975. // code paths here.
  976. tempMemory = m_State.memory.Clone();
  977. // If the state has enabled actions, temporarily disable them.
  978. hasEnabledActions = m_State.HasEnabledActions();
  979. for (var i = 0; i < actionMaps.Count; ++i)
  980. {
  981. var map = actionMaps[i];
  982. if (hasEnabledActions)
  983. m_State.DisableAllActions(map);
  984. // Let listeners know we are about to modify bindings. Do this *after* we disabled the
  985. // actions so that cancellations happen first.
  986. if (map.m_SingletonAction != null)
  987. InputActionState.NotifyListenersOfActionChange(InputActionChange.BoundControlsAboutToChange, map.m_SingletonAction);
  988. else if (m_Asset == null)
  989. InputActionState.NotifyListenersOfActionChange(InputActionChange.BoundControlsAboutToChange, map);
  990. }
  991. if (m_Asset != null)
  992. InputActionState.NotifyListenersOfActionChange(InputActionChange.BoundControlsAboutToChange, m_Asset);
  993. // Reuse the arrays we have so that we can avoid managed memory allocations, if possible.
  994. resolver.StartWithArraysFrom(m_State);
  995. // Throw away old memory.
  996. m_State.memory.Dispose();
  997. }
  998. // Resolve all maps in the asset.
  999. for (var i = 0; i < actionMaps.Count; ++i)
  1000. resolver.AddActionMap(actionMaps[i]);
  1001. // Install state.
  1002. if (m_State == null)
  1003. {
  1004. if (m_Asset != null)
  1005. {
  1006. var state = new InputActionState();
  1007. for (var i = 0; i < actionMaps.Count; ++i)
  1008. actionMaps[i].m_State = state;
  1009. m_Asset.m_SharedStateForAllMaps = state;
  1010. }
  1011. else
  1012. {
  1013. m_State = new InputActionState();
  1014. }
  1015. m_State.Initialize(resolver);
  1016. }
  1017. else
  1018. {
  1019. m_State.ClaimDataFrom(resolver);
  1020. }
  1021. // Wipe caches.
  1022. for (var i = 0; i < actionMaps.Count; ++i)
  1023. {
  1024. var map = actionMaps[i];
  1025. map.m_NeedToResolveBindings = false;
  1026. ////TODO: determine whether we really need to wipe this; keep them if nothing has changed
  1027. map.m_ControlsForEachAction = null;
  1028. if (map.m_SingletonAction != null)
  1029. InputActionState.NotifyListenersOfActionChange(InputActionChange.BoundControlsChanged, map.m_SingletonAction);
  1030. else if (m_Asset == null)
  1031. InputActionState.NotifyListenersOfActionChange(InputActionChange.BoundControlsChanged, map);
  1032. }
  1033. if (m_Asset != null)
  1034. InputActionState.NotifyListenersOfActionChange(InputActionChange.BoundControlsChanged, m_Asset);
  1035. // Re-enable actions.
  1036. if (hasEnabledActions)
  1037. m_State.RestoreActionStates(tempMemory);
  1038. }
  1039. finally
  1040. {
  1041. tempMemory.Dispose();
  1042. }
  1043. }
  1044. internal int FindBinding(InputBinding match)
  1045. {
  1046. var numBindings = m_Bindings.LengthSafe();
  1047. for (var i = 0; i < numBindings; ++i)
  1048. {
  1049. ref var binding = ref m_Bindings[i];
  1050. if (match.Matches(ref binding))
  1051. return i;
  1052. }
  1053. return -1;
  1054. }
  1055. #region Serialization
  1056. // Action maps are serialized in two different ways. For storage as imported assets in Unity's Library/ folder
  1057. // and in player data and asset bundles as well as for surviving domain reloads, InputActionMaps are serialized
  1058. // directly by Unity. For storage as source data in user projects, InputActionMaps are serialized indirectly
  1059. // as JSON by setting up a separate set of structs that are then read and written using Unity's JSON serializer.
  1060. [Serializable]
  1061. internal struct BindingJson
  1062. {
  1063. public string name;
  1064. public string id;
  1065. public string path;
  1066. public string interactions;
  1067. public string processors;
  1068. public string groups;
  1069. public string action;
  1070. public bool isComposite;
  1071. public bool isPartOfComposite;
  1072. public InputBinding ToBinding()
  1073. {
  1074. return new InputBinding
  1075. {
  1076. name = string.IsNullOrEmpty(name) ? null : name,
  1077. m_Id = string.IsNullOrEmpty(id) ? null : id,
  1078. path = string.IsNullOrEmpty(path) ? null : path,
  1079. action = string.IsNullOrEmpty(action) ? null : action,
  1080. interactions = string.IsNullOrEmpty(interactions) ? null : interactions,
  1081. processors = string.IsNullOrEmpty(processors) ? null : processors,
  1082. groups = string.IsNullOrEmpty(groups) ? null : groups,
  1083. isComposite = isComposite,
  1084. isPartOfComposite = isPartOfComposite,
  1085. };
  1086. }
  1087. public static BindingJson FromBinding(ref InputBinding binding)
  1088. {
  1089. return new BindingJson
  1090. {
  1091. name = binding.name,
  1092. id = binding.m_Id,
  1093. path = binding.path,
  1094. action = binding.action,
  1095. interactions = binding.interactions,
  1096. processors = binding.processors,
  1097. groups = binding.groups,
  1098. isComposite = binding.isComposite,
  1099. isPartOfComposite = binding.isPartOfComposite,
  1100. };
  1101. }
  1102. }
  1103. // Backwards-compatible read format.
  1104. [Serializable]
  1105. internal struct ReadActionJson
  1106. {
  1107. public string name;
  1108. public string type;
  1109. public string id;
  1110. public string expectedControlType;
  1111. public string expectedControlLayout;
  1112. public string processors;
  1113. public string interactions;
  1114. public bool passThrough;
  1115. public bool initialStateCheck;
  1116. // Bindings can either be on the action itself (in which case the action name
  1117. // for each binding is implied) or listed separately in the action file.
  1118. public BindingJson[] bindings;
  1119. public InputAction ToAction(string actionName = null)
  1120. {
  1121. // FormerlySerializedAs doesn't seem to work as expected so manually
  1122. // handling the rename here.
  1123. if (!string.IsNullOrEmpty(expectedControlLayout))
  1124. expectedControlType = expectedControlLayout;
  1125. // Determine type.
  1126. InputActionType actionType = default;
  1127. if (!string.IsNullOrEmpty(type))
  1128. actionType = (InputActionType)Enum.Parse(typeof(InputActionType), type, true);
  1129. else
  1130. {
  1131. // Old format that doesn't have type. Try to infer from settings.
  1132. if (passThrough)
  1133. actionType = InputActionType.PassThrough;
  1134. else if (initialStateCheck)
  1135. actionType = InputActionType.Value;
  1136. else if (!string.IsNullOrEmpty(expectedControlType) &&
  1137. (expectedControlType == "Button" || expectedControlType == "Key"))
  1138. actionType = InputActionType.Button;
  1139. }
  1140. return new InputAction(actionName ?? name, actionType)
  1141. {
  1142. m_Id = string.IsNullOrEmpty(id) ? null : id,
  1143. m_ExpectedControlType = !string.IsNullOrEmpty(expectedControlType)
  1144. ? expectedControlType
  1145. : null,
  1146. m_Processors = processors,
  1147. m_Interactions = interactions,
  1148. };
  1149. }
  1150. }
  1151. [Serializable]
  1152. internal struct WriteActionJson
  1153. {
  1154. public string name;
  1155. public string type;
  1156. public string id;
  1157. public string expectedControlType;
  1158. public string processors;
  1159. public string interactions;
  1160. public static WriteActionJson FromAction(InputAction action)
  1161. {
  1162. return new WriteActionJson
  1163. {
  1164. name = action.m_Name,
  1165. type = action.m_Type.ToString(),
  1166. id = action.m_Id,
  1167. expectedControlType = action.m_ExpectedControlType,
  1168. processors = action.processors,
  1169. interactions = action.interactions,
  1170. };
  1171. }
  1172. }
  1173. [Serializable]
  1174. internal struct ReadMapJson
  1175. {
  1176. public string name;
  1177. public string id;
  1178. public ReadActionJson[] actions;
  1179. public BindingJson[] bindings;
  1180. }
  1181. [Serializable]
  1182. internal struct WriteMapJson
  1183. {
  1184. public string name;
  1185. public string id;
  1186. public WriteActionJson[] actions;
  1187. public BindingJson[] bindings;
  1188. public static WriteMapJson FromMap(InputActionMap map)
  1189. {
  1190. WriteActionJson[] jsonActions = null;
  1191. BindingJson[] jsonBindings = null;
  1192. var actions = map.m_Actions;
  1193. if (actions != null)
  1194. {
  1195. var actionCount = actions.Length;
  1196. jsonActions = new WriteActionJson[actionCount];
  1197. for (var i = 0; i < actionCount; ++i)
  1198. jsonActions[i] = WriteActionJson.FromAction(actions[i]);
  1199. }
  1200. var bindings = map.m_Bindings;
  1201. if (bindings != null)
  1202. {
  1203. var bindingCount = bindings.Length;
  1204. jsonBindings = new BindingJson[bindingCount];
  1205. for (var i = 0; i < bindingCount; ++i)
  1206. jsonBindings[i] = BindingJson.FromBinding(ref bindings[i]);
  1207. }
  1208. return new WriteMapJson
  1209. {
  1210. name = map.name,
  1211. id = map.id.ToString(),
  1212. actions = jsonActions,
  1213. bindings = jsonBindings,
  1214. };
  1215. }
  1216. }
  1217. // We write JSON in a less flexible format than we allow to be read. JSON files
  1218. // we read can just be flat lists of actions with the map name being contained in
  1219. // the action name and containing their own bindings directly. JSON files we write
  1220. // go map by map and separate bindings and actions.
  1221. [Serializable]
  1222. internal struct WriteFileJson
  1223. {
  1224. public WriteMapJson[] maps;
  1225. public static WriteFileJson FromMap(InputActionMap map)
  1226. {
  1227. return new WriteFileJson
  1228. {
  1229. maps = new[] {WriteMapJson.FromMap(map)}
  1230. };
  1231. }
  1232. public static WriteFileJson FromMaps(IEnumerable<InputActionMap> maps)
  1233. {
  1234. var mapCount = maps.Count();
  1235. if (mapCount == 0)
  1236. return new WriteFileJson();
  1237. var mapsJson = new WriteMapJson[mapCount];
  1238. var index = 0;
  1239. foreach (var map in maps)
  1240. mapsJson[index++] = WriteMapJson.FromMap(map);
  1241. return new WriteFileJson {maps = mapsJson};
  1242. }
  1243. }
  1244. // A JSON representation of one or more sets of actions.
  1245. // Contains a list of actions. Each action may specify the set it belongs to
  1246. // as part of its name ("set/action").
  1247. [Serializable]
  1248. internal struct ReadFileJson
  1249. {
  1250. public ReadActionJson[] actions;
  1251. public ReadMapJson[] maps;
  1252. public InputActionMap[] ToMaps()
  1253. {
  1254. var mapList = new List<InputActionMap>();
  1255. var actionLists = new List<List<InputAction>>();
  1256. var bindingLists = new List<List<InputBinding>>();
  1257. // Process actions listed at toplevel.
  1258. var actionCount = actions?.Length ?? 0;
  1259. for (var i = 0; i < actionCount; ++i)
  1260. {
  1261. var jsonAction = actions[i];
  1262. if (string.IsNullOrEmpty(jsonAction.name))
  1263. throw new InvalidOperationException($"Action number {i + 1} has no name");
  1264. ////REVIEW: make sure all action names are unique?
  1265. // Determine name of action map.
  1266. string mapName = null;
  1267. var actionName = jsonAction.name;
  1268. var indexOfFirstSlash = actionName.IndexOf('/');
  1269. if (indexOfFirstSlash != -1)
  1270. {
  1271. mapName = actionName.Substring(0, indexOfFirstSlash);
  1272. actionName = actionName.Substring(indexOfFirstSlash + 1);
  1273. if (string.IsNullOrEmpty(actionName))
  1274. throw new InvalidOperationException(
  1275. $"Invalid action name '{jsonAction.name}' (missing action name after '/')");
  1276. }
  1277. // Try to find existing map.
  1278. InputActionMap map = null;
  1279. var mapIndex = 0;
  1280. for (; mapIndex < mapList.Count; ++mapIndex)
  1281. {
  1282. if (string.Compare(mapList[mapIndex].name, mapName, StringComparison.InvariantCultureIgnoreCase) == 0)
  1283. {
  1284. map = mapList[mapIndex];
  1285. break;
  1286. }
  1287. }
  1288. // Create new map if it's the first action in the map.
  1289. if (map == null)
  1290. {
  1291. // NOTE: No map IDs supported on this path.
  1292. map = new InputActionMap(mapName);
  1293. mapIndex = mapList.Count;
  1294. mapList.Add(map);
  1295. actionLists.Add(new List<InputAction>());
  1296. bindingLists.Add(new List<InputBinding>());
  1297. }
  1298. // Create action.
  1299. var action = jsonAction.ToAction(actionName);
  1300. actionLists[mapIndex].Add(action);
  1301. // Add bindings.
  1302. if (jsonAction.bindings != null)
  1303. {
  1304. var bindingsForMap = bindingLists[mapIndex];
  1305. for (var n = 0; n < jsonAction.bindings.Length; ++n)
  1306. {
  1307. var jsonBinding = jsonAction.bindings[n];
  1308. var binding = jsonBinding.ToBinding();
  1309. binding.action = action.m_Name;
  1310. bindingsForMap.Add(binding);
  1311. }
  1312. }
  1313. }
  1314. // Process maps.
  1315. var mapCount = maps?.Length ?? 0;
  1316. for (var i = 0; i < mapCount; ++i)
  1317. {
  1318. var jsonMap = maps[i];
  1319. var mapName = jsonMap.name;
  1320. if (string.IsNullOrEmpty(mapName))
  1321. throw new InvalidOperationException($"Map number {i + 1} has no name");
  1322. // Try to find existing map.
  1323. InputActionMap map = null;
  1324. var mapIndex = 0;
  1325. for (; mapIndex < mapList.Count; ++mapIndex)
  1326. {
  1327. if (string.Compare(mapList[mapIndex].name, mapName, StringComparison.InvariantCultureIgnoreCase) == 0)
  1328. {
  1329. map = mapList[mapIndex];
  1330. break;
  1331. }
  1332. }
  1333. // Create new map if we haven't seen it before.
  1334. if (map == null)
  1335. {
  1336. map = new InputActionMap(mapName)
  1337. {
  1338. m_Id = string.IsNullOrEmpty(jsonMap.id) ? null : jsonMap.id
  1339. };
  1340. mapIndex = mapList.Count;
  1341. mapList.Add(map);
  1342. actionLists.Add(new List<InputAction>());
  1343. bindingLists.Add(new List<InputBinding>());
  1344. }
  1345. // Process actions in map.
  1346. var actionCountInMap = jsonMap.actions?.Length ?? 0;
  1347. for (var n = 0; n < actionCountInMap; ++n)
  1348. {
  1349. var jsonAction = jsonMap.actions[n];
  1350. if (string.IsNullOrEmpty(jsonAction.name))
  1351. throw new InvalidOperationException($"Action number {i + 1} in map '{mapName}' has no name");
  1352. // Create action.
  1353. var action = jsonAction.ToAction();
  1354. actionLists[mapIndex].Add(action);
  1355. // Add bindings.
  1356. if (jsonAction.bindings != null)
  1357. {
  1358. var bindingList = bindingLists[mapIndex];
  1359. for (var k = 0; k < jsonAction.bindings.Length; ++k)
  1360. {
  1361. var jsonBinding = jsonAction.bindings[k];
  1362. var binding = jsonBinding.ToBinding();
  1363. binding.action = action.m_Name;
  1364. bindingList.Add(binding);
  1365. }
  1366. }
  1367. }
  1368. // Process bindings in map.
  1369. var bindingCountInMap = jsonMap.bindings?.Length ?? 0;
  1370. var bindingsForMap = bindingLists[mapIndex];
  1371. for (var n = 0; n < bindingCountInMap; ++n)
  1372. {
  1373. var jsonBinding = jsonMap.bindings[n];
  1374. var binding = jsonBinding.ToBinding();
  1375. bindingsForMap.Add(binding);
  1376. }
  1377. }
  1378. // Finalize arrays.
  1379. for (var i = 0; i < mapList.Count; ++i)
  1380. {
  1381. var map = mapList[i];
  1382. var actionArray = actionLists[i].ToArray();
  1383. var bindingArray = bindingLists[i].ToArray();
  1384. map.m_Actions = actionArray;
  1385. map.m_Bindings = bindingArray;
  1386. for (var n = 0; n < actionArray.Length; ++n)
  1387. {
  1388. var action = actionArray[n];
  1389. action.m_ActionMap = map;
  1390. }
  1391. }
  1392. return mapList.ToArray();
  1393. }
  1394. }
  1395. /// <summary>
  1396. /// Load one or more action maps from JSON.
  1397. /// </summary>
  1398. /// <param name="json">JSON representation of the action maps. Can be empty.</param>
  1399. /// <exception cref="ArgumentNullException"><paramref name="json"/> is <c>null</c>.</exception>
  1400. /// <returns>The array of action maps (may be empty) read from the given JSON string. Will not be
  1401. /// <c>null</c>.</returns>
  1402. /// <remarks>
  1403. /// Note that the format used by this method is different than what you
  1404. /// get if you call <c>JsonUtility.ToJson</c> on an InputActionMap instance. In other
  1405. /// words, the JSON format is not identical to the Unity serialized object representation
  1406. /// of the asset.
  1407. ///
  1408. /// <example>
  1409. /// <code>
  1410. /// var maps = InputActionMap.FromJson(@"
  1411. /// {
  1412. /// ""maps"" : [
  1413. /// {
  1414. /// ""name"" : ""Gameplay"",
  1415. /// ""actions"" : [
  1416. /// { ""name"" : ""fire"", ""type"" : ""button"" }
  1417. /// ],
  1418. /// ""bindings"" : [
  1419. /// { ""path"" : ""&lt;Gamepad&gt;/leftTrigger"", ""action"" : ""fire"" }
  1420. /// ],
  1421. /// }
  1422. /// ]
  1423. /// }
  1424. /// ");
  1425. /// </code>
  1426. /// </example>
  1427. /// </remarks>
  1428. /// <seealso cref="InputActionAsset.FromJson"/>
  1429. /// <seealso cref="ToJson(IEnumerable{InputActionMap})"/>
  1430. public static InputActionMap[] FromJson(string json)
  1431. {
  1432. if (json == null)
  1433. throw new ArgumentNullException(nameof(json));
  1434. var fileJson = JsonUtility.FromJson<ReadFileJson>(json);
  1435. return fileJson.ToMaps();
  1436. }
  1437. /// <summary>
  1438. /// Convert a set of action maps to JSON format.
  1439. /// </summary>
  1440. /// <param name="maps">List of action maps to serialize.</param>
  1441. /// <exception cref="ArgumentNullException"><paramref name="maps"/> is <c>null</c>.</exception>
  1442. /// <returns>JSON representation of the given action maps.</returns>
  1443. /// <remarks>
  1444. /// The result of this method can be loaded with <see cref="FromJson"/>.
  1445. ///
  1446. /// Note that the format used by this method is different than what you
  1447. /// get if you call <c>JsonUtility.ToJson</c> on an InputActionMap instance. In other
  1448. /// words, the JSON format is not identical to the Unity serialized object representation
  1449. /// of the asset.
  1450. /// </remarks>
  1451. /// <seealso cref="FromJson"/>
  1452. public static string ToJson(IEnumerable<InputActionMap> maps)
  1453. {
  1454. if (maps == null)
  1455. throw new ArgumentNullException(nameof(maps));
  1456. var fileJson = WriteFileJson.FromMaps(maps);
  1457. return JsonUtility.ToJson(fileJson, true);
  1458. }
  1459. /// <summary>
  1460. /// Convert the action map to JSON format.
  1461. /// </summary>
  1462. /// <returns>A JSON representation of the action map.</returns>
  1463. /// <remarks>
  1464. /// The result of this method can be loaded with <see cref="FromJson"/>.
  1465. ///
  1466. /// Note that the format used by this method is different than what you
  1467. /// get if you call <c>JsonUtility.ToJson</c> on an InputActionMap instance. In other
  1468. /// words, the JSON format is not identical to the Unity serialized object representation
  1469. /// of the asset.
  1470. /// </remarks>
  1471. public string ToJson()
  1472. {
  1473. var fileJson = WriteFileJson.FromMap(this);
  1474. return JsonUtility.ToJson(fileJson, true);
  1475. }
  1476. /// <summary>
  1477. /// Called by Unity before the action map is serialized using Unity's
  1478. /// serialization system.
  1479. /// </summary>
  1480. public void OnBeforeSerialize()
  1481. {
  1482. }
  1483. /// <summary>
  1484. /// Called by Unity after the action map has been deserialized using Unity's
  1485. /// serialization system.
  1486. /// </summary>
  1487. public void OnAfterDeserialize()
  1488. {
  1489. m_State = null;
  1490. m_MapIndexInState = InputActionState.kInvalidIndex;
  1491. // Restore references of actions linking back to us.
  1492. if (m_Actions != null)
  1493. {
  1494. var actionCount = m_Actions.Length;
  1495. for (var i = 0; i < actionCount; ++i)
  1496. m_Actions[i].m_ActionMap = this;
  1497. }
  1498. // Make sure we don't retain any cached per-action data when using serialization
  1499. // to doctor around in action map configurations in the editor.
  1500. ClearPerActionCachedBindingData();
  1501. }
  1502. #endregion
  1503. }
  1504. }