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