123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- using System.ComponentModel;
- using UnityEngine.InputSystem.Layouts;
- using UnityEngine.InputSystem.Processors;
- using UnityEngine.InputSystem.Utilities;
-
- #if UNITY_EDITOR
- using System;
- using UnityEditor;
- using UnityEngine.InputSystem.Editor;
- using UnityEngine.UIElements;
- #endif
-
- namespace UnityEngine.InputSystem.Composites
- {
- /// <summary>
- /// A single axis value computed from one axis that pulls in the <see cref="negative"/> direction (<see cref="minValue"/>) and one
- /// axis that pulls in the <see cref="positive"/> direction (<see cref="maxValue"/>).
- /// </summary>
- /// <remarks>
- /// The limits of the axis are determined by <see cref="minValue"/> and <see cref="maxValue"/>.
- /// By default, they are set to <c>[-1..1]</c>. The values can be set as parameters.
- ///
- /// <example>
- /// <code>
- /// var action = new InputAction();
- /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2)")
- /// .With("Negative", "<Keyboard>/a")
- /// .With("Positive", "<Keyboard>/d");
- /// </code>
- /// </example>
- ///
- /// If both axes are actuated at the same time, the behavior depends on <see cref="whichSideWins"/>.
- /// By default, neither side will win (<see cref="WhichSideWins.Neither"/>) and the result
- /// will be 0 (or, more precisely, the midpoint between <see cref="minValue"/> and <see cref="maxValue"/>).
- /// This can be customized to make the positive side win (<see cref="WhichSideWins.Positive"/>)
- /// or the negative one (<see cref="WhichSideWins.Negative"/>).
- ///
- /// This is useful, for example, in a driving game where break should cancel out accelerate.
- /// By binding <see cref="negative"/> to the break control(s) and <see cref="positive"/> to the
- /// acceleration control(s), and setting <see cref="whichSideWins"/> to <see cref="WhichSideWins.Negative"/>,
- /// if the break button is pressed, it will always cause the acceleration button to be ignored.
- ///
- /// The actual <em>absolute</em> values of <see cref="negative"/> and <see cref="positive"/> are used
- /// to scale <see cref="minValue"/> and <see cref="maxValue"/> respectively. So if, for example, <see cref="positive"/>
- /// is bound to <see cref="Gamepad.rightTrigger"/> and the trigger is at a value of 0.5, then the resulting
- /// value is <c>maxValue * 0.5</c> (the actual formula is <c>midPoint + (maxValue - midPoint) * positive</c>).
- /// </remarks>
- [DisplayStringFormat("{negative}/{positive}")]
- [DisplayName("Positive/Negative Binding")]
- public class AxisComposite : InputBindingComposite<float>
- {
- /// <summary>
- /// Binding for the axis input that controls the negative [<see cref="minValue"/>..0] direction of the
- /// combined axis.
- /// </summary>
- /// <remarks>
- /// This property is automatically assigned by the input system.
- /// </remarks>
- // ReSharper disable once MemberCanBePrivate.Global
- // ReSharper disable once FieldCanBeMadeReadOnly.Global
- [InputControl(layout = "Axis")] public int negative = 0;
-
- /// <summary>
- /// Binding for the axis input that controls the positive [0..<see cref="maxValue"/>] direction of the
- /// combined axis.
- /// </summary>
- /// <remarks>
- /// This property is automatically assigned by the input system.
- /// </remarks>
- // ReSharper disable once MemberCanBePrivate.Global
- // ReSharper disable once FieldCanBeMadeReadOnly.Global
- [InputControl(layout = "Axis")] public int positive = 0;
-
- /// <summary>
- /// The lower bound that the axis is limited to. -1 by default.
- /// </summary>
- /// <remarks>
- /// This value corresponds to the full actuation of the control(s) bound to <see cref="negative"/>.
- ///
- /// <example>
- /// <code>
- /// var action = new InputAction();
- /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2)")
- /// .With("Negative", "<Keyboard>/a")
- /// .With("Positive", "<Keyboard>/d");
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="maxValue"/>
- /// <seealso cref="negative"/>
- // ReSharper disable once MemberCanBePrivate.Global
- // ReSharper disable once FieldCanBeMadeReadOnly.Global
- [Tooltip("Value to return when the negative side is fully actuated.")]
- public float minValue = -1;
-
- /// <summary>
- /// The upper bound that the axis is limited to. 1 by default.
- /// </summary>
- /// <remarks>
- /// This value corresponds to the full actuation of the control(s) bound to <see cref="positive"/>.
- ///
- /// <example>
- /// <code>
- /// var action = new InputAction();
- /// action.AddCompositeBinding("Axis(minValue=0,maxValue=2)")
- /// .With("Negative", "<Keyboard>/a")
- /// .With("Positive", "<Keyboard>/d");
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="minValue"/>
- /// <seealso cref="positive"/>
- // ReSharper disable once MemberCanBePrivate.Global
- // ReSharper disable once FieldCanBeMadeReadOnly.Global
- [Tooltip("Value to return when the positive side is fully actuated.")]
- public float maxValue = 1;
-
- /// <summary>
- /// If both the <see cref="positive"/> and <see cref="negative"/> button are actuated, this
- /// determines which value is returned from the composite.
- /// </summary>
- [Tooltip("If both the positive and negative side are actuated, decides what value to return. 'Neither' (default) means that " +
- "the resulting value is the midpoint between min and max. 'Positive' means that max will be returned. 'Negative' means that " +
- "min will be returned.")]
- public WhichSideWins whichSideWins = WhichSideWins.Neither;
-
- /// <summary>
- /// The value that is returned if the composite is in a neutral position, that is, if
- /// neither <see cref="positive"/> nor <see cref="negative"/> are actuated or if
- /// <see cref="whichSideWins"/> is set to <see cref="WhichSideWins.Neither"/> and
- /// both <see cref="positive"/> and <see cref="negative"/> are actuated.
- /// </summary>
- public float midPoint => (maxValue + minValue) / 2;
-
- ////TODO: add parameters to control ramp up&down
-
- /// <inheritdoc />
- public override float ReadValue(ref InputBindingCompositeContext context)
- {
- var negativeValue = Mathf.Abs(context.ReadValue<float>(negative));
- var positiveValue = Mathf.Abs(context.ReadValue<float>(positive));
-
- var negativeIsActuated = negativeValue > Mathf.Epsilon;
- var positiveIsActuated = positiveValue > Mathf.Epsilon;
-
- if (negativeIsActuated == positiveIsActuated)
- {
- switch (whichSideWins)
- {
- case WhichSideWins.Negative:
- positiveIsActuated = false;
- break;
-
- case WhichSideWins.Positive:
- negativeIsActuated = false;
- break;
-
- case WhichSideWins.Neither:
- return midPoint;
- }
- }
-
- var mid = midPoint;
-
- if (negativeIsActuated)
- return mid - (mid - minValue) * negativeValue;
-
- return mid + (maxValue - mid) * positiveValue;
- }
-
- /// <inheritdoc />
- public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
- {
- var value = ReadValue(ref context);
- if (value < midPoint)
- {
- value = Mathf.Abs(value - midPoint);
- return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(minValue), 0);
- }
-
- value = Mathf.Abs(value - midPoint);
- return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(maxValue), 0);
- }
-
- /// <summary>
- /// What happens to the value of an <see cref="AxisComposite"/> if both <see cref="positive"/>
- /// and <see cref="negative"/> are actuated at the same time.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1717:OnlyFlagsEnumsShouldHavePluralNames", Justification = "False positive: `Wins` is not a plural form.")]
- public enum WhichSideWins
- {
- /// <summary>
- /// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the sides cancel
- /// each other out and the result is 0.
- /// </summary>
- Neither = 0,
-
- /// <summary>
- /// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the value of
- /// <see cref="positive"/> wins and <see cref="negative"/> is ignored.
- /// </summary>
- Positive = 1,
-
- /// <summary>
- /// If both <see cref="positive"/> and <see cref="negative"/> are actuated, the value of
- /// <see cref="negative"/> wins and <see cref="positive"/> is ignored.
- /// </summary>
- Negative = 2,
- }
- }
-
- #if UNITY_EDITOR
- internal class AxisCompositeEditor : InputParameterEditor<AxisComposite>
- {
- private GUIContent m_WhichAxisWinsLabel = new GUIContent("Which Side Wins",
- "Determine which axis 'wins' if both are actuated at the same time. "
- + "If 'Neither' is selected, the result is 0 (or, more precisely, "
- + "the midpoint between minValue and maxValue).");
-
- public override void OnGUI()
- {
- target.whichSideWins = (AxisComposite.WhichSideWins)EditorGUILayout.EnumPopup(m_WhichAxisWinsLabel, target.whichSideWins);
- }
-
- #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
- public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
- {
- var modeField = new EnumField(m_WhichAxisWinsLabel.text, target.whichSideWins)
- {
- tooltip = m_WhichAxisWinsLabel.tooltip
- };
-
- modeField.RegisterValueChangedCallback(evt =>
- {
- target.whichSideWins = (AxisComposite.WhichSideWins)evt.newValue;
- onChangedCallback();
- });
-
- root.Add(modeField);
- }
-
- #endif
- }
- #endif
- }
|