1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq.Expressions;
- using System.Reflection;
- using Unity.Collections.LowLevel.Unsafe;
- using UnityEngine.InputSystem.Utilities;
-
- namespace UnityEngine.InputSystem
- {
- partial class InputActionRebindingExtensions
- {
- /// <summary>
- /// Return the current value of the given parameter as found on the processors, interactions, or composites
- /// of the action's current bindings.
- /// </summary>
- /// <param name="action">Action on whose bindings to look for the value of the given parameter.</param>
- /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
- /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
- /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
- /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
- /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
- /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will be taken into account.</param>
- /// <returns>The current value of the given parameter or <c>null</c> if the parameter could not be found.</returns>
- /// <remarks>
- /// Parameters are found on interactions (<see cref="IInputInteraction"/>), processors (<see cref="InputProcessor"/>), and
- /// composites (see <see cref="InputBindingComposite"/>) that are applied to bindings. For example, the following binding
- /// adds a <c>Hold</c> interaction with a custom <c>duration</c> parameter on top of binding to the gamepad's A button:
- ///
- /// <example>
- /// <code>
- /// new InputBinding
- /// {
- /// path = "<Gamepad>/buttonSouth",
- /// interactions = "hold(duration=0.6)"
- /// };
- /// </code>
- /// </example>
- ///
- /// In the editor UI, parameters are set graphically from the properties sections in the right-most pane
- /// in the action editor when an action or a binding is selected.
- ///
- /// When the binding above is applied to an action, the <c>duration</c> parameter from the <c>Hold</c> interaction can be
- /// queried like so:
- ///
- /// <example>
- /// <code>
- /// action.GetParameterValue("duration") // Returns 0.6
- /// </code>
- /// </example>
- ///
- /// Note that if there are multiple objects on the action that use the same parameter name, the value of the <em>first</em> parameter
- /// that is encountered is returned. Also note that this method will create GC heap garbage.
- ///
- /// The type of object to query the parameter from can be include in the <paramref name="name"/> parameter. For example, if
- /// an action has both a <see cref="Interactions.TapInteraction"/> and a <see cref="Interactions.HoldInteraction"/> on it, the
- /// <c>duration</c> parameter can be queried independently like so:
- ///
- /// <example>
- /// <code>
- /// // Query "duration" from "hold":
- /// action.GetParameterValue("hold:duration");
- ///
- /// // Query "duration" from "tap":
- /// action.GetParameterValue("tap:duration");
- /// </code>
- /// </example>
- ///
- /// The names used here to identify the object holding the parameter are the same used by <see cref="InputSystem.RegisterInteraction"/>,
- /// <see cref="InputSystem.RegisterBindingComposite"/>, and <see cref="InputSystem.RegisterProcessor"/>.
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c></exception>
- /// <seealso cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
- /// <seealso cref="ApplyBindingOverride(InputAction,string,string,string)"/>
- /// <seealso cref="Editor.InputParameterEditor"/>
- public static PrimitiveValue? GetParameterValue(this InputAction action, string name, InputBinding bindingMask = default)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
-
- return action.GetParameterValue(new ParameterOverride(name, bindingMask));
- }
-
- private static PrimitiveValue? GetParameterValue(this InputAction action, ParameterOverride parameterOverride)
- {
- parameterOverride.bindingMask.action = action.name;
-
- var actionMap = action.GetOrCreateActionMap();
- actionMap.ResolveBindingsIfNecessary();
- foreach (var parameter in new ParameterEnumerable(actionMap.m_State, parameterOverride, actionMap.m_MapIndexInState))
- {
- var value = parameter.field.GetValue(parameter.instance);
- return PrimitiveValue.FromObject(value);
- }
-
- return null;
- }
-
- /// <summary>
- /// Return the current value of the given parameter as found on the processors, interactions, or composites
- /// of the action's current bindings.
- /// </summary>
- /// <param name="action">Action on whose bindings to look for the value of the given parameter.</param>
- /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
- /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
- /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
- /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
- /// <param name="bindingIndex">Index of the binding in <paramref name="action"/>'s <see cref="InputAction.bindings"/>
- /// to look for processors, interactions, and composites on.</param>
- /// <returns>The current value of the given parameter or <c>null</c> if the parameter not could be found.</returns>
- /// <remarks>
- /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
- /// to specifically target a single binding by index. Otherwise, the method is identical in functionality.
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c></exception>
- public static PrimitiveValue? GetParameterValue(this InputAction action, string name, int bindingIndex)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
- if (bindingIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(bindingIndex));
-
- var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(bindingIndex);
- var bindingMask = new InputBinding { id = action.GetOrCreateActionMap().bindings[indexOnMap].id };
-
- return action.GetParameterValue(name, bindingMask);
- }
-
- /// <summary>
- /// Return the current value of the given parameter as found on the processors, interactions, or composites
- /// of the action's current bindings.
- /// </summary>
- /// <param name="action">Action on whose bindings to look for the value of the given parameter.</param>
- /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
- /// name and type of the parameter being looked for.</param>
- /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
- /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will be taken into account.</param>
- /// <returns>The current value of the given parameter or <c>null</c> if the parameter not could be found.</returns>
- /// <remarks>
- /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
- /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
- /// type-safe and does not involve strings.
- ///
- /// <example>
- /// <code>
- /// // Get the "duration" parameter from a TapInteraction.
- /// // This is equivalent to calling GetParameterValue("tap:duration")
- /// // but will return a float? instead of a PrimitiveValue?.
- /// action.GetParameterValue((TapInteraction x) => x.duration)
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c></exception>
- /// <seealso cref="ApplyParameterOverride{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},TValue,InputBinding)"/>
- public static unsafe TValue? GetParameterValue<TObject, TValue>(this InputAction action, Expression<Func<TObject, TValue>> expr, InputBinding bindingMask = default)
- where TValue : struct
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (expr == null)
- throw new ArgumentNullException(nameof(expr));
-
- var parameterOverride = ExtractParameterOverride(expr, bindingMask);
- var value = action.GetParameterValue(parameterOverride);
-
- if (value == null)
- return null;
-
- // Type is guaranteed to match but just in case,
- // make extra sure with a check here.
- if (Type.GetTypeCode(typeof(TValue)) == value.Value.type)
- {
- // Can't just cast here so use UnsafeUtility to work around that.
- var v = value.Value;
- var result = default(TValue);
- UnsafeUtility.MemCpy(UnsafeUtility.AddressOf(ref result),
- v.valuePtr,
- UnsafeUtility.SizeOf<TValue>());
- return result;
- }
-
- // Shouldn't get here but just in case, do a conversion using C#'s Convert
- // machinery as a fallback.
- return (TValue)Convert.ChangeType(value.Value.ToObject(), typeof(TValue));
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
- /// </summary>
- /// <param name="action">An action on whose <see cref="InputAction.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
- /// name and type of the parameter whose value to set.</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
- /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will have the override applied to them.</param>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c>
- /// or empty.</exception>
- /// <remarks>
- /// This method is a variation of <see cref="ApplyParameterOverride(InputAction,string,PrimitiveValue,InputBinding)"/>
- /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
- /// type-safe and does not involve strings.
- ///
- /// <example>
- /// <code>
- /// // Override the "duration" parameter from a TapInteraction.
- /// // This is equivalent to calling ApplyParameterOverride("tap:duration", 0.4f).
- /// action.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="GetParameterValue{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},InputBinding)"/>
- public static void ApplyParameterOverride<TObject, TValue>(this InputAction action, Expression<Func<TObject, TValue>> expr, TValue value,
- InputBinding bindingMask = default)
- where TValue : struct
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (expr == null)
- throw new ArgumentNullException(nameof(expr));
-
- var actionMap = action.GetOrCreateActionMap();
- actionMap.ResolveBindingsIfNecessary();
- bindingMask.action = action.name;
-
- var parameterOverride = ExtractParameterOverride(expr, bindingMask, PrimitiveValue.From(value));
-
- ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
- ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
- parameterOverride);
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of <paramref name="actionMap"/>.
- /// </summary>
- /// <param name="actionMap">An action on whose <see cref="InputActionMap.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
- /// name and type of the parameter whose value to set.</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
- /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will have the override applied to them.</param>
- /// <exception cref="ArgumentNullException"><paramref name="actionMap"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c>
- /// or empty.</exception>
- /// <remarks>
- /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/>
- /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
- /// type-safe and does not involve strings.
- ///
- /// <example>
- /// <code>
- /// // Override the "duration" parameter from a TapInteraction.
- /// // This is equivalent to calling mApplyParameterOverride("tap:duration", 0.4f).
- /// actionMap.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="GetParameterValue{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},InputBinding)"/>
- public static void ApplyParameterOverride<TObject, TValue>(this InputActionMap actionMap, Expression<Func<TObject, TValue>> expr, TValue value,
- InputBinding bindingMask = default)
- where TValue : struct
- {
- if (actionMap == null)
- throw new ArgumentNullException(nameof(actionMap));
- if (expr == null)
- throw new ArgumentNullException(nameof(expr));
-
- actionMap.ResolveBindingsIfNecessary();
-
- var parameterOverride = ExtractParameterOverride(expr, bindingMask, PrimitiveValue.From(value));
-
- ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
- ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
- parameterOverride);
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of the <see cref="InputActionAsset.actionMaps"/>
- /// in <paramref name="asset"/>.
- /// </summary>
- /// <param name="asset">An asset on whose <see cref="InputActionMap.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="expr">An expression such as <c>(TapInteraction x) => x.duration</c> that determines the
- /// name and type of the parameter whose value to set.</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingMask">Optional mask that determines on which bindings to look for objects with parameters. If used, only
- /// bindings that match (see <see cref="InputBinding.Matches"/>) the given mask will have the override applied to them.</param>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or- <paramref name="expr"/> is <c>null</c>
- /// or empty.</exception>
- /// <remarks>
- /// This method is a variation of <see cref="ApplyParameterOverride(InputActionAsset,string,PrimitiveValue,InputBinding)"/>
- /// that encapsulates a reference to the name of the parameter and the type of object it is found on in a way that is
- /// type-safe and does not involve strings.
- ///
- /// <example>
- /// <code>
- /// // Override the "duration" parameter from a TapInteraction.
- /// // This is equivalent to calling mApplyParameterOverride("tap:duration", 0.4f).
- /// asset.ApplyParameterOverride((TapInteraction x) => x.duration, 0.4f);
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="GetParameterValue{TObject,TValue}(InputAction,Expression{Func{TObject,TValue}},InputBinding)"/>
- public static void ApplyParameterOverride<TObject, TValue>(this InputActionAsset asset, Expression<Func<TObject, TValue>> expr, TValue value,
- InputBinding bindingMask = default)
- where TValue : struct
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (expr == null)
- throw new ArgumentNullException(nameof(expr));
-
- asset.ResolveBindingsIfNecessary();
-
- var parameterOverride = ExtractParameterOverride(expr, bindingMask, PrimitiveValue.From(value));
-
- ApplyParameterOverride(asset.m_SharedStateForAllMaps, -1,
- ref asset.m_ParameterOverrides, ref asset.m_ParameterOverridesCount,
- parameterOverride);
- }
-
- private static ParameterOverride ExtractParameterOverride<TObject, TValue>(Expression<Func<TObject, TValue>> expr,
- InputBinding bindingMask = default, PrimitiveValue value = default)
- {
- if (!(expr is LambdaExpression lambda))
- throw new ArgumentException($"Expression must be a LambdaExpression but was a {expr.GetType().Name} instead", nameof(expr));
-
- if (!(lambda.Body is MemberExpression body))
- {
- // If the field type in the lambda doesn't match the TValue type being used,
- // but there is a coercion, the compiler will automatically insert a Convert(x.name, TValue)
- // expression.
- if (lambda.Body is UnaryExpression unary && unary.NodeType == ExpressionType.Convert && unary.Operand is MemberExpression b)
- {
- body = b;
- }
- else
- {
- throw new ArgumentException(
- $"Body in LambdaExpression must be a MemberExpression (x.name) but was a {expr.GetType().Name} instead",
- nameof(expr));
- }
- }
-
- string objectRegistrationName;
- if (typeof(InputProcessor).IsAssignableFrom(typeof(TObject)))
- objectRegistrationName = InputProcessor.s_Processors.FindNameForType(typeof(TObject));
- else if (typeof(IInputInteraction).IsAssignableFrom(typeof(TObject)))
- objectRegistrationName = InputInteraction.s_Interactions.FindNameForType(typeof(TObject));
- else if (typeof(InputBindingComposite).IsAssignableFrom(typeof(TObject)))
- objectRegistrationName = InputBindingComposite.s_Composites.FindNameForType(typeof(TObject));
- else
- throw new ArgumentException(
- $"Given type must be an InputProcessor, IInputInteraction, or InputBindingComposite (was {typeof(TObject).Name})",
- nameof(TObject));
-
- return new ParameterOverride(objectRegistrationName, body.Member.Name, bindingMask, value);
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of <paramref name="actionMap"/>.
- /// </summary>
- /// <param name="actionMap">An action map on whose <see cref="InputActionMap.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
- /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
- /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
- /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingMask">A binding mask that determines which of <paramref name="actionMap"/>'s <see cref="InputActionMap.bindings"/>
- /// 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>
- /// <remarks>
- /// This method both directly applies the new value and also stores the override internally.
- ///
- /// If an override for the same parameter <paramref name="name"/> and with the same <paramref name="bindingMask"/> already exists,
- /// its value is simply updated. No new override will be created.
- ///
- /// You can use this method to set parameters (public fields) on composites, interactions, and processors that are created
- /// from bindings.
- ///
- /// <example>
- /// <code>
- /// // Create an action map with two actions.
- /// var map = new InputActionMap();
- /// var action1 = map.AddAction("action1");
- /// var action2 = map.AddAction("action2");
- ///
- /// // Add a binding to each action to which a "ClampProcessor" is applied.
- /// // This processor has two parameters:
- /// // - "min" (float)
- /// // - "max" (float)
- /// action1.AddBinding(">Gamepad>/rightTrigger", processors: "clamp(min=0.2,max=0.8)");
- /// action2.AddBinding(">Gamepad>/leftTrigger", processors: "clamp(min=0.2,max=0.8)");
- ///
- /// // Apply parameter overrides to set the values differently.
- /// // This will apply the setting to *both* the bindings on action1 *and* action2.
- /// map.ApplyParameterOverride("min", 0.3f);
- /// map.ApplyParameterOverride("max", 0.9f);
- /// </code>
- /// </example>
- ///
- /// An override can optionally be directed at a specific type of object.
- ///
- /// <example>
- /// <code>
- /// map.ApplyParameterOverride("clamp:min", 0.3f);
- /// map.ApplyParameterOverride("clamp:max", 0.9f);
- /// </code>
- /// </example>
- ///
- /// By default, the parameter override will apply to all bindings in the map. To limit the override
- /// to specific bindings, you can supply a <paramref name="bindingMask"/>.
- ///
- /// <example>
- /// <code>
- /// // Apply a parameter override only to action1.
- /// map.ApplyBindingOverride("clamp:min", 0.25f, new InputBinding { action = action1.name });
- ///
- /// // Apply a parameter override only to a specific binding path.
- /// map.ApplyBindingOverride("clamp:min", 0.4f, new InputBinding { path = "<Gamepad>/leftTrigger" });
- /// </code>
- /// </example>
- ///
- /// If multiple overrides exist for the same parameter, an attempt is made to choose the override that is most specific.
- /// Say, that you apply an override for <c>"duration"</c> on an entire <see cref="InputActionAsset"/> using
- /// <see cref="ApplyParameterOverride(InputActionAsset,String,PrimitiveValue,InputBinding)"/>. But then you also apply
- /// an override to just an individual <see cref="InputAction"/> inside the asset. In this case, the <c>"duration"</c>
- /// override for just that action will be applied to bindings of that action and the override inside the asset will
- /// be applied to bindings of all other actions. Note that if multiple overrides exist that could all be considered
- /// equally valid, the behavior is undecided.
- ///
- /// Note that parameter overrides stay in place on the map. Like binding overrides, however, they are not
- /// automatically persisted and thus need to be reapplied when actions are loaded from assets. This will, however, be applied
- /// automatically to bindings added to the action in the future as well as whenever bindings are resolved to controls.
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="actionMap"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
- /// or empty.</exception>
- /// <seealso cref="GetParameterValue(InputAction,string,InputBinding)"/>
- public static void ApplyParameterOverride(this InputActionMap actionMap, string name, PrimitiveValue value, InputBinding bindingMask = default)
- {
- if (actionMap == null)
- throw new ArgumentNullException(nameof(actionMap));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
-
- actionMap.ResolveBindingsIfNecessary();
-
- ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
- ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
- new ParameterOverride(name, bindingMask, value));
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputActionMap.bindings"/> of each of the <see cref="InputActionAsset.actionMaps"/>
- /// in <paramref name="asset"/>.
- /// </summary>
- /// <param name="asset">An <c>.inputactions</c> asset on whose <see cref="InputActionMap.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
- /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
- /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
- /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingMask">A binding mask that determines which of the <see cref="InputActionMap.bindings"/>
- /// 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>
- /// <remarks>
- /// This method both directly applies the new value and also stores the override internally.
- ///
- /// If an override for the same parameter <paramref name="name"/> and with the same <paramref name="bindingMask"/> already exists,
- /// its value is simply updated. No new override will be created.
- ///
- /// You can use this method to set parameters (public fields) on composites, interactions, and processors that are created
- /// from bindings.
- ///
- /// <example>
- /// <code>
- /// // Create an asset with one action map and two actions.
- /// var asset = ScriptableObject.CreateInstance<InputActionAsset>();
- /// var map = asset.AddActionMap("map");
- /// var action1 = map.AddAction("action1");
- /// var action2 = map.AddAction("action2");
- ///
- /// // Add a binding to each action to which a "ClampProcessor" is applied.
- /// // This processor has two parameters:
- /// // - "min" (float)
- /// // - "max" (float)
- /// action1.AddBinding(">Gamepad>/rightTrigger", processors: "clamp(min=0.2,max=0.8)");
- /// action2.AddBinding(">Gamepad>/leftTrigger", processors: "clamp(min=0.2,max=0.8)");
- ///
- /// // Apply parameter overrides to set the values differently.
- /// // This will apply the setting to *both* the bindings on action1 *and* action2.
- /// asset.ApplyParameterOverride("min", 0.3f);
- /// asset.ApplyParameterOverride("max", 0.9f);
- /// </code>
- /// </example>
- ///
- /// An override can optionally be directed at a specific type of object.
- ///
- /// <example>
- /// <code>
- /// asset.ApplyParameterOverride("clamp:min", 0.3f);
- /// asset.ApplyParameterOverride("clamp:max", 0.9f);
- /// </code>
- /// </example>
- ///
- /// By default, the parameter override will apply to all bindings in the asset. To limit the override
- /// to specific bindings, you can supply a <paramref name="bindingMask"/>.
- ///
- /// <example>
- /// <code>
- /// // Apply a parameter override only to action1.
- /// asset.ApplyBindingOverride("clamp:min", 0.25f, new InputBinding { action = action1.name });
- ///
- /// // Apply a parameter override only to a specific binding path.
- /// asset.ApplyBindingOverride("clamp:min", 0.4f, new InputBinding { path = "<Gamepad>/leftTrigger" });
- /// </code>
- /// </example>
- ///
- /// If multiple overrides exist for the same parameter, an attempt is made to choose the override that is most specific.
- /// Say, that you apply an override for <c>"duration"</c> on an entire <see cref="InputActionAsset"/> using
- /// <see cref="ApplyParameterOverride(InputActionAsset,String,PrimitiveValue,InputBinding)"/>. But then you also apply
- /// an override to just an individual <see cref="InputAction"/> inside the asset. In this case, the <c>"duration"</c>
- /// override for just that action will be applied to bindings of that action and the override inside the asset will
- /// be applied to bindings of all other actions. Note that if multiple overrides exist that could all be considered
- /// equally valid, the behavior is undecided.
- ///
- /// Note that parameter overrides stay in place on the map. Like binding overrides, however, they are not
- /// automatically persisted and thus need to be reapplied when actions are loaded from assets. This will, however, be applied
- /// automatically to bindings added to the action in the future as well as whenever bindings are resolved to controls.
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
- /// or empty.</exception>
- /// <seealso cref="GetParameterValue(InputAction,string,InputBinding)"/>
- public static void ApplyParameterOverride(this InputActionAsset asset, string name, PrimitiveValue value, InputBinding bindingMask = default)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
-
- asset.ResolveBindingsIfNecessary();
-
- ApplyParameterOverride(asset.m_SharedStateForAllMaps, -1,
- ref asset.m_ParameterOverrides, ref asset.m_ParameterOverridesCount,
- new ParameterOverride(name, bindingMask, value));
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
- /// </summary>
- /// <param name="action">An action on whose <see cref="InputAction.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
- /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
- /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
- /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingMask">A binding mask that determines which of <paramref name="action"/>'s <see cref="InputAction.bindings"/>
- /// 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>
- /// <remarks>
- /// This method both directly applies the new value and also stores the override internally.
- ///
- /// If an override for the same parameter <paramref name="name"/> on the same <paramref name="action"/> and with the same
- /// <paramref name="bindingMask"/> already exists, its value is simply updated. No new override will be created.
- ///
- /// You can use this method to set parameters (public fields) on composites, interactions, and processors that are created
- /// from bindings.
- ///
- /// <example>
- /// <code>
- /// // Create an action with a binding that has a "ClampProcessor" applied to it.
- /// // This processor has two parameters:
- /// // - "min" (float)
- /// // - "max" (float)
- /// var action = new InputAction(binding: ">Gamepad>/rightTrigger", processors: "clamp(min=0.2,max=0.8)");
- ///
- /// // Apply parameter overrides to set the values differently.
- /// action.ApplyParameterOverride("min", 0.3f);
- /// action.ApplyParameterOverride("max", 0.9f);
- /// </code>
- /// </example>
- ///
- /// An override can optionally be directed at a specific type of object.
- ///
- /// <example>
- /// <code>
- /// // Create an action with both a "tap" and a "hold" interaction. Both have a
- /// // "duration" parameter.
- /// var action = new InputAction(binding: "<Gamepad>/buttonSouth", interactions: "tap;hold");
- ///
- /// // Apply parameter overrides individually to the two.
- /// action.ApplyParameterOverride("tap:duration", 0.6f);
- /// action.ApplyParameterOverride("hold:duration", 4f);
- /// </code>
- /// </example>
- ///
- /// By default, the parameter override will apply to all bindings on the action. To limit the override
- /// to specific bindings, you can supply a <paramref name="bindingMask"/>.
- ///
- /// <example>
- /// <code>
- /// // Create a "look" style action with a mouse and a gamepad binding.
- /// var lookAction = new InputAction();
- /// lookAction.AddBinding("<Mouse>/delta", processors: "scaleVector2", groups: "Mouse");
- /// lookAction.AddBinding("<Gamepad>/rightStick", processors: "scaleVector2", groups: "Gamepad");
- ///
- /// // Override scaling of the mouse delta individually.
- /// lookAction.ApplyBindingOverride("scaleVector2:x", 0.25f, InputBinding.MaskByGroup("Mouse"));
- /// lookAction.ApplyBindingOverride("scaleVector2:y", 0.25f, InputBinding.MaskByGroup("Mouse"));
- ///
- /// // Can also do that by path.
- /// lookAction.ApplyBindingOverride("scaleVector2:x", 0.25f, new InputBinding("<Mouse>/delta"));
- /// lookAction.ApplyBindingOverride("scaleVector2:y", 0.25f, new InputBinding("<Mouse>/delta"));
- /// </code>
- /// </example>
- ///
- /// Note that parameter overrides stay in place on the action. Like binding overrides, however, they are not
- /// automatically persisted and thus need to be reapplied when actions are loaded from assets. This will, however, be applied
- /// automatically to bindings added to the action in the future as well as whenever bindings for the action are resolved.
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
- /// or empty.</exception>
- /// <seealso cref="GetParameterValue(InputAction,string,InputBinding)"/>
- public static void ApplyParameterOverride(this InputAction action, string name, PrimitiveValue value, InputBinding bindingMask = default)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- var actionMap = action.GetOrCreateActionMap();
- actionMap.ResolveBindingsIfNecessary();
- bindingMask.action = action.name;
-
- ApplyParameterOverride(actionMap.m_State, actionMap.m_MapIndexInState,
- ref actionMap.m_ParameterOverrides, ref actionMap.m_ParameterOverridesCount,
- new ParameterOverride(name, bindingMask, value));
- }
-
- /// <summary>
- /// Set the value of the given parameter on the <see cref="InputBindingComposite"/>, <see cref="IInputInteraction"/>,
- /// and <see cref="InputProcessor"/> objects found on the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
- /// </summary>
- /// <param name="action">An action on whose <see cref="InputAction.bindings"/> to look for objects to set
- /// the parameter value on.</param>
- /// <param name="name">Name of the parameter to get the value of. Case-insensitive. This can either be just the name of the
- /// parameter (like <c>"duration"</c> or expressed as <c>nameof(TapInteraction.duration)</c>) or can be prefixed with the
- /// type of object to get the parameter value from. For example, <c>"tap:duration"</c> will specifically get the <c>"duration"</c>
- /// parameter from the object registered as <c>"tap"</c> (which will usually be <see cref="Interactions.TapInteraction"/>).</param>
- /// <param name="value">New value to assign to the parameter.</param>
- /// <param name="bindingIndex">Index of the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/> to which
- /// to restrict the parameter override to.</param>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="bindingIndex"/> is negative or equal or greater than the number of
- /// <see cref="InputAction.bindings"/> of <paramref name="action"/>.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="name"/> is <c>null</c>
- /// or empty.</exception>
- /// <remarks>
- /// This method is a variation of <see cref="ApplyParameterOverride(InputActionMap,string,PrimitiveValue,InputBinding)"/> which
- /// allows specifying a binding by index. It otherwise behaves identically to that method.
- /// </remarks>
- public static void ApplyParameterOverride(this InputAction action, string name, PrimitiveValue value, int bindingIndex)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
- if (bindingIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(bindingIndex));
-
- var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(bindingIndex);
- var bindingMask = new InputBinding { id = action.GetOrCreateActionMap().bindings[indexOnMap].id };
-
- action.ApplyParameterOverride(name, value, bindingMask);
- }
-
- private static void ApplyParameterOverride(InputActionState state, int mapIndex,
- ref ParameterOverride[] parameterOverrides, ref int parameterOverridesCount, ParameterOverride parameterOverride)
- {
- // Update the parameter overrides on the map or asset.
- var haveExistingOverride = false;
- if (parameterOverrides != null)
- {
- // Try to find existing override.
- for (var i = 0; i < parameterOverridesCount; ++i)
- {
- ref var p = ref parameterOverrides[i];
- if (string.Equals(p.objectRegistrationName, parameterOverride.objectRegistrationName, StringComparison.OrdinalIgnoreCase) &&
- string.Equals(p.parameter, parameterOverride.parameter, StringComparison.OrdinalIgnoreCase) &&
- p.bindingMask == parameterOverride.bindingMask)
- {
- haveExistingOverride = true;
- // Update value on existing override.
- p = parameterOverride;
- break;
- }
- }
- }
- if (!haveExistingOverride)
- {
- // Add new override.
- ArrayHelpers.AppendWithCapacity(ref parameterOverrides, ref parameterOverridesCount, parameterOverride);
- }
-
- // Set value on all current processor and/or interaction instances that use the parameter.
- foreach (var parameter in new ParameterEnumerable(state, parameterOverride, mapIndex))
- {
- // We cannot just blindly apply the parameter here as the override we have set may be less
- // specific than an override we already have applied. So instead, we look up the most specific
- // override and set that.
- var actionMap = state.GetActionMap(parameter.bindingIndex);
- ref var binding = ref state.GetBinding(parameter.bindingIndex);
- var overrideToApply = ParameterOverride.Find(actionMap, ref binding, parameterOverride.parameter,
- parameterOverride.objectRegistrationName);
- if (overrideToApply.HasValue)
- {
- var fieldTypeCode = Type.GetTypeCode(parameter.field.FieldType);
- parameter.field.SetValue(parameter.instance, overrideToApply.Value.value.ConvertTo(fieldTypeCode).ToObject());
- }
- }
- }
-
- internal struct Parameter
- {
- public object instance;
- public FieldInfo field;
- public int bindingIndex;
- }
-
- // Finds all instances of a parameter in one or more actions.
- private struct ParameterEnumerable : IEnumerable<Parameter>
- {
- private InputActionState m_State;
- private ParameterOverride m_Parameter;
- private int m_MapIndex;
-
- public ParameterEnumerable(InputActionState state, ParameterOverride parameter, int mapIndex = -1)
- {
- m_State = state;
- m_Parameter = parameter;
- m_MapIndex = mapIndex;
- }
-
- public ParameterEnumerator GetEnumerator()
- {
- return new ParameterEnumerator(m_State, m_Parameter, m_MapIndex);
- }
-
- IEnumerator<Parameter> IEnumerable<Parameter>.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- private struct ParameterEnumerator : IEnumerator<Parameter>
- {
- private InputActionState m_State;
- private int m_MapIndex;
- private int m_BindingCurrentIndex;
- private int m_BindingEndIndex;
- private int m_InteractionCurrentIndex;
- private int m_InteractionEndIndex;
- private int m_ProcessorCurrentIndex;
- private int m_ProcessorEndIndex;
-
- private InputBinding m_BindingMask;
- private Type m_ObjectType;
- private string m_ParameterName;
- private bool m_MayBeInteraction;
- private bool m_MayBeProcessor;
- private bool m_MayBeComposite;
- private bool m_CurrentBindingIsComposite;
- private object m_CurrentObject;
- private FieldInfo m_CurrentParameter;
-
- public ParameterEnumerator(InputActionState state, ParameterOverride parameter, int mapIndex = -1)
- : this()
- {
- m_State = state;
- m_ParameterName = parameter.parameter;
- m_MapIndex = mapIndex;
- m_ObjectType = parameter.objectType;
- m_MayBeComposite = m_ObjectType == null || typeof(InputBindingComposite).IsAssignableFrom(m_ObjectType);
- m_MayBeProcessor = m_ObjectType == null || typeof(InputProcessor).IsAssignableFrom(m_ObjectType);
- m_MayBeInteraction = m_ObjectType == null || typeof(IInputInteraction).IsAssignableFrom(m_ObjectType);
- m_BindingMask = parameter.bindingMask;
- Reset();
- }
-
- private bool MoveToNextBinding()
- {
- // Find a binding that matches our mask.
- while (true)
- {
- ++m_BindingCurrentIndex;
- if (m_BindingCurrentIndex >= m_BindingEndIndex)
- return false; // Reached the end.
-
- ref var binding = ref m_State.GetBinding(m_BindingCurrentIndex);
- ref var bindingState = ref m_State.GetBindingState(m_BindingCurrentIndex);
-
- // Skip any binding that has no associated objects with parameters.
- if (bindingState.processorCount == 0 && bindingState.interactionCount == 0 && !binding.isComposite)
- continue;
-
- // If we're only looking for composites, skip any binding that isn't one.
- if (m_MayBeComposite && !m_MayBeProcessor && !m_MayBeInteraction && !binding.isComposite)
- continue;
-
- // If we're only looking for processors, skip any that hasn't got any.
- if (m_MayBeProcessor && !m_MayBeComposite && !m_MayBeInteraction && bindingState.processorCount == 0)
- continue;
-
- // If we're only looking for interactions, skip any that hasn't got any.
- if (m_MayBeInteraction && !m_MayBeComposite && !m_MayBeProcessor && bindingState.interactionCount == 0)
- continue;
-
- if (m_BindingMask.Matches(ref binding))
- {
- if (m_MayBeComposite)
- m_CurrentBindingIsComposite = binding.isComposite;
-
- // Reset interaction and processor count.
- m_ProcessorCurrentIndex = bindingState.processorStartIndex - 1; // Minus one to account for first MoveNext().
- m_ProcessorEndIndex = bindingState.processorStartIndex + bindingState.processorCount;
- m_InteractionCurrentIndex = bindingState.interactionStartIndex - 1; // Minus one to account for first MoveNext().
- m_InteractionEndIndex = bindingState.interactionStartIndex + bindingState.interactionCount;
-
- return true;
- }
- }
- }
-
- private bool MoveToNextInteraction()
- {
- while (m_InteractionCurrentIndex < m_InteractionEndIndex)
- {
- ++m_InteractionCurrentIndex;
- if (m_InteractionCurrentIndex == m_InteractionEndIndex)
- break;
- var interaction = m_State.interactions[m_InteractionCurrentIndex];
- if (FindParameter(interaction))
- return true;
- }
- return false;
- }
-
- private bool MoveToNextProcessor()
- {
- while (m_ProcessorCurrentIndex < m_ProcessorEndIndex)
- {
- ++m_ProcessorCurrentIndex;
- if (m_ProcessorCurrentIndex == m_ProcessorEndIndex)
- break;
- var processor = m_State.processors[m_ProcessorCurrentIndex];
- if (FindParameter(processor))
- return true;
- }
- return false;
- }
-
- private bool FindParameter(object instance)
- {
- if (m_ObjectType != null && !m_ObjectType.IsInstanceOfType(instance))
- return false;
-
- var field = instance.GetType().GetField(m_ParameterName,
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
- if (field == null)
- return false;
-
- m_CurrentParameter = field;
- m_CurrentObject = instance;
-
- return true;
- }
-
- public bool MoveNext()
- {
- while (true)
- {
- if (m_MayBeInteraction && MoveToNextInteraction())
- return true;
-
- if (m_MayBeProcessor && MoveToNextProcessor())
- return true;
-
- if (!MoveToNextBinding())
- return false;
-
- if (m_MayBeComposite && m_CurrentBindingIsComposite)
- {
- var compositeIndex = m_State.GetBindingState(m_BindingCurrentIndex).compositeOrCompositeBindingIndex;
- var composite = m_State.composites[compositeIndex];
- if (FindParameter(composite))
- return true;
- }
- }
- }
-
- public unsafe void Reset()
- {
- m_CurrentObject = default;
- m_CurrentParameter = default;
- m_InteractionCurrentIndex = default;
- m_InteractionEndIndex = default;
- m_ProcessorCurrentIndex = default;
- m_ProcessorEndIndex = default;
- m_CurrentBindingIsComposite = default;
- if (m_MapIndex < 0)
- {
- m_BindingCurrentIndex = -1; // Account for first MoveNext().
- m_BindingEndIndex = m_State.totalBindingCount;
- }
- else
- {
- m_BindingCurrentIndex = m_State.mapIndices[m_MapIndex].bindingStartIndex - 1; // Account for first MoveNext().
- m_BindingEndIndex = m_State.mapIndices[m_MapIndex].bindingStartIndex + m_State.mapIndices[m_MapIndex].bindingCount;
- }
- }
-
- public Parameter Current => new Parameter
- {
- instance = m_CurrentObject,
- field = m_CurrentParameter,
- bindingIndex = m_BindingCurrentIndex,
- };
-
- object IEnumerator.Current => Current;
-
- public void Dispose()
- {
- }
- }
-
- internal struct ParameterOverride
- {
- public string objectRegistrationName; // Optional. Such as "hold" or "scale".
- public string parameter;
- public InputBinding bindingMask;
- public PrimitiveValue value;
-
- public Type objectType =>
- InputProcessor.s_Processors.LookupTypeRegistration(objectRegistrationName)
- ?? InputInteraction.s_Interactions.LookupTypeRegistration(objectRegistrationName)
- ?? InputBindingComposite.s_Composites.LookupTypeRegistration(objectRegistrationName);
-
- public ParameterOverride(string parameterName, InputBinding bindingMask, PrimitiveValue value = default)
- {
- var colonIndex = parameterName.IndexOf(':');
- if (colonIndex < 0)
- {
- objectRegistrationName = null;
- parameter = parameterName;
- }
- else
- {
- objectRegistrationName = parameterName.Substring(0, colonIndex);
- parameter = parameterName.Substring(colonIndex + 1);
- }
- this.bindingMask = bindingMask;
- this.value = value;
- }
-
- public ParameterOverride(string objectRegistrationName, string parameterName, InputBinding bindingMask, PrimitiveValue value = default)
- {
- this.objectRegistrationName = objectRegistrationName;
- this.parameter = parameterName;
- this.bindingMask = bindingMask;
- this.value = value;
- }
-
- // Find the *most specific* override to apply to the given parameter.
- public static ParameterOverride? Find(InputActionMap actionMap, ref InputBinding binding, string parameterName, string objectRegistrationName)
- {
- // Look at level of map.
- var overrideOnMap = Find(actionMap.m_ParameterOverrides, actionMap.m_ParameterOverridesCount, ref binding, parameterName,
- objectRegistrationName);
-
- // Look at level of asset (if present).
- var asset = actionMap.asset;
- var overrideOnAsset = asset != null
- ? Find(asset.m_ParameterOverrides, asset.m_ParameterOverridesCount, ref binding, parameterName,
- objectRegistrationName)
- : null;
-
- return PickMoreSpecificOne(overrideOnMap, overrideOnAsset);
- }
-
- private static ParameterOverride? Find(ParameterOverride[] overrides, int overrideCount,
- ref InputBinding binding, string parameterName, string objectRegistrationName)
- {
- ParameterOverride? result = null;
- for (var i = 0; i < overrideCount; ++i)
- {
- ref var current = ref overrides[i];
-
- if (!string.Equals(parameterName, current.parameter, StringComparison.OrdinalIgnoreCase))
- continue; // Different parameter name.
-
- if (!current.bindingMask.Matches(binding))
- continue;
-
- if (current.objectRegistrationName != null && !string.Equals(current.objectRegistrationName, objectRegistrationName,
- StringComparison.OrdinalIgnoreCase))
- continue;
-
- if (result == null)
- {
- // First match.
- result = current;
- }
- else
- {
- // Already have a match. See which one is more specific.
- result = PickMoreSpecificOne(result, current);
- }
- }
- return result;
- }
-
- private static ParameterOverride? PickMoreSpecificOne(ParameterOverride? first, ParameterOverride? second)
- {
- if (first == null)
- return second;
- if (second == null)
- return first;
-
- // Having an objectRegistrationName always wins vs not having one.
- if (first.Value.objectRegistrationName != null && second.Value.objectRegistrationName == null)
- return first;
- if (second.Value.objectRegistrationName != null && first.Value.objectRegistrationName == null)
- return second;
-
- // Targeting a specific path always wins vs not doing so.
- if (first.Value.bindingMask.effectivePath != null && second.Value.bindingMask.effectivePath == null)
- return first;
- if (second.Value.bindingMask.effectivePath != null && first.Value.bindingMask.effectivePath == null)
- return second;
-
- // Targeting a specific actions always wins vs not doing so.
- if (first.Value.bindingMask.action != null && second.Value.bindingMask.action == null)
- return first;
- if (second.Value.bindingMask.action != null && first.Value.bindingMask.action == null)
- return second;
-
- // Undecided. First wins by default.
- return first;
- }
- }
- }
- }
|