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.

InputActionMap.cs 86KB

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