Нема описа
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.

InputActionParameters.cs 58KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using UnityEngine.InputSystem.Utilities;
  8. namespace UnityEngine.InputSystem
  9. {
  10. partial class InputActionRebindingExtensions
  11. {
  12. /// <summary>
  13. /// Return the current value of the given parameter as found on the processors, interactions, or composites
  14. /// of the action's current bindings.
  15. /// </summary>
  16. /// <param name="action">Action on whose bindings to look for the value of the given parameter.</param>
  17. /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
  18. /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
  19. /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
  20. /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
  21. /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
  22. /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will be taken into account.</param>
  23. /// <returns>The current value of the given parameter or <c>null</c> if the parameter could not be found.</returns>
  24. /// <remarks>
  25. /// Parameters are found on interactions (<see cref="IInputInteraction"/>), processors (<see cref="InputProcessor"/>), and
  26. /// composites (see <see cref="InputBindingComposite"/>) that are applied to bindings. For example, the following binding
  27. /// adds a <c>Hold</c> interaction with a custom <c>duration</c> parameter on top of binding to the gamepad's A button:
  28. ///
  29. /// <example>
  30. /// <code>
  31. /// new InputBinding
  32. /// {
  33. /// path = "&lt;Gamepad&gt;/buttonSouth",
  34. /// interactions = "hold(duration=0.6)"
  35. /// };
  36. /// </code>
  37. /// </example>
  38. ///
  39. /// In the editor UI, parameters are set graphically from the properties sections in the right-most pane
  40. /// in the action editor when an action or a binding is selected.
  41. ///
  42. /// When the binding above is applied to an action, the <c>duration</c> parameter from the <c>Hold</c> interaction can be
  43. /// queried like so:
  44. ///
  45. /// <example>
  46. /// <code>
  47. /// action.GetParameterValue("duration") // Returns 0.6
  48. /// </code>
  49. /// </example>
  50. ///
  51. /// Note that if there are multiple objects on the action that use the same parameter name, the value of the <em>first</em> parameter
  52. /// that is encountered is returned. Also note that this method will create GC heap garbage.
  53. ///
  54. /// The type of object to query the parameter from can be include in the <paramref name="name"/> parameter. For example, if
  55. /// an action has both a <see cref="Interactions.TapInteraction"/> and a <see cref="Interactions.HoldInteraction"/> on it, the
  56. /// <c>duration</c> parameter can be queried independently like so:
  57. ///
  58. /// <example>
  59. /// <code>
  60. /// // Query "duration" from "hold":
  61. /// action.GetParameterValue("hold:duration");
  62. ///
  63. /// // Query "duration" from "tap":
  64. /// action.GetParameterValue("tap:duration");
  65. /// </code>
  66. /// </example>
  67. ///
  68. /// The names used here to identify the object holding the parameter are the same used by <see cref="InputSystem.RegisterInteraction"/>,
  69. /// <see cref="InputSystem.RegisterBindingComposite"/>, and <see cref="InputSystem.RegisterProcessor"/>.
  70. /// </remarks>
  71. /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c></exception>
  72. /// <seealso cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
  73. /// <seealso cref="ApplyBindingOverride(InputAction,string,string,string)"/>
  74. /// <seealso cref="Editor.InputParameterEditor"/>
  75. public static PrimitiveValue? GetParameterValue(this InputAction action, string name, InputBinding bindingMask = default)
  76. {
  77. if (action == null)
  78. throw new ArgumentNullException(nameof(action));
  79. if (string.IsNullOrEmpty(name))
  80. throw new ArgumentNullException(nameof(name));
  81. return action.GetParameterValue(new ParameterOverride(name, bindingMask));
  82. }
  83. private static PrimitiveValue? GetParameterValue(this InputAction action, ParameterOverride parameterOverride)
  84. {
  85. parameterOverride.bindingMask.action = action.name;
  86. var actionMap = action.GetOrCreateActionMap();
  87. actionMap.ResolveBindingsIfNecessary();
  88. foreach (var parameter in new ParameterEnumerable(actionMap.m_State, parameterOverride, actionMap.m_MapIndexInState))
  89. {
  90. var value = parameter.field.GetValue(parameter.instance);
  91. return PrimitiveValue.FromObject(value);
  92. }
  93. return null;
  94. }
  95. /// <summary>
  96. /// Return the current value of the given parameter as found on the processors, interactions, or composites
  97. /// of the action's current bindings.
  98. /// </summary>
  99. /// <param name="action">Action on whose bindings to look for the value of the given parameter.</param>
  100. /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
  101. /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
  102. /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
  103. /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
  104. /// <param name="bindingIndex">Index of the binding in <paramref name="action"/>'s <see cref="InputAction.bindings"/>
  105. /// to look for processors, interactions, and composites on.</param>
  106. /// <returns>The current value of the given parameter or <c>null</c> if the parameter not could be found.</returns>
  107. /// <remarks>
  108. /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
  109. /// to specifically target a single binding by index. Otherwise, the method is identical in functionality.
  110. /// </remarks>
  111. /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c></exception>
  112. public static PrimitiveValue? GetParameterValue(this InputAction action, string name, int bindingIndex)
  113. {
  114. if (action == null)
  115. throw new ArgumentNullException(nameof(action));
  116. if (string.IsNullOrEmpty(name))
  117. throw new ArgumentNullException(nameof(name));
  118. if (bindingIndex < 0)
  119. throw new ArgumentOutOfRangeException(nameof(bindingIndex));
  120. var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(bindingIndex);
  121. var bindingMask = new InputBinding { id = action.GetOrCreateActionMap().bindings[indexOnMap].id };
  122. return action.GetParameterValue(name, bindingMask);
  123. }
  124. /// <summary>
  125. /// Return the current value of the given parameter as found on the processors, interactions, or composites
  126. /// of the action's current bindings.
  127. /// </summary>
  128. /// <param name="action">Action on whose bindings to look for the value of the given parameter.</param>
  129. /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
  130. /// name and type of the parameter being looked for.</param>
  131. /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
  132. /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will be taken into account.</param>
  133. /// <returns>The current value of the given parameter or <c>null</c> if the parameter not could be found.</returns>
  134. /// <remarks>
  135. /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
  136. /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
  137. /// type-safe and does not involve strings.
  138. ///
  139. /// <example>
  140. /// <code>
  141. /// // Get the "duration" parameter from a TapInteraction.
  142. /// // This is equivalent to calling GetParameterValue("tap:duration")
  143. /// // but will return a float? instead of a PrimitiveValue?.
  144. /// action.GetParameterValue((TapInteraction x) => x.duration)
  145. /// </code>
  146. /// </example>
  147. /// </remarks>
  148. /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c></exception>
  149. /// <seealso cref="ApplyParameterOverride{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},TValue,InputBinding)"/>
  150. public static unsafe TValue? GetParameterValue<TObject, TValue>(this InputAction action, Expression<Func<TObject, TValue>> expr, InputBinding bindingMask = default)
  151. where TValue : struct
  152. {
  153. if (action == null)
  154. throw new ArgumentNullException(nameof(action));
  155. if (expr == null)
  156. throw new ArgumentNullException(nameof(expr));
  157. var parameterOverride = ExtractParameterOverride(expr, bindingMask);
  158. var value = action.GetParameterValue(parameterOverride);
  159. if (value == null)
  160. return null;
  161. // Type is guaranteed to match but just in case,
  162. // make extra sure with a check here.
  163. if (Type.GetTypeCode(typeof(TValue)) == value.Value.type)
  164. {
  165. // Can't just cast here so use UnsafeUtility to work around that.
  166. var v = value.Value;
  167. var result = default(TValue);
  168. UnsafeUtility.MemCpy(UnsafeUtility.AddressOf(ref result),
  169. v.valuePtr,
  170. UnsafeUtility.SizeOf<TValue>());
  171. return result;
  172. }
  173. // Shouldn't get here but just in case, do a conversion using C#'s Convert
  174. // machinery as a fallback.
  175. return (TValue)Convert.ChangeType(value.Value.ToObject(), typeof(TValue));
  176. }
  177. /// <summary>
  178. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  179. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
  180. /// </summary>
  181. /// <param name="action">An action on whose <see cref="InputAction.bindings"/> to look for objects to set
  182. /// the parameter value on.</param>
  183. /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
  184. /// name and type of the parameter whose value to set.</param>
  185. /// <param name="value">New value to assign to the parameter.</param>
  186. /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
  187. /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will have the override applied to them.</param>
  188. /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c>
  189. /// or empty.</exception>
  190. /// <remarks>
  191. /// This method is a variation of <see cref="ApplyParameterOverride(InputAction,string,PrimitiveValue,InputBinding)"/>
  192. /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
  193. /// type-safe and does not involve strings.
  194. ///
  195. /// <example>
  196. /// <code>
  197. /// // Override the "duration" parameter from a TapInteraction.
  198. /// // This is equivalent to calling ApplyParameterOverride("tap:duration", 0.4f).
  199. /// action.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
  200. /// </code>
  201. /// </example>
  202. /// </remarks>
  203. /// <seealso cref="GetParameterValue{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},InputBinding)"/>
  204. public static void ApplyParameterOverride<TObject, TValue>(this InputAction action, Expression<Func<TObject, TValue>> expr, TValue value,
  205. InputBinding bindingMask = default)
  206. where TValue : struct
  207. {
  208. if (action == null)
  209. throw new ArgumentNullException(nameof(action));
  210. if (expr == null)
  211. throw new ArgumentNullException(nameof(expr));
  212. var actionMap = action.GetOrCreateActionMap();
  213. actionMap.ResolveBindingsIfNecessary();
  214. bindingMask.action = action.name;
  215. var parameterOverride = ExtractParameterOverride(expr, bindingMask, PrimitiveValue.From(value));
  216. ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
  217. ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
  218. parameterOverride);
  219. }
  220. /// <summary>
  221. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  222. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of <paramref name="actionMap"/>.
  223. /// </summary>
  224. /// <param name="actionMap">An action on whose <see cref="InputActionMap.bindings"/> to look for objects to set
  225. /// the parameter value on.</param>
  226. /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
  227. /// name and type of the parameter whose value to set.</param>
  228. /// <param name="value">New value to assign to the parameter.</param>
  229. /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
  230. /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will have the override applied to them.</param>
  231. /// <exception cref="ArgumentNullException"><paramref name="actionMap"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c>
  232. /// or empty.</exception>
  233. /// <remarks>
  234. /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
  235. /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
  236. /// type-safe and does not involve strings.
  237. ///
  238. /// <example>
  239. /// <code>
  240. /// // Override the "duration" parameter from a TapInteraction.
  241. /// // This is equivalent to calling mApplyParameterOverride("tap:duration", 0.4f).
  242. /// actionMap.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
  243. /// </code>
  244. /// </example>
  245. /// </remarks>
  246. /// <seealso cref="GetParameterValue{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},InputBinding)"/>
  247. public static void ApplyParameterOverride<TObject, TValue>(this InputActionMap actionMap, Expression<Func<TObject, TValue>> expr, TValue value,
  248. InputBinding bindingMask = default)
  249. where TValue : struct
  250. {
  251. if (actionMap == null)
  252. throw new ArgumentNullException(nameof(actionMap));
  253. if (expr == null)
  254. throw new ArgumentNullException(nameof(expr));
  255. actionMap.ResolveBindingsIfNecessary();
  256. var parameterOverride = ExtractParameterOverride(expr, bindingMask, PrimitiveValue.From(value));
  257. ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
  258. ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
  259. parameterOverride);
  260. }
  261. /// <summary>
  262. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  263. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of the <see cref="InputActionAsset.actionMaps"/>
  264. /// in <paramref name="asset"/>.
  265. /// </summary>
  266. /// <param name="asset">An asset on whose <see cref="InputActionMap.bindings"/> to look for objects to set
  267. /// the parameter value on.</param>
  268. /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
  269. /// name and type of the parameter whose value to set.</param>
  270. /// <param name="value">New value to assign to the parameter.</param>
  271. /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
  272. /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will have the override applied to them.</param>
  273. /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c>
  274. /// or empty.</exception>
  275. /// <remarks>
  276. /// This method is a variation of <see cref="ApplyParameterOverride(InputActionAsset,string,PrimitiveValue,InputBinding)"/>
  277. /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
  278. /// type-safe and does not involve strings.
  279. ///
  280. /// <example>
  281. /// <code>
  282. /// // Override the "duration" parameter from a TapInteraction.
  283. /// // This is equivalent to calling mApplyParameterOverride("tap:duration", 0.4f).
  284. /// asset.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
  285. /// </code>
  286. /// </example>
  287. /// </remarks>
  288. /// <seealso cref="GetParameterValue{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},InputBinding)"/>
  289. public static void ApplyParameterOverride<TObject, TValue>(this InputActionAsset asset, Expression<Func<TObject, TValue>> expr, TValue value,
  290. InputBinding bindingMask = default)
  291. where TValue : struct
  292. {
  293. if (asset == null)
  294. throw new ArgumentNullException(nameof(asset));
  295. if (expr == null)
  296. throw new ArgumentNullException(nameof(expr));
  297. asset.ResolveBindingsIfNecessary();
  298. var parameterOverride = ExtractParameterOverride(expr, bindingMask, PrimitiveValue.From(value));
  299. ApplyParameterOverride(asset.m_SharedStateForAllMaps, -1,
  300. ref asset.m_ParameterOverrides, ref asset.m_ParameterOverridesCount,
  301. parameterOverride);
  302. }
  303. private static ParameterOverride ExtractParameterOverride<TObject, TValue>(Expression<Func<TObject, TValue>> expr,
  304. InputBinding bindingMask = default, PrimitiveValue value = default)
  305. {
  306. if (!(expr is LambdaExpression lambda))
  307. throw new ArgumentException($"Expression must be a LambdaExpression but was a {expr.GetType().Name} instead", nameof(expr));
  308. if (!(lambda.Body is MemberExpression body))
  309. {
  310. // If the field type in the lambda doesn't match the TValue type being used,
  311. // but there is a coercion, the compiler will automatically insert a Convert(x.name, TValue)
  312. // expression.
  313. if (lambda.Body is UnaryExpression unary && unary.NodeType == ExpressionType.Convert && unary.Operand is MemberExpression b)
  314. {
  315. body = b;
  316. }
  317. else
  318. {
  319. throw new ArgumentException(
  320. $"Body in LambdaExpression must be a MemberExpression (x.name) but was a {expr.GetType().Name} instead",
  321. nameof(expr));
  322. }
  323. }
  324. string objectRegistrationName;
  325. if (typeof(InputProcessor).IsAssignableFrom(typeof(TObject)))
  326. objectRegistrationName = InputProcessor.s_Processors.FindNameForType(typeof(TObject));
  327. else if (typeof(IInputInteraction).IsAssignableFrom(typeof(TObject)))
  328. objectRegistrationName = InputInteraction.s_Interactions.FindNameForType(typeof(TObject));
  329. else if (typeof(InputBindingComposite).IsAssignableFrom(typeof(TObject)))
  330. objectRegistrationName = InputBindingComposite.s_Composites.FindNameForType(typeof(TObject));
  331. else
  332. throw new ArgumentException(
  333. $"Given type must be an InputProcessor, IInputInteraction, or InputBindingComposite (was {typeof(TObject).Name})",
  334. nameof(TObject));
  335. return new ParameterOverride(objectRegistrationName, body.Member.Name, bindingMask, value);
  336. }
  337. /// <summary>
  338. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  339. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of <paramref name="actionMap"/>.
  340. /// </summary>
  341. /// <param name="actionMap">An action map on whose <see cref="InputActionMap.bindings"/> to look for objects to set
  342. /// the parameter value on.</param>
  343. /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
  344. /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
  345. /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
  346. /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
  347. /// <param name="value">New value to assign to the parameter.</param>
  348. /// <param name="bindingMask">A binding mask that determines which of <paramref name="actionMap"/>'s <see cref="InputActionMap.bindings"/>
  349. /// to apply the override to. By default this is empty which leads to the override to be applied to all bindings in the map.</param>
  350. /// <remarks>
  351. /// This method both directly applies the new value and also stores the override internally.
  352. ///
  353. /// If an override for the same parameter <paramref name="name"/> and with the same <paramref name="bindingMask"/> already exists,
  354. /// its value is simply updated. No new override will be created.
  355. ///
  356. /// You can use this method to set parameters (public fields) on composites, interactions, and processors that are created
  357. /// from bindings.
  358. ///
  359. /// <example>
  360. /// <code>
  361. /// // Create an action map with two actions.
  362. /// var map = new InputActionMap();
  363. /// var action1 = map.AddAction("action1");
  364. /// var action2 = map.AddAction("action2");
  365. ///
  366. /// // Add a binding to each action to which a "ClampProcessor" is applied.
  367. /// // This processor has two parameters:
  368. /// // - "min" (float)
  369. /// // - "max" (float)
  370. /// action1.AddBinding("&gt;Gamepad&gt;/rightTrigger", processors: "clamp(min=0.2,max=0.8)");
  371. /// action2.AddBinding("&gt;Gamepad&gt;/leftTrigger", processors: "clamp(min=0.2,max=0.8)");
  372. ///
  373. /// // Apply parameter overrides to set the values differently.
  374. /// // This will apply the setting to *both* the bindings on action1 *and* action2.
  375. /// map.ApplyParameterOverride("min", 0.3f);
  376. /// map.ApplyParameterOverride("max", 0.9f);
  377. /// </code>
  378. /// </example>
  379. ///
  380. /// An override can optionally be directed at a specific type of object.
  381. ///
  382. /// <example>
  383. /// <code>
  384. /// map.ApplyParameterOverride("clamp:min", 0.3f);
  385. /// map.ApplyParameterOverride("clamp:max", 0.9f);
  386. /// </code>
  387. /// </example>
  388. ///
  389. /// By default, the parameter override will apply to all bindings in the map. To limit the override
  390. /// to specific bindings, you can supply a <paramref name="bindingMask"/>.
  391. ///
  392. /// <example>
  393. /// <code>
  394. /// // Apply a parameter override only to action1.
  395. /// map.ApplyBindingOverride("clamp:min", 0.25f, new InputBinding { action = action1.name });
  396. ///
  397. /// // Apply a parameter override only to a specific binding path.
  398. /// map.ApplyBindingOverride("clamp:min", 0.4f, new InputBinding { path = "&lt;Gamepad&gt;/leftTrigger" });
  399. /// </code>
  400. /// </example>
  401. ///
  402. /// If multiple overrides exist for the same parameter, an attempt is made to choose the override that is most specific.
  403. /// Say, that you apply an override for <c>"duration"</c> on an entire <see cref="InputActionAsset"/> using
  404. /// <see cref="ApplyParameterOverride(InputActionAsset,String,PrimitiveValue,InputBinding)"/>. But then you also apply
  405. /// an override to just an individual <see cref="InputAction"/> inside the asset. In this case, the <c>"duration"</c>
  406. /// override for just that action will be applied to bindings of that action and the override inside the asset will
  407. /// be applied to bindings of all other actions. Note that if multiple overrides exist that could all be considered
  408. /// equally valid, the behavior is undecided.
  409. ///
  410. /// Note that parameter overrides stay in place on the map. Like binding overrides, however, they are not
  411. /// automatically persisted and thus need to be reapplied when actions are loaded from assets. This will, however, be applied
  412. /// automatically to bindings added to the action in the future as well as whenever bindings are resolved to controls.
  413. /// </remarks>
  414. /// <exception cref="ArgumentNullException"><paramref name="actionMap"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
  415. /// or empty.</exception>
  416. /// <seealso cref="GetParameterValue(InputAction,string,InputBinding)"/>
  417. public static void ApplyParameterOverride(this InputActionMap actionMap, string name, PrimitiveValue value, InputBinding bindingMask = default)
  418. {
  419. if (actionMap == null)
  420. throw new ArgumentNullException(nameof(actionMap));
  421. if (string.IsNullOrEmpty(name))
  422. throw new ArgumentNullException(nameof(name));
  423. actionMap.ResolveBindingsIfNecessary();
  424. ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
  425. ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
  426. new ParameterOverride(name, bindingMask, value));
  427. }
  428. /// <summary>
  429. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  430. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of each of the <see cref="InputActionAsset.actionMaps"/>
  431. /// in <paramref name="asset"/>.
  432. /// </summary>
  433. /// <param name="asset">An <c>.inputactions</c> asset on whose <see cref="InputActionMap.bindings"/> to look for objects to set
  434. /// the parameter value on.</param>
  435. /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
  436. /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
  437. /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
  438. /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
  439. /// <param name="value">New value to assign to the parameter.</param>
  440. /// <param name="bindingMask">A binding mask that determines which of the <see cref="InputActionMap.bindings"/>
  441. /// to apply the override to. By default this is empty which leads to the override to be applied to all bindings in the asset.</param>
  442. /// <remarks>
  443. /// This method both directly applies the new value and also stores the override internally.
  444. ///
  445. /// If an override for the same parameter <paramref name="name"/> and with the same <paramref name="bindingMask"/> already exists,
  446. /// its value is simply updated. No new override will be created.
  447. ///
  448. /// You can use this method to set parameters (public fields) on composites, interactions, and processors that are created
  449. /// from bindings.
  450. ///
  451. /// <example>
  452. /// <code>
  453. /// // Create an asset with one action map and two actions.
  454. /// var asset = ScriptableObject.CreateInstance&lt;InputActionAsset&gt;();
  455. /// var map = asset.AddActionMap("map");
  456. /// var action1 = map.AddAction("action1");
  457. /// var action2 = map.AddAction("action2");
  458. ///
  459. /// // Add a binding to each action to which a "ClampProcessor" is applied.
  460. /// // This processor has two parameters:
  461. /// // - "min" (float)
  462. /// // - "max" (float)
  463. /// action1.AddBinding("&gt;Gamepad&gt;/rightTrigger", processors: "clamp(min=0.2,max=0.8)");
  464. /// action2.AddBinding("&gt;Gamepad&gt;/leftTrigger", processors: "clamp(min=0.2,max=0.8)");
  465. ///
  466. /// // Apply parameter overrides to set the values differently.
  467. /// // This will apply the setting to *both* the bindings on action1 *and* action2.
  468. /// asset.ApplyParameterOverride("min", 0.3f);
  469. /// asset.ApplyParameterOverride("max", 0.9f);
  470. /// </code>
  471. /// </example>
  472. ///
  473. /// An override can optionally be directed at a specific type of object.
  474. ///
  475. /// <example>
  476. /// <code>
  477. /// asset.ApplyParameterOverride("clamp:min", 0.3f);
  478. /// asset.ApplyParameterOverride("clamp:max", 0.9f);
  479. /// </code>
  480. /// </example>
  481. ///
  482. /// By default, the parameter override will apply to all bindings in the asset. To limit the override
  483. /// to specific bindings, you can supply a <paramref name="bindingMask"/>.
  484. ///
  485. /// <example>
  486. /// <code>
  487. /// // Apply a parameter override only to action1.
  488. /// asset.ApplyBindingOverride("clamp:min", 0.25f, new InputBinding { action = action1.name });
  489. ///
  490. /// // Apply a parameter override only to a specific binding path.
  491. /// asset.ApplyBindingOverride("clamp:min", 0.4f, new InputBinding { path = "&lt;Gamepad&gt;/leftTrigger" });
  492. /// </code>
  493. /// </example>
  494. ///
  495. /// If multiple overrides exist for the same parameter, an attempt is made to choose the override that is most specific.
  496. /// Say, that you apply an override for <c>"duration"</c> on an entire <see cref="InputActionAsset"/> using
  497. /// <see cref="ApplyParameterOverride(InputActionAsset,String,PrimitiveValue,InputBinding)"/>. But then you also apply
  498. /// an override to just an individual <see cref="InputAction"/> inside the asset. In this case, the <c>"duration"</c>
  499. /// override for just that action will be applied to bindings of that action and the override inside the asset will
  500. /// be applied to bindings of all other actions. Note that if multiple overrides exist that could all be considered
  501. /// equally valid, the behavior is undecided.
  502. ///
  503. /// Note that parameter overrides stay in place on the map. Like binding overrides, however, they are not
  504. /// automatically persisted and thus need to be reapplied when actions are loaded from assets. This will, however, be applied
  505. /// automatically to bindings added to the action in the future as well as whenever bindings are resolved to controls.
  506. /// </remarks>
  507. /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
  508. /// or empty.</exception>
  509. /// <seealso cref="GetParameterValue(InputAction,string,InputBinding)"/>
  510. public static void ApplyParameterOverride(this InputActionAsset asset, string name, PrimitiveValue value, InputBinding bindingMask = default)
  511. {
  512. if (asset == null)
  513. throw new ArgumentNullException(nameof(asset));
  514. if (string.IsNullOrEmpty(name))
  515. throw new ArgumentNullException(nameof(name));
  516. asset.ResolveBindingsIfNecessary();
  517. ApplyParameterOverride(asset.m_SharedStateForAllMaps, -1,
  518. ref asset.m_ParameterOverrides, ref asset.m_ParameterOverridesCount,
  519. new ParameterOverride(name, bindingMask, value));
  520. }
  521. /// <summary>
  522. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  523. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
  524. /// </summary>
  525. /// <param name="action">An action on whose <see cref="InputAction.bindings"/> to look for objects to set
  526. /// the parameter value on.</param>
  527. /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
  528. /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
  529. /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
  530. /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
  531. /// <param name="value">New value to assign to the parameter.</param>
  532. /// <param name="bindingMask">A binding mask that determines which of <paramref name="action"/>'s <see cref="InputAction.bindings"/>
  533. /// to apply the override to. By default this is empty which leads to the override to be applied to all bindings of the action.</param>
  534. /// <remarks>
  535. /// This method both directly applies the new value and also stores the override internally.
  536. ///
  537. /// If an override for the same parameter <paramref name="name"/> on the same <paramref name="action"/> and with the same
  538. /// <paramref name="bindingMask"/> already exists, its value is simply updated. No new override will be created.
  539. ///
  540. /// You can use this method to set parameters (public fields) on composites, interactions, and processors that are created
  541. /// from bindings.
  542. ///
  543. /// <example>
  544. /// <code>
  545. /// // Create an action with a binding that has a "ClampProcessor" applied to it.
  546. /// // This processor has two parameters:
  547. /// // - "min" (float)
  548. /// // - "max" (float)
  549. /// var action = new InputAction(binding: "&gt;Gamepad&gt;/rightTrigger", processors: "clamp(min=0.2,max=0.8)");
  550. ///
  551. /// // Apply parameter overrides to set the values differently.
  552. /// action.ApplyParameterOverride("min", 0.3f);
  553. /// action.ApplyParameterOverride("max", 0.9f);
  554. /// </code>
  555. /// </example>
  556. ///
  557. /// An override can optionally be directed at a specific type of object.
  558. ///
  559. /// <example>
  560. /// <code>
  561. /// // Create an action with both a "tap" and a "hold" interaction. Both have a
  562. /// // "duration" parameter.
  563. /// var action = new InputAction(binding: "&lt;Gamepad&gt;/buttonSouth", interactions: "tap;hold");
  564. ///
  565. /// // Apply parameter overrides individually to the two.
  566. /// action.ApplyParameterOverride("tap:duration", 0.6f);
  567. /// action.ApplyParameterOverride("hold:duration", 4f);
  568. /// </code>
  569. /// </example>
  570. ///
  571. /// By default, the parameter override will apply to all bindings on the action. To limit the override
  572. /// to specific bindings, you can supply a <paramref name="bindingMask"/>.
  573. ///
  574. /// <example>
  575. /// <code>
  576. /// // Create a "look" style action with a mouse and a gamepad binding.
  577. /// var lookAction = new InputAction();
  578. /// lookAction.AddBinding("&lt;Mouse&gt;/delta", processors: "scaleVector2", groups: "Mouse");
  579. /// lookAction.AddBinding("&lt;Gamepad&gt;/rightStick", processors: "scaleVector2", groups: "Gamepad");
  580. ///
  581. /// // Override scaling of the mouse delta individually.
  582. /// lookAction.ApplyBindingOverride("scaleVector2:x", 0.25f, InputBinding.MaskByGroup("Mouse"));
  583. /// lookAction.ApplyBindingOverride("scaleVector2:y", 0.25f, InputBinding.MaskByGroup("Mouse"));
  584. ///
  585. /// // Can also do that by path.
  586. /// lookAction.ApplyBindingOverride("scaleVector2:x", 0.25f, new InputBinding("&lt;Mouse&gt;/delta"));
  587. /// lookAction.ApplyBindingOverride("scaleVector2:y", 0.25f, new InputBinding("&lt;Mouse&gt;/delta"));
  588. /// </code>
  589. /// </example>
  590. ///
  591. /// Note that parameter overrides stay in place on the action. Like binding overrides, however, they are not
  592. /// automatically persisted and thus need to be reapplied when actions are loaded from assets. This will, however, be applied
  593. /// automatically to bindings added to the action in the future as well as whenever bindings for the action are resolved.
  594. /// </remarks>
  595. /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
  596. /// or empty.</exception>
  597. /// <seealso cref="GetParameterValue(InputAction,string,InputBinding)"/>
  598. public static void ApplyParameterOverride(this InputAction action, string name, PrimitiveValue value, InputBinding bindingMask = default)
  599. {
  600. if (action == null)
  601. throw new ArgumentNullException(nameof(action));
  602. if (name == null)
  603. throw new ArgumentNullException(nameof(name));
  604. var actionMap = action.GetOrCreateActionMap();
  605. actionMap.ResolveBindingsIfNecessary();
  606. bindingMask.action = action.name;
  607. ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
  608. ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
  609. new ParameterOverride(name, bindingMask, value));
  610. }
  611. /// <summary>
  612. /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
  613. /// and <see cref="InputProcessor"/> objects found on the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
  614. /// </summary>
  615. /// <param name="action">An action on whose <see cref="InputAction.bindings"/> to look for objects to set
  616. /// the parameter value on.</param>
  617. /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
  618. /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
  619. /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
  620. /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
  621. /// <param name="value">New value to assign to the parameter.</param>
  622. /// <param name="bindingIndex">Index of the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/> to which
  623. /// to restrict the parameter override to.</param>
  624. /// <exception cref="ArgumentOutOfRangeException"><paramref name="bindingIndex"/> is negative or equal or greater than the number of
  625. /// <see cref="InputAction.bindings"/> of <paramref name="action"/>.</exception>
  626. /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
  627. /// or empty.</exception>
  628. /// <remarks>
  629. /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/> which
  630. /// allows specifying a binding by index. It otherwise behaves identically to that method.
  631. /// </remarks>
  632. public static void ApplyParameterOverride(this InputAction action, string name, PrimitiveValue value, int bindingIndex)
  633. {
  634. if (action == null)
  635. throw new ArgumentNullException(nameof(action));
  636. if (string.IsNullOrEmpty(name))
  637. throw new ArgumentNullException(nameof(name));
  638. if (bindingIndex < 0)
  639. throw new ArgumentOutOfRangeException(nameof(bindingIndex));
  640. var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(bindingIndex);
  641. var bindingMask = new InputBinding { id = action.GetOrCreateActionMap().bindings[indexOnMap].id };
  642. action.ApplyParameterOverride(name, value, bindingMask);
  643. }
  644. private static void ApplyParameterOverride(InputActionState state, int mapIndex,
  645. ref ParameterOverride[] parameterOverrides, ref int parameterOverridesCount, ParameterOverride parameterOverride)
  646. {
  647. // Update the parameter overrides on the map or asset.
  648. var haveExistingOverride = false;
  649. if (parameterOverrides != null)
  650. {
  651. // Try to find existing override.
  652. for (var i = 0; i < parameterOverridesCount; ++i)
  653. {
  654. ref var p = ref parameterOverrides[i];
  655. if (string.Equals(p.objectRegistrationName, parameterOverride.objectRegistrationName, StringComparison.OrdinalIgnoreCase) &&
  656. string.Equals(p.parameter, parameterOverride.parameter, StringComparison.OrdinalIgnoreCase) &&
  657. p.bindingMask == parameterOverride.bindingMask)
  658. {
  659. haveExistingOverride = true;
  660. // Update value on existing override.
  661. p = parameterOverride;
  662. break;
  663. }
  664. }
  665. }
  666. if (!haveExistingOverride)
  667. {
  668. // Add new override.
  669. ArrayHelpers.AppendWithCapacity(ref parameterOverrides, ref parameterOverridesCount, parameterOverride);
  670. }
  671. // Set value on all current processor and/or interaction instances that use the parameter.
  672. foreach (var parameter in new ParameterEnumerable(state, parameterOverride, mapIndex))
  673. {
  674. // We cannot just blindly apply the parameter here as the override we have set may be less
  675. // specific than an override we already have applied. So instead, we look up the most specific
  676. // override and set that.
  677. var actionMap = state.GetActionMap(parameter.bindingIndex);
  678. ref var binding = ref state.GetBinding(parameter.bindingIndex);
  679. var overrideToApply = ParameterOverride.Find(actionMap, ref binding, parameterOverride.parameter,
  680. parameterOverride.objectRegistrationName);
  681. if (overrideToApply.HasValue)
  682. {
  683. var fieldTypeCode = Type.GetTypeCode(parameter.field.FieldType);
  684. parameter.field.SetValue(parameter.instance, overrideToApply.Value.value.ConvertTo(fieldTypeCode).ToObject());
  685. }
  686. }
  687. }
  688. internal struct Parameter
  689. {
  690. public object instance;
  691. public FieldInfo field;
  692. public int bindingIndex;
  693. }
  694. // Finds all instances of a parameter in one or more actions.
  695. private struct ParameterEnumerable : IEnumerable<Parameter>
  696. {
  697. private InputActionState m_State;
  698. private ParameterOverride m_Parameter;
  699. private int m_MapIndex;
  700. public ParameterEnumerable(InputActionState state, ParameterOverride parameter, int mapIndex = -1)
  701. {
  702. m_State = state;
  703. m_Parameter = parameter;
  704. m_MapIndex = mapIndex;
  705. }
  706. public ParameterEnumerator GetEnumerator()
  707. {
  708. return new ParameterEnumerator(m_State, m_Parameter, m_MapIndex);
  709. }
  710. IEnumerator<Parameter> IEnumerable<Parameter>.GetEnumerator()
  711. {
  712. return GetEnumerator();
  713. }
  714. IEnumerator IEnumerable.GetEnumerator()
  715. {
  716. return GetEnumerator();
  717. }
  718. }
  719. private struct ParameterEnumerator : IEnumerator<Parameter>
  720. {
  721. private InputActionState m_State;
  722. private int m_MapIndex;
  723. private int m_BindingCurrentIndex;
  724. private int m_BindingEndIndex;
  725. private int m_InteractionCurrentIndex;
  726. private int m_InteractionEndIndex;
  727. private int m_ProcessorCurrentIndex;
  728. private int m_ProcessorEndIndex;
  729. private InputBinding m_BindingMask;
  730. private Type m_ObjectType;
  731. private string m_ParameterName;
  732. private bool m_MayBeInteraction;
  733. private bool m_MayBeProcessor;
  734. private bool m_MayBeComposite;
  735. private bool m_CurrentBindingIsComposite;
  736. private object m_CurrentObject;
  737. private FieldInfo m_CurrentParameter;
  738. public ParameterEnumerator(InputActionState state, ParameterOverride parameter, int mapIndex = -1)
  739. : this()
  740. {
  741. m_State = state;
  742. m_ParameterName = parameter.parameter;
  743. m_MapIndex = mapIndex;
  744. m_ObjectType = parameter.objectType;
  745. m_MayBeComposite = m_ObjectType == null || typeof(InputBindingComposite).IsAssignableFrom(m_ObjectType);
  746. m_MayBeProcessor = m_ObjectType == null || typeof(InputProcessor).IsAssignableFrom(m_ObjectType);
  747. m_MayBeInteraction = m_ObjectType == null || typeof(IInputInteraction).IsAssignableFrom(m_ObjectType);
  748. m_BindingMask = parameter.bindingMask;
  749. Reset();
  750. }
  751. private bool MoveToNextBinding()
  752. {
  753. // Find a binding that matches our mask.
  754. while (true)
  755. {
  756. ++m_BindingCurrentIndex;
  757. if (m_BindingCurrentIndex >= m_BindingEndIndex)
  758. return false; // Reached the end.
  759. ref var binding = ref m_State.GetBinding(m_BindingCurrentIndex);
  760. ref var bindingState = ref m_State.GetBindingState(m_BindingCurrentIndex);
  761. // Skip any binding that has no associated objects with parameters.
  762. if (bindingState.processorCount == 0 && bindingState.interactionCount == 0 && !binding.isComposite)
  763. continue;
  764. // If we're only looking for composites, skip any binding that isn't one.
  765. if (m_MayBeComposite && !m_MayBeProcessor && !m_MayBeInteraction && !binding.isComposite)
  766. continue;
  767. // If we're only looking for processors, skip any that hasn't got any.
  768. if (m_MayBeProcessor && !m_MayBeComposite && !m_MayBeInteraction && bindingState.processorCount == 0)
  769. continue;
  770. // If we're only looking for interactions, skip any that hasn't got any.
  771. if (m_MayBeInteraction && !m_MayBeComposite && !m_MayBeProcessor && bindingState.interactionCount == 0)
  772. continue;
  773. if (m_BindingMask.Matches(ref binding))
  774. {
  775. if (m_MayBeComposite)
  776. m_CurrentBindingIsComposite = binding.isComposite;
  777. // Reset interaction and processor count.
  778. m_ProcessorCurrentIndex = bindingState.processorStartIndex - 1; // Minus one to account for first MoveNext().
  779. m_ProcessorEndIndex = bindingState.processorStartIndex + bindingState.processorCount;
  780. m_InteractionCurrentIndex = bindingState.interactionStartIndex - 1; // Minus one to account for first MoveNext().
  781. m_InteractionEndIndex = bindingState.interactionStartIndex + bindingState.interactionCount;
  782. return true;
  783. }
  784. }
  785. }
  786. private bool MoveToNextInteraction()
  787. {
  788. while (m_InteractionCurrentIndex < m_InteractionEndIndex)
  789. {
  790. ++m_InteractionCurrentIndex;
  791. if (m_InteractionCurrentIndex == m_InteractionEndIndex)
  792. break;
  793. var interaction = m_State.interactions[m_InteractionCurrentIndex];
  794. if (FindParameter(interaction))
  795. return true;
  796. }
  797. return false;
  798. }
  799. private bool MoveToNextProcessor()
  800. {
  801. while (m_ProcessorCurrentIndex < m_ProcessorEndIndex)
  802. {
  803. ++m_ProcessorCurrentIndex;
  804. if (m_ProcessorCurrentIndex == m_ProcessorEndIndex)
  805. break;
  806. var processor = m_State.processors[m_ProcessorCurrentIndex];
  807. if (FindParameter(processor))
  808. return true;
  809. }
  810. return false;
  811. }
  812. private bool FindParameter(object instance)
  813. {
  814. if (m_ObjectType != null && !m_ObjectType.IsInstanceOfType(instance))
  815. return false;
  816. var field = instance.GetType().GetField(m_ParameterName,
  817. BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
  818. if (field == null)
  819. return false;
  820. m_CurrentParameter = field;
  821. m_CurrentObject = instance;
  822. return true;
  823. }
  824. public bool MoveNext()
  825. {
  826. while (true)
  827. {
  828. if (m_MayBeInteraction && MoveToNextInteraction())
  829. return true;
  830. if (m_MayBeProcessor && MoveToNextProcessor())
  831. return true;
  832. if (!MoveToNextBinding())
  833. return false;
  834. if (m_MayBeComposite && m_CurrentBindingIsComposite)
  835. {
  836. var compositeIndex = m_State.GetBindingState(m_BindingCurrentIndex).compositeOrCompositeBindingIndex;
  837. var composite = m_State.composites[compositeIndex];
  838. if (FindParameter(composite))
  839. return true;
  840. }
  841. }
  842. }
  843. public unsafe void Reset()
  844. {
  845. m_CurrentObject = default;
  846. m_CurrentParameter = default;
  847. m_InteractionCurrentIndex = default;
  848. m_InteractionEndIndex = default;
  849. m_ProcessorCurrentIndex = default;
  850. m_ProcessorEndIndex = default;
  851. m_CurrentBindingIsComposite = default;
  852. if (m_MapIndex < 0)
  853. {
  854. m_BindingCurrentIndex = -1; // Account for first MoveNext().
  855. m_BindingEndIndex = m_State.totalBindingCount;
  856. }
  857. else
  858. {
  859. m_BindingCurrentIndex = m_State.mapIndices[m_MapIndex].bindingStartIndex - 1; // Account for first MoveNext().
  860. m_BindingEndIndex = m_State.mapIndices[m_MapIndex].bindingStartIndex + m_State.mapIndices[m_MapIndex].bindingCount;
  861. }
  862. }
  863. public Parameter Current => new Parameter
  864. {
  865. instance = m_CurrentObject,
  866. field = m_CurrentParameter,
  867. bindingIndex = m_BindingCurrentIndex,
  868. };
  869. object IEnumerator.Current => Current;
  870. public void Dispose()
  871. {
  872. }
  873. }
  874. internal struct ParameterOverride
  875. {
  876. public string objectRegistrationName; // Optional. Such as "hold" or "scale".
  877. public string parameter;
  878. public InputBinding bindingMask;
  879. public PrimitiveValue value;
  880. public Type objectType =>
  881. InputProcessor.s_Processors.LookupTypeRegistration(objectRegistrationName)
  882. ?? InputInteraction.s_Interactions.LookupTypeRegistration(objectRegistrationName)
  883. ?? InputBindingComposite.s_Composites.LookupTypeRegistration(objectRegistrationName);
  884. public ParameterOverride(string parameterName, InputBinding bindingMask, PrimitiveValue value = default)
  885. {
  886. var colonIndex = parameterName.IndexOf(':');
  887. if (colonIndex < 0)
  888. {
  889. objectRegistrationName = null;
  890. parameter = parameterName;
  891. }
  892. else
  893. {
  894. objectRegistrationName = parameterName.Substring(0, colonIndex);
  895. parameter = parameterName.Substring(colonIndex + 1);
  896. }
  897. this.bindingMask = bindingMask;
  898. this.value = value;
  899. }
  900. public ParameterOverride(string objectRegistrationName, string parameterName, InputBinding bindingMask, PrimitiveValue value = default)
  901. {
  902. this.objectRegistrationName = objectRegistrationName;
  903. this.parameter = parameterName;
  904. this.bindingMask = bindingMask;
  905. this.value = value;
  906. }
  907. // Find the *most specific* override to apply to the given parameter.
  908. public static ParameterOverride? Find(InputActionMap actionMap, ref InputBinding binding, string parameterName, string objectRegistrationName)
  909. {
  910. // Look at level of map.
  911. var overrideOnMap = Find(actionMap.m_ParameterOverrides, actionMap.m_ParameterOverridesCount, ref binding, parameterName,
  912. objectRegistrationName);
  913. // Look at level of asset (if present).
  914. var asset = actionMap.asset;
  915. var overrideOnAsset = asset != null
  916. ? Find(asset.m_ParameterOverrides, asset.m_ParameterOverridesCount, ref binding, parameterName,
  917. objectRegistrationName)
  918. : null;
  919. return PickMoreSpecificOne(overrideOnMap, overrideOnAsset);
  920. }
  921. private static ParameterOverride? Find(ParameterOverride[] overrides, int overrideCount,
  922. ref InputBinding binding, string parameterName, string objectRegistrationName)
  923. {
  924. ParameterOverride? result = null;
  925. for (var i = 0; i < overrideCount; ++i)
  926. {
  927. ref var current = ref overrides[i];
  928. if (!string.Equals(parameterName, current.parameter, StringComparison.OrdinalIgnoreCase))
  929. continue; // Different parameter name.
  930. if (!current.bindingMask.Matches(binding))
  931. continue;
  932. if (current.objectRegistrationName != null && !string.Equals(current.objectRegistrationName, objectRegistrationName,
  933. StringComparison.OrdinalIgnoreCase))
  934. continue;
  935. if (result == null)
  936. {
  937. // First match.
  938. result = current;
  939. }
  940. else
  941. {
  942. // Already have a match. See which one is more specific.
  943. result = PickMoreSpecificOne(result, current);
  944. }
  945. }
  946. return result;
  947. }
  948. private static ParameterOverride? PickMoreSpecificOne(ParameterOverride? first, ParameterOverride? second)
  949. {
  950. if (first == null)
  951. return second;
  952. if (second == null)
  953. return first;
  954. // Having an objectRegistrationName always wins vs not having one.
  955. if (first.Value.objectRegistrationName != null && second.Value.objectRegistrationName == null)
  956. return first;
  957. if (second.Value.objectRegistrationName != null && first.Value.objectRegistrationName == null)
  958. return second;
  959. // Targeting a specific path always wins vs not doing so.
  960. if (first.Value.bindingMask.effectivePath != null && second.Value.bindingMask.effectivePath == null)
  961. return first;
  962. if (second.Value.bindingMask.effectivePath != null && first.Value.bindingMask.effectivePath == null)
  963. return second;
  964. // Targeting a specific actions always wins vs not doing so.
  965. if (first.Value.bindingMask.action != null && second.Value.bindingMask.action == null)
  966. return first;
  967. if (second.Value.bindingMask.action != null && first.Value.bindingMask.action == null)
  968. return second;
  969. // Undecided. First wins by default.
  970. return first;
  971. }
  972. }
  973. }
  974. }