using System;
using System.Collections.Generic;
namespace UnityEngine.InputSystem
{
///
/// Contextual data made available when processing values of composite bindings.
///
///
/// An instance of this struct is passed to .
/// 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 ReadValue. The data it retrieves is only valid during
/// the callback.
///
///
///
///
public struct InputBindingCompositeContext
{
///
/// Information about a control bound to a part of a composite.
///
public struct PartBinding
{
///
/// Identifier of the part. This is the numeric identifier stored in the public
/// fields of the composite by the input system.
///
public int part { get; set; }
///
/// The control bound to the part.
///
public InputControl control { get; set; }
}
///
/// Enumerate all the controls that are part of the composite.
///
///
public IEnumerable 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);
}
///
/// Read the value of the giving part binding.
///
/// 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.
/// Type of value to read. This must match the
/// value type expected from controls bound to the part.
/// The value read from the part bindings.
/// The given
/// value type does not match the actual value type of the control(s) bound
/// to the part.
///
/// If no control is bound to the given part, the return value will always
/// be default(TValue). 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 IComparable
/// implemented by .
///
/// Note that this method only works with values that are IComparable.
/// To read a value type that is not IComparable or to supply a custom
/// comparer, use .
///
/// If an invalid is supplied, the return value
/// will simply be default(TValue). No exception is thrown.
///
///
///
/// 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;
/// }
/// }
///
///
///
///
///
public unsafe TValue ReadValue(int partNumber)
where TValue : struct, IComparable
{
if (m_State == null)
return default;
return m_State.ReadCompositePartValue>
(m_BindingIndex, partNumber, null, out _);
}
///
/// Same as but also return the control
/// from which the value was read.
///
/// Number of the part to read. This is assigned
/// automatically by the input system and should be treated as an opaque
/// identifier.
/// Receives the 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
/// null if is not a valid part or if no
/// controls are bound to the part.
/// Type of value to read. This must match the
/// value type expected from controls bound to the part.
/// The value read from the part bindings.
///
/// Like , this method relies on using IComparable
/// implemented by to determine the greatest value
/// if multiple controls are bound to the specified part.
///
///
public unsafe TValue ReadValue(int partNumber, out InputControl sourceControl)
where TValue : struct, IComparable
{
if (m_State == null)
{
sourceControl = null;
return default;
}
var value = m_State.ReadCompositePartValue>(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
///
/// Read the value of the given part bindings and use the given
/// to determine which value to return if multiple controls are bound to the part.
///
/// Number of the part to read. This is assigned
/// automatically by the input system and should be treated as an opaque
/// identifier.
/// Instance of for comparing
/// multiple values.
/// Type of value to read. This must match the
/// value type expected from controls bound to the part.
/// The value read from the part bindings.
/// Comparer to use if multiple controls are bound to
/// the given part. All values will be compared using TComparer.Compare and
/// the greatest value will be returned.
/// The value read from the part bindings.
///
/// This method is a useful alternative to for
/// value types that do not implement IComparable or when the default comparison
/// behavior is undesirable.
///
///
///
/// 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);
/// }
/// }
///
///
///
///
///
public unsafe TValue ReadValue(int partNumber, TComparer comparer = default)
where TValue : struct
where TComparer : IComparer
{
if (m_State == null)
return default;
return m_State.ReadCompositePartValue(
m_BindingIndex, partNumber, null, out _, comparer);
}
///
/// Like but also return
/// the control from which the value has ultimately been read.
///
/// Number of the part to read. This is assigned
/// automatically by the input system and should be treated as an opaque
/// identifier.
/// Receives the 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
/// null if is not a valid part or if no
/// controls are bound to the part.
/// Instance of for comparing
/// multiple values.
/// Type of value to read. This must match the
/// value type expected from controls bound to the part.
/// The value read from the part bindings.
/// Comparer to use if multiple controls are bound to
/// the given part. All values will be compared using TComparer.Compare and
/// the greatest value will be returned.
/// The value read from the part bindings.
public unsafe TValue ReadValue(int partNumber, out InputControl sourceControl, TComparer comparer = default)
where TValue : struct
where TComparer : IComparer
{
if (m_State == null)
{
sourceControl = null;
return default;
}
var value = m_State.ReadCompositePartValue(m_BindingIndex, partNumber, null,
out var controlIndex, comparer);
if (controlIndex != InputActionState.kInvalidIndex)
sourceControl = m_State.controls[controlIndex];
else
sourceControl = null;
return value;
}
///
/// Like but treat bound controls as buttons. This means
/// that custom are respected and that floating-point
/// values from non-ButtonControls will be compared to .
///
/// Number of the part to read. This is assigned
/// automatically by the input system and should be treated as an opaque
/// identifier.
/// True if any button bound to the part is pressed.
///
/// This method expects all controls bound to the part to be of type InputControl<float>.
///
/// This method is different from just calling with a float
/// parameter and comparing the result to in that
/// custom press points set on individual ButtonControls will be respected.
///
///
///
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>(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);
}
///
/// Return the timestamp (see ) for when the given
/// binding part crossed the button press threshold (see ).
///
/// Number of the part to read. This is assigned
/// automatically by the input system and should be treated as an opaque
/// identifier.
/// Returns the time at which the given part binding moved into "press" state or 0 if there's
/// current no press.
///
/// If the given part has more than a single binding and/or more than a single bound control, the earliest
/// press time is returned.
///
public double GetPressTime(int partNumber)
{
return m_State.GetCompositePartPressTime(m_BindingIndex, partNumber);
}
internal InputActionState m_State;
internal int m_BindingIndex;
private struct DefaultComparer : IComparer
where TValue : IComparable
{
public int Compare(TValue x, TValue y)
{
return x.CompareTo(y);
}
}
}
}