File size: 9,311 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
using System.ComponentModel;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Processors;
using UnityEngine.InputSystem.Utilities;

namespace UnityEngine.InputSystem.Composites
{
    /// <summary>
    /// A single axis value computed from one axis that pulls in the <see cref="negative"/> direction (<see cref="minValue"/>) and one
    /// axis that pulls in the <see cref="positive"/> direction (<see cref="maxValue"/>).
    /// </summary>
    /// <remarks>
    /// The limits of the axis are determined by <see cref="minValue"/> and <see cref="maxValue"/>.
    /// By default, they are set to <c>[-1..1]</c>. The values can be set as parameters.
    ///
    /// <example>
    /// <code>
    /// var action = new InputAction();
    /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2")
    ///     .With("Negative", "&lt;Keyboard&gt;/a")
    ///     .With("Positive", "&lt;Keyboard&gt;/d");
    /// </code>
    /// </example>
    ///
    /// If both axes are actuated at the same time, the behavior depends on <see cref="whichSideWins"/>.
    /// By default, neither side will win (<see cref="WhichSideWins.Neither"/>) and the result
    /// will be 0 (or, more precisely, the midpoint between <see cref="minValue"/> and <see cref="maxValue"/>).
    /// This can be customized to make the positive side win (<see cref="WhichSideWins.Positive"/>)
    /// or the negative one (<see cref="WhichSideWins.Negative"/>).
    ///
    /// This is useful, for example, in a driving game where break should cancel out accelerate.
    /// By binding <see cref="negative"/> to the break control(s) and <see cref="positive"/> to the
    /// acceleration control(s), and setting <see cref="whichSideWins"/> to <see cref="WhichSideWins.Negative"/>,
    /// if the break button is pressed, it will always cause the acceleration button to be ignored.
    ///
    /// The actual <em>absolute</em> values of <see cref="negative"/> and <see cref="positive"/> are used
    /// to scale <see cref="minValue"/> and <see cref="maxValue"/> respectively. So if, for example, <see cref="positive"/>
    /// is bound to <see cref="Gamepad.rightTrigger"/> and the trigger is at a value of 0.5, then the resulting
    /// value is <c>maxValue * 0.5</c> (the actual formula is <c>midPoint + (maxValue - midPoint) * positive</c>).
    /// </remarks>
    [DisplayStringFormat("{negative}/{positive}")]
    [DisplayName("Positive/Negative Binding")]
    public class AxisComposite : InputBindingComposite<float>
    {
        /// <summary>
        /// Binding for the axis input that controls the negative [<see cref="minValue"/>..0] direction of the
        /// combined axis.
        /// </summary>
        /// <remarks>
        /// This property is automatically assigned by the input system.
        /// </remarks>
        // ReSharper disable once MemberCanBePrivate.Global
        // ReSharper disable once FieldCanBeMadeReadOnly.Global
        [InputControl(layout = "Axis")] public int negative = 0;

        /// <summary>
        /// Binding for the axis input that controls the positive [0..<see cref="maxValue"/>] direction of the
        /// combined axis.
        /// </summary>
        /// <remarks>
        /// This property is automatically assigned by the input system.
        /// </remarks>
        // ReSharper disable once MemberCanBePrivate.Global
        // ReSharper disable once FieldCanBeMadeReadOnly.Global
        [InputControl(layout = "Axis")] public int positive = 0;

        /// <summary>
        /// The lower bound that the axis is limited to. -1 by default.
        /// </summary>
        /// <remarks>
        /// This value corresponds to the full actuation of the control(s) bound to <see cref="negative"/>.
        ///
        /// <example>
        /// <code>
        /// var action = new InputAction();
        /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2")
        ///     .With("Negative", "&lt;Keyboard&gt;/a")
        ///     .With("Positive", "&lt;Keyboard&gt;/d");
        /// </code>
        /// </example>
        /// </remarks>
        /// <seealso cref="maxValue"/>
        /// <seealso cref="negative"/>
        // 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;

        /// <summary>
        /// The upper bound that the axis is limited to. 1 by default.
        /// </summary>
        /// <remarks>
        /// This value corresponds to the full actuation of the control(s) bound to <see cref="positive"/>.
        ///
        /// <example>
        /// <code>
        /// var action = new InputAction();
        /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2")
        ///     .With("Negative", "&lt;Keyboard&gt;/a")
        ///     .With("Positive", "&lt;Keyboard&gt;/d");
        /// </code>
        /// </example>
        /// </remarks>
        /// <seealso cref="minValue"/>
        /// <seealso cref="positive"/>
        // 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;

        /// <summary>
        /// If both the <see cref="positive"/> and <see cref="negative"/> button are actuated, this
        /// determines which value is returned from the composite.
        /// </summary>
        [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;

        /// <summary>
        /// The value that is returned if the composite is in a neutral position, that is, if
        /// neither <see cref="positive"/> nor <see cref="negative"/> are actuated or if
        /// <see cref="whichSideWins"/> is set to <see cref="WhichSideWins.Neither"/> and
        /// both <see cref="positive"/> and <see cref="negative"/> are actuated.
        /// </summary>
        public float midPoint => (maxValue + minValue) / 2;

        ////TODO: add parameters to control ramp up&down

        /// <inheritdoc />
        public override float ReadValue(ref InputBindingCompositeContext context)
        {
            var negativeValue = Mathf.Abs(context.ReadValue<float>(negative));
            var positiveValue = Mathf.Abs(context.ReadValue<float>(positive));

            var negativeIsActuated = negativeValue > Mathf.Epsilon;
            var positiveIsActuated = positiveValue > Mathf.Epsilon;

            if (negativeIsActuated == positiveIsActuated)
            {
                switch (whichSideWins)
                {
                    case WhichSideWins.Negative:
                        positiveIsActuated = false;
                        break;

                    case WhichSideWins.Positive:
                        negativeIsActuated = false;
                        break;

                    case WhichSideWins.Neither:
                        return midPoint;
                }
            }

            var mid = midPoint;

            if (negativeIsActuated)
                return mid - (mid - minValue) * negativeValue;

            return mid + (maxValue - mid) * positiveValue;
        }

        /// <inheritdoc />
        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);
        }

        /// <summary>
        /// What happens to the value of an <see cref="AxisComposite"/> if both <see cref="positive"/>
        /// and <see cref="negative"/> are actuated at the same time.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1717:OnlyFlagsEnumsShouldHavePluralNames", Justification = "False positive: `Wins` is not a plural form.")]
        public enum WhichSideWins
        {
            /// <summary>
            /// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the sides cancel
            /// each other out and the result is 0.
            /// </summary>
            Neither = 0,

            /// <summary>
            /// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the value of
            /// <see cref="positive"/> wins and <see cref="negative"/> is ignored.
            /// </summary>
            Positive = 1,

            /// <summary>
            /// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the value of
            /// <see cref="negative"/> wins and <see cref="positive"/> is ignored.
            /// </summary>
            Negative = 2,
        }
    }
}