using UnityEngine.InputSystem.Layouts; using UnityEngine.InputSystem.Processors; using UnityEngine.InputSystem.Utilities; using UnityEngine.Scripting; namespace UnityEngine.InputSystem.Composites { /// /// A single axis value computed from a "negative" and a "positive" button. /// /// /// This composite allows to arrange any arbitrary two buttons from a device in an /// axis configuration such that one button pushes in one direction and the other /// pushes in the opposite direction. /// /// The limits of the axis are determined by and . /// By default, they are set to [-1..1]. The values can be set as parameters. /// /// /// /// var action = new InputAction(); /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2") /// .With("Negative", "<Keyboard>/a") /// .With("Positive", "<Keyboard>/d"); /// /// /// /// If both buttons are pressed at the same time, the behavior depends on . /// By default, neither side will win () and the result /// will be 0 (or, more precisely, the midpoint between and ). /// This can be customized to make the positive side win () /// or the negative one (). /// /// This is useful, for example, in a driving game where break should cancel out accelerate. /// By binding to the break control(s) and to the /// acceleration control(s), and setting to , /// if the break button is pressed, it will always cause the acceleration button to be ignored. /// /// The values returned are the actual actuation values of the buttons, unaltered for /// and inverted for . This means that if the buttons are actual axes (e.g. /// the triggers on gamepads), then the values correspond to how much the axis is actuated. /// [Preserve] [DisplayStringFormat("{negative}/{positive}")] public class AxisComposite : InputBindingComposite { /// /// Binding for the button that controls the positive direction of the axis. /// /// /// This property is automatically assigned by the input system. /// // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once FieldCanBeMadeReadOnly.Global [InputControl(layout = "Button")] public int negative = 0; /// /// Binding for the button that controls the negative direction of the axis. /// /// /// This property is automatically assigned by the input system. /// // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once FieldCanBeMadeReadOnly.Global [InputControl(layout = "Button")] public int positive = 0; /// /// The lower bound that the axis is limited to. -1 by default. /// /// /// This value corresponds to the full actuation of the control(s) bound to . /// /// /// /// var action = new InputAction(); /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2") /// .With("Negative", "<Keyboard>/a") /// .With("Positive", "<Keyboard>/d"); /// /// /// /// /// // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once FieldCanBeMadeReadOnly.Global [Tooltip("Value to return when the negative side is fully actuated.")] public float minValue = -1; /// /// The upper bound that the axis is limited to. 1 by default. /// /// /// This value corresponds to the full actuation of the control(s) bound to . /// /// /// /// var action = new InputAction(); /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2") /// .With("Negative", "<Keyboard>/a") /// .With("Positive", "<Keyboard>/d"); /// /// /// /// /// // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once FieldCanBeMadeReadOnly.Global [Tooltip("Value to return when the positive side is fully actuated.")] public float maxValue = 1; /// /// If both the and button are actuated, this /// determines which value is returned from the composite. /// [Tooltip("If both the positive and negative side are actuated, decides what value to return. 'Neither' (default) means that " + "the resulting value is the midpoint between min and max. 'Positive' means that max will be returned. 'Negative' means that " + "min will be returned.")] public WhichSideWins whichSideWins = WhichSideWins.Neither; /// /// The value that is returned if the composite is in a neutral position, i.e. if /// neither nor are actuated or if /// is set to and /// both and are actuated. /// public float midPoint => (maxValue + minValue) / 2; ////TODO: add parameters to control ramp up&down /// public override float ReadValue(ref InputBindingCompositeContext context) { var negativeValue = context.ReadValue(negative); var positiveValue = context.ReadValue(positive); ////TODO: take partial actuation into account (e.g. amount of actuation of gamepad trigger should result in partial actuation of axis) ////REVIEW: should this respect press points? var negativeIsPressed = negativeValue > 0; var positiveIsPressed = positiveValue > 0; if (negativeIsPressed == positiveIsPressed) { switch (whichSideWins) { case WhichSideWins.Negative: return -negativeValue; case WhichSideWins.Positive: return positiveValue; case WhichSideWins.Neither: return midPoint; } } if (negativeIsPressed) return -negativeValue; return positiveValue; } /// public override float EvaluateMagnitude(ref InputBindingCompositeContext context) { var value = ReadValue(ref context); if (value < midPoint) { value = Mathf.Abs(value - midPoint); return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(minValue), 0); } value = Mathf.Abs(value - midPoint); return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(maxValue), 0); } /// /// What happens to the value of an if both /// and are actuated at the same time. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1717:OnlyFlagsEnumsShouldHavePluralNames", Justification = "False positive: `Wins` is not a plural form.")] public enum WhichSideWins { /// /// If both and are actuated, the sides cancel /// each other out and the result is 0. /// Neither = 0, /// /// If both and are actuated, the value of /// wins and is ignored. /// Positive = 1, /// /// If both and are actuated, the value of /// wins and is ignored. /// Negative = 2, } } }