Search Unity

Resolved How to know if 1D axis binds are being pressed.

Discussion in 'Input System' started by Phoenix248, Nov 3, 2022.

  1. Phoenix248

    Phoenix248

    Joined:
    Sep 20, 2019
    Posts:
    52
    Hi everyone!

    I have a 1D axis type action, with positive and negative buttons, and I need to know if they are being pressed in the current frame. The problem is that when I press both sides at the same time, it returns 0. So pressing both and pressing none results in the same thing. How could I tell them apart?
     
  2. andrew_oc

    andrew_oc

    Unity Technologies

    Joined:
    Apr 16, 2021
    Posts:
    77
    This probably isn't as easy to do as it should be but one way would be to create a custom axis composite that contains extra information, namely whether any keys of the composite are currently actuated. Here's a working example:

    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using UnityEngine.InputSystem;
    4. using UnityEngine.InputSystem.Composites;
    5. using UnityEngine.InputSystem.Layouts;
    6. using UnityEngine.InputSystem.Processors;
    7.  
    8. public struct CustomAxisValue
    9. {
    10.     public float absoluteValue;
    11.     public bool isAnyControlActuated;
    12. }
    13.  
    14. #if UNITY_EDITOR
    15. [InitializeOnLoad]
    16. #endif
    17. public class CustomAxisComposite : InputBindingComposite<CustomAxisValue>
    18. {
    19. #if UNITY_EDITOR
    20.     static CustomAxisComposite()
    21.     {
    22.         Initialize();
    23.     }
    24.  
    25. #endif
    26.  
    27.     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    28.     private static void Initialize()
    29.     {
    30.         InputSystem.RegisterBindingComposite<CustomAxisComposite>();
    31.     }
    32.  
    33.     [InputControl(layout = "Axis")] public int negative = 0;
    34.     [InputControl(layout = "Axis")] public int positive = 0;
    35.  
    36.     public float minValue = -1;
    37.     public float maxValue = 1;
    38.  
    39.     public AxisComposite.WhichSideWins whichSideWins = AxisComposite.WhichSideWins.Neither;
    40.  
    41.     public float midPoint => (maxValue + minValue) / 2;
    42.  
    43.     public override CustomAxisValue ReadValue(ref InputBindingCompositeContext context)
    44.     {
    45.         var negativeValue = Mathf.Abs(context.ReadValue<float>(negative));
    46.         var positiveValue = Mathf.Abs(context.ReadValue<float>(positive));
    47.  
    48.         var negativeIsActuated = negativeValue > Mathf.Epsilon;
    49.         var positiveIsActuated = positiveValue > Mathf.Epsilon;
    50.  
    51.         if (negativeIsActuated == positiveIsActuated)
    52.         {
    53.             switch (whichSideWins)
    54.             {
    55.                 case AxisComposite.WhichSideWins.Negative:
    56.                     positiveIsActuated = false;
    57.                     break;
    58.  
    59.                 case AxisComposite.WhichSideWins.Positive:
    60.                     negativeIsActuated = false;
    61.                     break;
    62.  
    63.                 case AxisComposite.WhichSideWins.Neither:
    64.                     return new CustomAxisValue
    65.                     {
    66.                         absoluteValue = midPoint,
    67.                         isAnyControlActuated = negativeIsActuated
    68.                     };
    69.             }
    70.         }
    71.  
    72.         var mid = midPoint;
    73.  
    74.         if (negativeIsActuated)
    75.             return new CustomAxisValue
    76.             {
    77.                 absoluteValue = mid - (mid - minValue) * negativeValue,
    78.                 isAnyControlActuated = true
    79.             };
    80.  
    81.         return new CustomAxisValue
    82.         {
    83.             absoluteValue = mid + (maxValue - mid) * positiveValue,
    84.             isAnyControlActuated = true
    85.         };
    86.     }
    87.  
    88.     public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
    89.     {
    90.         var customAxisValue = ReadValue(ref context);
    91.  
    92.         // if both axes are currently actuated, the absolute value will be zero, but if we return zero from EvaluateMagnitude
    93.         // the system will mark the action as not in progress and subsequent calls to InputAction.ReadValue<> won't return
    94.         // the value we want, so just return a tiny value here to fool the system.
    95.         if (customAxisValue.isAnyControlActuated && customAxisValue.absoluteValue == 0)
    96.             return float.Epsilon;
    97.  
    98.         var value = customAxisValue.absoluteValue;
    99.         if (value < midPoint)
    100.         {
    101.             value = Mathf.Abs(value - midPoint);
    102.             return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(minValue), 0);
    103.         }
    104.  
    105.         value = Mathf.Abs(value - midPoint);
    106.         return NormalizeProcessor.Normalize(value, 0, Mathf.Abs(maxValue), 0);
    107.     }
    108. }
    Then when reading the value, you can check if the isAnyControlActuated property is true, like this:

    Code (CSharp):
    1. var value = m_Action.ReadValue<CustomAxisValue>();
    2.         Debug.Log("Is any control actuated = " + value.isAnyControlActuated + ", value = " + value.absoluteValue);
     
  3. Phoenix248

    Phoenix248

    Joined:
    Sep 20, 2019
    Posts:
    52
    Working perfectly, thanks! Would be nice to have this built in in the future.