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<float>
/// {
/// // 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<float>();
///
/// // Read the vector.
/// var secondValue = context.ReadValue<Vector2>();
///
/// // 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<Vector2>
/// {
/// [InputControl(layout = "Vector2")]
/// public int part;
///
/// public override Vector2 ReadValue(ref InputBindingCompositeContext context)
/// {
/// // Return the Vector3 with the greatest magnitude.
/// return context.ReadValue<Vector2, Vector2MagnitudeComparer>(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<float></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);
}
}
}
}
|