Maze / Library /PackageCache /com.unity.inputsystem@1.6.1 /InputSystem /Actions /InputActionParameters.cs
| 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; | |
| } | |
| } | |
| } | |
| } | |