File size: 16,806 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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
using System;
using System.Collections.Generic;

namespace UnityEngine.InputSystem
{
    /// <summary>
    /// Contextual data made available when processing values of composite bindings.
    /// </summary>
    /// <remarks>
    /// An instance of this struct is passed to <see
    /// cref="InputBindingComposite{TValue}.ReadValue(ref InputBindingCompositeContext)"/>.
    /// Use it to access contextual data such as the value for individual part bindings.
    ///
    /// Note that an instance of this struct should never be held on to past the duration
    /// of the call to <c>ReadValue</c>. The data it retrieves is only valid during
    /// the callback.
    /// </remarks>
    /// <seealso cref="InputBindingComposite"/>
    /// <seealso cref="InputBindingComposite{TValue}"/>
    /// <seealso cref="InputBindingComposite{TValue}.ReadValue(ref InputBindingCompositeContext)"/>
    public struct InputBindingCompositeContext
    {
        /// <summary>
        /// Information about a control bound to a part of a composite.
        /// </summary>
        public struct PartBinding
        {
            /// <summary>
            /// Identifier of the part. This is the numeric identifier stored in the public
            /// fields of the composite by the input system.
            /// </summary>
            public int part { get; set; }

            /// <summary>
            /// The control bound to the part.
            /// </summary>
            public InputControl control { get; set; }
        }

        /// <summary>
        /// Enumerate all the controls that are part of the composite.
        /// </summary>
        /// <seealso cref="InputBindingComposite.FinishSetup"/>
        public IEnumerable<PartBinding> controls
        {
            get
            {
                if (m_State == null)
                    yield break;

                var totalBindingCount = m_State.totalBindingCount;
                for (var bindingIndex = m_BindingIndex + 1; bindingIndex < totalBindingCount; ++bindingIndex)
                {
                    var bindingState = m_State.GetBindingState(bindingIndex);
                    if (!bindingState.isPartOfComposite)
                        break;

                    var controlStartIndex = bindingState.controlStartIndex;
                    for (var i = 0; i < bindingState.controlCount; ++i)
                    {
                        var control = m_State.controls[controlStartIndex + i];
                        yield return new PartBinding
                        {
                            part = bindingState.partIndex,
                            control = control
                        };
                    }
                }
            }
        }

        public float EvaluateMagnitude(int partNumber)
        {
            return m_State.EvaluateCompositePartMagnitude(m_BindingIndex, partNumber);
        }

        /// <summary>
        /// Read the value of the giving part binding.
        /// </summary>
        /// <param name="partNumber">Number of the part to read. This is assigned
        /// automatically by the input system and should be treated as an opaque
        /// identifier. See the example below.</param>
        /// <typeparam name="TValue">Type of value to read. This must match the
        /// value type expected from controls bound to the part.</typeparam>
        /// <returns>The value read from the part bindings.</returns>
        /// <exception cref="InvalidOperationException">The given <typeparamref name="TValue"/>
        /// value type does not match the actual value type of the control(s) bound
        /// to the part.</exception>
        /// <remarks>
        /// If no control is bound to the given part, the return value will always
        /// be <c>default(TValue)</c>. If a single control is bound to the part, the
        /// value will be that of the control. If multiple controls are bound to a
        /// part, the return value will be that greatest one according to <c>IComparable</c>
        /// implemented by <typeparamref name="TValue"/>.
        ///
        /// Note that this method only works with values that are <c>IComparable</c>.
        /// To read a value type that is not <c>IComparable</c> or to supply a custom
        /// comparer, use <see cref="ReadValue{TValue,TComparer}(int,TComparer)"/>.
        ///
        /// If an invalid <paramref name="partNumber"/> is supplied, the return value
        /// will simply be <c>default(TValue)</c>. No exception is thrown.
        ///
        /// <example>
        /// <code>
        /// public class MyComposite : InputBindingComposite&lt;float&gt;
        /// {
        ///     // Defines a "part" binding for the composite. Each part can be
        ///     // bound to arbitrary many times (including not at all). The "layout"
        ///     // property of the attribute we supply determines what kind of
        ///     // control is expected to be bound to the part.
        ///     //
        ///     // When initializing a composite instance, the input system will
        ///     // automatically assign part numbers and store them in the fields
        ///     // we define here.
        ///     [InputControl(layout = "Button")]
        ///     public int firstPart;
        ///
        ///     // Defines a second part.
        ///     [InputControl(layout = "Vector2")]
        ///     public int secondPart;
        ///
        ///     public override float ReadValue(ref InputBindingCompositeContext context)
        ///     {
        ///         // Read the button.
        ///         var firstValue = context.ReadValue&lt;float&gt;();
        ///
        ///         // Read the vector.
        ///         var secondValue = context.ReadValue&lt;Vector2&gt;();
        ///
        ///         // Perform some computation based on the inputs. Here, we just
        ///         // scale the vector by the value we got from the button.
        ///         return secondValue * firstValue;
        ///     }
        /// }
        /// </code>
        /// </example>
        /// </remarks>
        /// <seealso cref="ReadValue{TValue,TComparer}(int,TComparer)"/>
        /// <seealso cref="InputControl{TValue}.ReadValue"/>
        public unsafe TValue ReadValue<TValue>(int partNumber)
            where TValue : struct, IComparable<TValue>
        {
            if (m_State == null)
                return default;

            return m_State.ReadCompositePartValue<TValue, DefaultComparer<TValue>>
                    (m_BindingIndex, partNumber, null, out _);
        }

        /// <summary>
        /// Same as <see cref="ReadValue{TValue}(int)"/> but also return the control
        /// from which the value was read.
        /// </summary>
        /// <param name="partNumber">Number of the part to read. This is assigned
        /// automatically by the input system and should be treated as an opaque
        /// identifier.</param>
        /// <param name="sourceControl">Receives the <see cref="InputControl"/> from
        /// which the value was read. If multiple controls are bound to the given part,
        /// this is the control whose value was ultimately selected. Will be set to
        /// <c>null</c> if <paramref name="partNumber"/> is not a valid part or if no
        /// controls are bound to the part.</param>
        /// <typeparam name="TValue">Type of value to read. This must match the
        /// value type expected from controls bound to the part.</typeparam>
        /// <returns>The value read from the part bindings.</returns>
        /// <remarks>
        /// Like <see cref="ReadValue{TValue}(int)"/>, this method relies on using <c>IComparable</c>
        /// implemented by <typeparamref name="TValue"/> to determine the greatest value
        /// if multiple controls are bound to the specified part.
        /// </remarks>
        /// <seealso cref="ReadValue{TValue}(int)"/>
        public unsafe TValue ReadValue<TValue>(int partNumber, out InputControl sourceControl)
            where TValue : struct, IComparable<TValue>
        {
            if (m_State == null)
            {
                sourceControl = null;
                return default;
            }

            var value = m_State.ReadCompositePartValue<TValue, DefaultComparer<TValue>>(m_BindingIndex, partNumber,
                null, out var controlIndex);
            if (controlIndex != InputActionState.kInvalidIndex)
                sourceControl = m_State.controls[controlIndex];
            else
                sourceControl = null;

            return value;
        }

        ////TODO: once we can break the API, remove the versions that rely on comparers and do everything through magnitude

        /// <summary>
        /// Read the value of the given part bindings and use the given <paramref name="comparer"/>
        /// to determine which value to return if multiple controls are bound to the part.
        /// </summary>
        /// <param name="partNumber">Number of the part to read. This is assigned
        /// automatically by the input system and should be treated as an opaque
        /// identifier.</param>
        /// <param name="comparer">Instance of <typeparamref name="TComparer"/> for comparing
        /// multiple values.</param>
        /// <typeparam name="TValue">Type of value to read. This must match the
        /// value type expected from controls bound to the part.</typeparam>
        /// <returns>The value read from the part bindings.</returns>
        /// <typeparam name="TComparer">Comparer to use if multiple controls are bound to
        /// the given part. All values will be compared using <c>TComparer.Compare</c> and
        /// the greatest value will be returned.</typeparam>
        /// <returns>The value read from the part bindings.</returns>
        /// <remarks>
        /// This method is a useful alternative to <see cref="ReadValue{TValue}(int)"/> for
        /// value types that do not implement <c>IComparable</c> or when the default comparison
        /// behavior is undesirable.
        ///
        /// <example>
        /// <code>
        /// public class CompositeWithVector2Part : InputBindingComposite&lt;Vector2&gt;
        /// {
        ///     [InputControl(layout = "Vector2")]
        ///     public int part;
        ///
        ///     public override Vector2 ReadValue(ref InputBindingCompositeContext context)
        ///     {
        ///         // Return the Vector3 with the greatest magnitude.
        ///         return context.ReadValue&lt;Vector2, Vector2MagnitudeComparer&gt;(part);
        ///     }
        /// }
        /// </code>
        /// </example>
        /// </remarks>
        /// <seealso cref="Utilities.Vector2MagnitudeComparer"/>
        /// <seealso cref="Utilities.Vector3MagnitudeComparer"/>
        public unsafe TValue ReadValue<TValue, TComparer>(int partNumber, TComparer comparer = default)
            where TValue : struct
            where TComparer : IComparer<TValue>
        {
            if (m_State == null)
                return default;

            return m_State.ReadCompositePartValue<TValue, TComparer>(
                m_BindingIndex, partNumber, null, out _, comparer);
        }

        /// <summary>
        /// Like <see cref="ReadValue{TValue,TComparer}(int,TComparer)"/> but also return
        /// the control from which the value has ultimately been read.
        /// </summary>
        /// <param name="partNumber">Number of the part to read. This is assigned
        /// automatically by the input system and should be treated as an opaque
        /// identifier.</param>
        /// <param name="sourceControl">Receives the <see cref="InputControl"/> from
        /// which the value was read. If multiple controls are bound to the given part,
        /// this is the control whose value was ultimately selected. Will be set to
        /// <c>null</c> if <paramref name="partNumber"/> is not a valid part or if no
        /// controls are bound to the part.</param>
        /// <param name="comparer">Instance of <typeparamref name="TComparer"/> for comparing
        /// multiple values.</param>
        /// <typeparam name="TValue">Type of value to read. This must match the
        /// value type expected from controls bound to the part.</typeparam>
        /// <returns>The value read from the part bindings.</returns>
        /// <typeparam name="TComparer">Comparer to use if multiple controls are bound to
        /// the given part. All values will be compared using <c>TComparer.Compare</c> and
        /// the greatest value will be returned.</typeparam>
        /// <returns>The value read from the part bindings.</returns>
        public unsafe TValue ReadValue<TValue, TComparer>(int partNumber, out InputControl sourceControl, TComparer comparer = default)
            where TValue : struct
            where TComparer : IComparer<TValue>
        {
            if (m_State == null)
            {
                sourceControl = null;
                return default;
            }

            var value = m_State.ReadCompositePartValue<TValue, TComparer>(m_BindingIndex, partNumber, null,
                out var controlIndex, comparer);

            if (controlIndex != InputActionState.kInvalidIndex)
                sourceControl = m_State.controls[controlIndex];
            else
                sourceControl = null;

            return value;
        }

        /// <summary>
        /// Like <see cref="ReadValue{TValue}(int)"/> but treat bound controls as buttons. This means
        /// that custom <see cref="Controls.ButtonControl.pressPoint"/> are respected and that floating-point
        /// values from non-ButtonControls will be compared to <see cref="InputSettings.defaultButtonPressPoint"/>.
        /// </summary>
        /// <param name="partNumber">Number of the part to read. This is assigned
        /// automatically by the input system and should be treated as an opaque
        /// identifier.</param>
        /// <returns>True if any button bound to the part is pressed.</returns>
        /// <remarks>
        /// This method expects all controls bound to the part to be of type <c>InputControl&lt;float&gt;</c>.
        ///
        /// This method is different from just calling <see cref="ReadValue{TValue}(int)"/> with a <c>float</c>
        /// parameter and comparing the result to <see cref="InputSettings.defaultButtonPressPoint"/> in that
        /// custom press points set on individual ButtonControls will be respected.
        /// </remarks>
        /// <seealso cref="Controls.ButtonControl"/>
        /// <seealso cref="InputSettings.defaultButtonPressPoint"/>
        public unsafe bool ReadValueAsButton(int partNumber)
        {
            if (m_State == null)
                return default;

            ////REVIEW: wouldn't this have to take release points into account now?

            var buttonValue = false;
            m_State.ReadCompositePartValue<float, DefaultComparer<float>>(m_BindingIndex, partNumber, &buttonValue,
                out _);
            return buttonValue;
        }

        public unsafe void ReadValue(int partNumber, void* buffer, int bufferSize)
        {
            m_State?.ReadCompositePartValue(m_BindingIndex, partNumber, buffer, bufferSize);
        }

        public object ReadValueAsObject(int partNumber)
        {
            return m_State.ReadCompositePartValueAsObject(m_BindingIndex, partNumber);
        }

        /// <summary>
        /// Return the timestamp (see <see cref="LowLevel.InputEvent.time"/>) for when the given
        /// binding part crossed the button press threshold (see <see cref="Controls.ButtonControl.pressPoint"/>).
        /// </summary>
        /// <param name="partNumber">Number of the part to read. This is assigned
        /// automatically by the input system and should be treated as an opaque
        /// identifier.</param>
        /// <returns>Returns the time at which the given part binding moved into "press" state or 0 if there's
        /// current no press.</returns>
        /// <remarks>
        /// If the given part has more than a single binding and/or more than a single bound control, the <em>earliest</em>
        /// press time is returned.
        /// </remarks>
        public double GetPressTime(int partNumber)
        {
            return m_State.GetCompositePartPressTime(m_BindingIndex, partNumber);
        }

        internal InputActionState m_State;
        internal int m_BindingIndex;

        private struct DefaultComparer<TValue> : IComparer<TValue>
            where TValue : IComparable<TValue>
        {
            public int Compare(TValue x, TValue y)
            {
                return x.CompareTo(y);
            }
        }
    }
}