Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Input System 1.1.0 preview 2 - Axis with modifier key composite

Discussion in 'Input System' started by bjornsyse, Jan 8, 2021.

  1. bjornsyse


    Mar 28, 2017

    Using the latest preview, is there any way to add a 1D axis, let's say a Gamepad joystick Y direction + modifier key so that the Action only is activated when the a certain modifier key on the gamepad + the axis is used in conjunction? I've seen the button + modifier, but I'm after an axis with modifier.

    Best regards

    - Björn
  2. _s_e_r_


    Jan 28, 2015

    So I needed the same thing, and saw that even on the 1.1.0-preview 3, there was nothing like an axis with modifier, so looking at the documentation for creating a custom composite:

    as well as looking at the code of both the AxisComposite, and OneModifierComposite, I came up with this:

    Code (CSharp):
    2. using System.ComponentModel;
    3. using UnityEngine;
    4. using UnityEngine.InputSystem;
    5. using UnityEngine.InputSystem.Composites;
    6. using UnityEngine.InputSystem.Layouts;
    7. using UnityEngine.InputSystem.Utilities;
    9. #if UNITY_EDITOR
    10. using UnityEditor;
    11. [InitializeOnLoad] // Automatically register in editor.
    12. #endif
    14. [DisplayStringFormat("{modifier}+{negative}/{positive}")]
    15. [DisplayName("Positive/Negative Binding with Modifier")]
    16. public class AxisModifierComposite : AxisComposite
    17. {
    18.     // Each part binding is represented as a field of type int and annotated with
    19.     // InputControlAttribute. Setting "layout" restricts the controls that
    20.     // are made available for picking in the UI.
    21.     //
    22.     // On creation, the int value is set to an integer identifier for the binding
    23.     // part. This identifier can read values from InputBindingCompositeContext.
    24.     // See ReadValue() below.
    25.     [InputControl(layout = "Button")] public int modifier;
    27.     // This method computes the resulting input value of the composite based
    28.     // on the input from its part bindings.
    29.     public override float ReadValue(ref InputBindingCompositeContext context)
    30.     {
    31.         if (context.ReadValueAsButton(modifier))
    32.             return base.ReadValue(ref context);
    33.         return default;
    34.     }
    36.     // This method computes the current actuation of the binding as a whole.
    37.     public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
    38.     {
    39.         if (context.ReadValueAsButton(modifier))
    40.             return base.EvaluateMagnitude(ref context);
    41.         return default;
    42.     }
    44.     static AxisModifierComposite()
    45.     {
    46.         InputSystem.RegisterBindingComposite<AxisModifierComposite>();
    47.     }
    49.     [RuntimeInitializeOnLoadMethod]
    50.     static void Init() {} // Trigger static constructor.
    51. }
    This can be used when adding a binding, and it's basically a combination of both a binding with one modifier, and a 1D Axis


    Hopefully it is helpful!
    bjornsyse likes this.
  3. Rene-Damm


    Unity Technologies

    Sep 15, 2012
    As of 1.1-preview.2, OneModifierComposite should provide this functionality. Unlike with ButtonWithOneModifier, the non-modifier binding can be to anything, not just buttons.
  4. bjornsyse


    Mar 28, 2017
    Ok, I'll have a look a both your suggestions tomorrow! OneModifierComposite, is that available in the Add binding dropdown or by code or where can I find it?
  5. bjornsyse


    Mar 28, 2017
    (Btw, Preview3 that just came out messed up the Oculus touch bindings. )
  6. bjornsyse


    Mar 28, 2017
    But can it be bound to a 1Daxis? I seems on my end I can only choose a specific direction for the axis, not make use of the negative/positive composite parts together with a modifier?

    I mean, I understand how I can bind it to for example a joystick axis like this:

    But what if I want to bind a modfier key to a Keyboard composite equivalent like using Up/Down arrows as an axis but together with a modifier. SHIFT+Up/Down for example. Can that be achieved @Rene-Damm ?
    Last edited: Feb 15, 2021
  7. bjornsyse


    Mar 28, 2017
    This is very helpful and work great, thank you a bunch for that!

    One question, which might not be directly related to your code but more how to think when using to modifier keys in general. Lets say I use Up/Down-arrows in composite for Action A. If I then want to add SHIFT as a modifier (using your script for example) together with Up/Down-arrows to engage Action B. Then it seems, Action A is also engaged whenever Action B is, since it reacty to the axis composite without considering any modifiers. What is the right way to plan around this? should I add inverted modfiers to gate the Action A bindings aswell?
  8. bjornsyse


    Mar 28, 2017
    Hi again

    I ended up adding this boolean flag to check for an Inverted state of the modifier key. That is, whether the state of the modifier key NOT being pressed should gate the action in question.

    Code (CSharp):
    1. public bool invertModifier;
    3.     // This method computes the resulting input value of the composite based
    4.     // on the input from its part bindings but gated by the modifier key.
    5.     public override float ReadValue(ref InputBindingCompositeContext context)
    6.     {
    7.         float baseValue = base.ReadValue(ref context);
    9.         if (!invertModifier)
    10.         {
    11.             if (context.ReadValueAsButton(modifier))
    12.                 return base.ReadValue(ref context);
    13.             return default;
    14.         }
    16.         if (!context.ReadValueAsButton(modifier))
    17.             return base.ReadValue(ref context);
    18.         return default;
    19.     }

    ArosRex likes this.
  9. huulong


    Jul 1, 2013
    Thanks, I needed the same but for a button, not an axis, so I derived from OneModifierComposite, added a flag invertModifier and then overrode the methods in a similar way, inverting all conditions on modifier. Here is the script with long comments removed:

    Code (CSharp):
    1. using System.ComponentModel;
    2. using Unity.Collections.LowLevel.Unsafe;
    3. using UnityEngine;
    4. using UnityEngine.InputSystem;
    5. using UnityEngine.InputSystem.Composites;
    6. using UnityEngine.InputSystem.Utilities;
    8. #if UNITY_EDITOR
    9. using UnityEditor;
    10. [InitializeOnLoad]
    11. #endif
    12. [DisplayStringFormat("{modifier}+{binding}")]
    13. [DisplayName("Binding With Invertible Modifier")]
    14. public class InvertibleModifierComposite : OneModifierComposite
    15. {
    16.     // In the editor, the static class constructor will be called on startup
    17.     // because of [InitializeOnLoad].
    18.     #if UNITY_EDITOR
    19.     static InvertibleModifierComposite()
    20.     {
    21.         // Trigger our RegisterBindingComposite code in the editor.
    22.         Initialize();
    23.     }
    24.     #endif
    26.     // In the player, [RuntimeInitializeOnLoadMethod] will make sure our
    27.     // initialization code gets called during startup.
    28.     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    29.     private static void Initialize()
    30.     {
    31.         InputSystem.RegisterBindingComposite<InvertibleModifierComposite>();
    32.     }
    34.     /// <summary>
    35.     /// When set to true, the modifier must be released for the binding to be considered.
    36.     /// Else, the modifier must be pressed, and this behaves like OneModifierComposite.
    37.     /// </summary>
    38.     public bool invertModifier = false;
    40.     // Below, we override all methods of OneModifierComposite that check modifier, and prefix the condition
    41.     // with `invertModifier ^`
    43.     public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
    44.     {
    45.         if (invertModifier ^ context.ReadValueAsButton(modifier))
    46.             return context.EvaluateMagnitude(binding);
    47.         return default;
    48.     }
    50.     public override unsafe void ReadValue(ref InputBindingCompositeContext context, void* buffer, int bufferSize)
    51.     {
    52.         if (invertModifier ^ context.ReadValueAsButton(modifier))
    53.             context.ReadValue(binding, buffer, bufferSize);
    54.         else
    55.             UnsafeUtility.MemClear(buffer, valueSizeInBytes);
    56.     }
    58.     public override object ReadValueAsObject(ref InputBindingCompositeContext context)
    59.     {
    60.         if (invertModifier ^ context.ReadValueAsButton(modifier))
    61.             return context.ReadValueAsObject(binding);
    62.         return null;
    63.     }
    64. }
    Full script source (latest version):

    Usage example (taken from comment in source above):

    Say you want Alt+Enter to toggle fullscreen, but only Enter (with Alt released) to trigger UI Submit, to avoid submitting and toggling fullscreen at the same time.
    First, bind a OneModifierComposite input Alt+Enter to your action ToggleFullscreen. You can also use an InvertibleModifierComposite with invertModifier = false.
    Then, bind an InvertibleModifierComposite with invertModifier = true to the UI Submit action (in this particular case, you'll also want to remove the default entry based on Usages "Submit [Any]" which overlaps Enter).
    Last edited: Mar 13, 2022
  10. huulong


    Jul 1, 2013
    Another idea could be to mimic other input editors and instead of having a freestyle modifier field which can take an arbitrary key/button, focus on common modifiers i.e. Ctrl, Shift, Alt, Meta/Super and add a bool member (flag) for each.

    Then, when bool is true, modifier must be pressed and when bool is false, modifier *must* be released. This way, there are no input conflicts arising as long as you never use the same set of modifiers twice (in the same input context).

    Alternatively we could use a ternary state: Pressed, Released, Any (works with both Pressed and Released) if we don't mind user pressing modifiers that would be unused otherwise.

    However, it requires some code change to check the common modifier keys specifically, so more work on the derived class. Also, it would not need Composite at all, as all info would be inside the flags, so I'm not sure which class we should derive from (InputBinding is a struct, it must be wrapped in some class when you add a simple Binding in InputActions, but I don't see which one in Worst case, we could make a dummy Composite with 0 child, but that seems a bit wasteful.
  11. eGregiousGames


    Oct 30, 2020
    Is there a way to do this in-editor yet?
  12. Zardify


    Jul 15, 2015
    Yep! There actually is. It's a bit non-intuitive and took me a while to think backwards, but this is basically how you'd do it:

    Where my first "One Modifier" has a Scale processor on it with a factor of -1.
    Oh, and Zoom is Action Type: Value, Control Type: Axis