Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. Unity 2020 LTS & Unity 2021.1 have been released.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Using ButtonWithOneModifier without triggering another action using the same binding?

Discussion in 'Input System' started by Mman1235, Nov 9, 2019.

  1. Mman1235

    Mman1235

    Joined:
    Aug 23, 2015
    Posts:
    2
    Hi,

    Been using as of late the new package, and it is an absolute joy to use.

    I have only really encountered one issue (aside from maybe fleshing out the documentation farther).

    Let's say I have 2 actions:

    actionA uses a button with a press interaction.

    actionB uses a ButtonWithOneModifier the modifier is assigned to some stick direction,
    but the button is assigned to the same binding and interaction as actionA.

    Now whenever I trigger actionB, actionA triggers right after B.

    Now I have in my game's logic a way to bypass this,
    but I don't want to manage in the game code state's on if an action performed event should do it's logic or not.

    Is there a way using only the InputSystem to not cause actionA to perform right after actionB?

    Thanks!
     
  2. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    179
    One way I have gotten around this issue is to define a custom composite like ButtonWithOneModifier, but with a bool that negates the modifier when set to true. With this setup, you can use that composite for both actions, so that one action can produce a non-zero value only when the modifier is pressed, and the other only when the modifier is not pressed. Here's a link for defining custom composites: https://docs.unity3d.com/Packages/c...14044167.1563642269#writing-custom-composites
     
  3. Mman1235

    Mman1235

    Joined:
    Aug 23, 2015
    Posts:
    2
    Thanks for the suggestion!

    Though i'm still contemplating if this would be the best course of action.
     
  4. nickleplated

    nickleplated

    Joined:
    Nov 2, 2018
    Posts:
    26
    What I ended up doing was to override the Keyboard layout and add a new inverted synthetic control that returns 1 if no modifiers are pressed and 0 if any of them are. Then I can just use the standard "Button with One Modifier".

    When I don't want a modifier pressed, I set the Modifier path to "<Keyboard>/noModifier".

    It's only set up to work with the keyboard modifiers, but it's good enough for me until an official solution comes out.

    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using UnityEditor;
    3. #endif
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6. using UnityEngine.InputSystem.Controls;
    7. using UnityEngine.InputSystem.Layouts;
    8. using UnityEngine.InputSystem.LowLevel;
    9.  
    10. #if UNITY_EDITOR
    11. [InitializeOnLoad] // Make sure static constructor is called during startup.
    12. #endif
    13. [InputControlLayout(stateType = typeof(KeyboardState), isGenericTypeOfDevice = true)]
    14. [UnityEngine.Scripting.Preserve]
    15. public class KeyboardEx : Keyboard
    16. {
    17.     /// <summary>
    18.     /// A Button control that returns the opposite of the pressed state.
    19.     /// If the button is pressed, returns 0.0 and if not pressed, returns 1.0.
    20.     /// </remarks>
    21.     [UnityEngine.Scripting.Preserve]
    22.     public class InvertButtonControl : ButtonControl
    23.     {
    24.         ////TODO: wasPressedThisFrame and wasReleasedThisFrame
    25.  
    26.         public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
    27.         {
    28.             return base.ReadUnprocessedValueFromState(statePtr) > 0f ? 0f : 1f;
    29.         }
    30.     }
    31.  
    32.     /// <summary>
    33.     /// An artificial inverted combination of <see cref="leftShiftKey"/>, <see cref="rightShiftKey"/>,
    34.     /// <see cref="leftCtrlKey"/>, <see cref="rightCtrlKey"/>,
    35.     /// <see cref="leftAltKey"/>, and <see cref="rightAltKey"/> into one control.
    36.     /// </summary>
    37.     /// <value>Control representing a combined left and right shift, control, and alt key.</value>
    38.     /// <remarks>
    39.     /// This is a <see cref="InputControl.synthetic"/> button which is considered pressed whenever the left and
    40.     /// right shift, control, and alt keys are not pressed.
    41.     /// </remarks>
    42.     public InvertButtonControl noModifier { get; private set; }
    43.  
    44.     protected override void FinishSetup()
    45.     {
    46.         base.FinishSetup();
    47.  
    48.         noModifier = GetChildControl<InvertButtonControl>("noModifier");
    49.     }
    50.  
    51.     static KeyboardEx()
    52.     {
    53.         const string json = @"
    54.    {
    55.        ""name"" : ""KeyboardEx"",
    56.        ""extend"" : ""Keyboard"",
    57.        ""controls"" : [
    58.            {
    59.                ""name"" : ""noModifier"",
    60.                ""layout"" : ""DiscreteButton"",
    61.                ""usage"" : ""Modifier"",
    62.                ""offset"" : 0,
    63.                ""bit"" : 51,
    64.                ""sizeInBits"" : 8,
    65.                ""synthetic"" : true
    66.            }
    67.        ]
    68.    }";
    69.  
    70.         InputSystem.RegisterLayoutOverride(json);
    71.     }
    72.  
    73.     // In the Player, trigger the calling of our static constructor
    74.     // by having an empty method annotated with RuntimeInitializeOnLoadMethod.
    75.     [RuntimeInitializeOnLoadMethod]
    76.     private static void Init()
    77.     {
    78.     }
    79. }
    80.  
     
    fstorehaug likes this.
  5. Rene-Damm

    Rene-Damm

    Unity Technologies

    Joined:
    Sep 15, 2012
    Posts:
    1,550
    Yeah, unfortunately ATM it's down to workarounds. We have this on the list as a high priority item. The fact that actions will indiscriminately consume input instead of figuring out priority among them is a source of problems in a couple areas. Unfortunately, the needed change here won't happen for 1.0 but I'm sure it'll be one of the first things that'll get picked up after.
     
    bjornsyse and nickleplated like this.
  6. Ziboo

    Ziboo

    Joined:
    Aug 30, 2011
    Posts:
    355
    Any news on that issue ?
     
  7. Rene-Damm

    Rene-Damm

    Unity Technologies

    Joined:
    Sep 15, 2012
    Posts:
    1,550
    Nothing in the way of implementation yet but it'll get worked on soon.
     
  8. Why485

    Why485

    Joined:
    Jun 30, 2013
    Posts:
    41
    This has been an issue for me as well as my game has a lot of key combinations. For example F to do one action, and Shift+F to do the opposite action. I've temporarily worked around it with some messy input code to block input from one of the actions for a frame but this is quite ugly and not sustainable as the user needs to be able to create their own custom combination keybinds.

    Next time I'm working on input, I'll have a look through the workarounds posted here, but this is a very relevant issue for me.
     
  9. hipinds

    hipinds

    Joined:
    Aug 31, 2017
    Posts:
    2
    Is this fixed? I'm facing this issue atm
     
  10. rboerdijk

    rboerdijk

    Joined:
    Aug 4, 2018
    Posts:
    93
    Same here

    Using Input System 1.1.0 preview 2, I did find an "InvertProcessor" and to test setup one Action with ShiftKey+DeltaMouse and the Other with (Inverted-ShiftKey)+DeltaMouse.
    Both actions however trigger when pressing shift, so the "Invert" seems to be ignored (probably it only works inverting binding-values, not for inverting input-modifiers).
    InvertProcessor.png

    I also could not get the "<Keyboard>/noModifier" example to work. Added the example script into a .cs file in my project, but the "noModifier" keyboard mapping does not appear.
     
    Last edited: Nov 9, 2020
    bjornsyse and Zyphial like this.
  11. loganolson-dd

    loganolson-dd

    Joined:
    Sep 14, 2017
    Posts:
    4
  12. Zyphial

    Zyphial

    Joined:
    Dec 27, 2020
    Posts:
    2
    Yeah, it appears to be ignored. That's a real bummer and hopefully easier to fix than a total overhaul. I have a hard time envisioning logic that can detect if a modifier is down but not if a modifier isn't down && this invert processor flag is set.
     
  13. ColdHand

    ColdHand

    Joined:
    Apr 10, 2018
    Posts:
    6
    I also have this problem as well. I'm making a fighter game, and while I have no problem with Keyboard scheme because it doesn't use any modifier, The gamepad scheme uses the modifier to differentiate the normal attack and special attack by pressing the Left shoulder and its respective button. So any progress on this problem?
     
  14. loten

    loten

    Joined:
    Dec 10, 2017
    Posts:
    10
    High priority over a year now... @Rene-Damm any news?
     
    kyleyoungblom and rboerdijk like this.
  15. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    179
    Hello everybody! I have been working on a solution to this issue and I just got around to publishing my personal solution on github for everyone to use. You can set up combos with unlimited modifiers (each one being negatable). Here's the link: TRS6123/AginerianInput (github.com). Hope you all find this useful!
     
  16. josenajarqs

    josenajarqs

    Joined:
    Jan 27, 2021
    Posts:
    20
    Hi, Could you help me please? I know it has nothing to do with this, but do you know why this error happens?
     

    Attached Files:

  17. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    179
    josenajarqs likes this.
  18. josenajarqs

    josenajarqs

    Joined:
    Jan 27, 2021
    Posts:
    20
    thanks for your answer! what I want is to remmap my keys from the menu (in another scene) the code works fine for me when I test it with my player (not prefab) in the "Gameplay" scene, but when I drag my player (the prefab from the "Gameplay" scene) to the menu code, I get this error: Cannot switch to actions "menu" input is not enable. I have this code in the Menu

    Code (CSharp):
    1. [SerializeField] private InputActionReference jumpAction = null;
    2.         [SerializeField] private PlayerController playerController = null;
    3.         [SerializeField] private TMP_Text bindingDisplayNameText = null;
    4.         [SerializeField] private GameObject startRebindObject = null;
    5.         [SerializeField] private GameObject waitingForInputObject = null;
    6.  
    7.         private InputActionRebindingExtensions.RebindingOperation rebindingOperation;
    8.  
    9.         private const string RebindsKey = "rebinds";
    10.  
    11.  
    12.         public void StartRebinding()
    13.         {
    14.             startRebindObject.SetActive(false);
    15.             waitingForInputObject.SetActive(true);
    16.  
    17.             playerController.PlayerInput.SwitchCurrentActionMap("Menu");
    18.  
    19.             rebindingOperation = jumpAction.action.PerformInteractiveRebinding()
    20.                 .WithControlsExcluding("Mouse")
    21.                 .OnMatchWaitForAnother(0.1f)
    22.                 .OnComplete(operation => RebindComplete())
    23.                 .Start();
    24.         }
    25.  
    26.         private void RebindComplete()
    27.         {
    28.             int bindingIndex = jumpAction.action.GetBindingIndexForControl(jumpAction.action.controls[0]);
    29.  
    30.             bindingDisplayNameText.text = InputControlPath.ToHumanReadableString(
    31.                 jumpAction.action.bindings[bindingIndex].effectivePath,
    32.                 InputControlPath.HumanReadableStringOptions.OmitDevice);
    33.  
    34.             rebindingOperation.Dispose();
    35.  
    36.             startRebindObject.SetActive(true);
    37.             waitingForInputObject.SetActive(false);
    38.  
    39.             playerController.PlayerInput.SwitchCurrentActionMap("Gameplay");
    40.         }
    41.     }
    42. }
    And this is in the PlayerController
    Code (CSharp):
    1.   [SerializeField] private PlayerInput playerInput = null;
    2.         [SerializeField] private CharacterController controller = null;
    3.  
     
  19. josenajarqs

    josenajarqs

    Joined:
    Jan 27, 2021
    Posts:
    20
    everything works fine, i use the same menu script in the scene where the game starts and drag my ObjectPlayer to the script, remap my keys and everything works fine, but in the menu scene when trying to drag my PlayerPrefab to the script and try remapping the keys the same error appears, Cannot Switch action "menu" is not enabled
     
  20. Boji

    Boji

    Joined:
    Jul 10, 2012
    Posts:
    41
    =))))))))))

    Maybe you need one more year to work on it...
     
  21. bjornsyse

    bjornsyse

    Joined:
    Mar 28, 2017
    Posts:
    51
    Fantastic, thank you for your effort!

    Hi, would this work with Keyboard composites as well? Like Up/Down-arrow composite + a negated modifier key?
     
  22. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    179
    @bjornsyse Thanks for your appreciation! As for your question, if you duplicate the axis binding, set one binding to the Up Arrow, and set the other binding to the Down Arrow with the Invert Processor, you should get the desired result.
     
  23. bjornsyse

    bjornsyse

    Joined:
    Mar 28, 2017
    Posts:
    51
    Hi

    Hmm, I never thought about like that. I though we had to engage the positive/negative variables and do the calculation like in the other composites (AxisComposite) for example. Have you tried your suggestion? I couldn't get it to work over here. Might be missing something.

     
  24. bjornsyse

    bjornsyse

    Joined:
    Mar 28, 2017
    Posts:
    51
    Hi again. In the meantime, I ended up using the script provided by @_s_e_r_ in this thread (
    AxisModifierComposite 
    ) but modified it by 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;
    2.    
    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);
    8.        
    9.         if (!invertModifier)
    10.         {
    11.             if (context.ReadValueAsButton(modifier))
    12.                 return base.ReadValue(ref context);
    13.             return default;
    14.         }
    15.  
    16.         if (!context.ReadValueAsButton(modifier))
    17.             return base.ReadValue(ref context);
    18.         return default;
    19.     }
    20.    
    Works well for me.


     
  25. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    179
    @bjornsyse Admittedly I didn't try my suggestion until today and I see that the inverted axis binding won't work as expected. I just submitted changes to the git repo to fix the issue. It now works well on my end.
     
    bjornsyse likes this.
  26. Saucyminator

    Saucyminator

    Joined:
    Nov 23, 2015
    Posts:
    45
    Just hit this issue as well. Hopefully there's a planned solution (or workaround).
     
unityunity