356 lines
16 KiB
C#
356 lines
16 KiB
C#
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|