Using ButtonWithOneModifier without triggering another action using the same binding?

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!

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

Thanks for the suggestion!

Though i’m still contemplating if this would be the best course of action.

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 “/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.

#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;

#if UNITY_EDITOR
[InitializeOnLoad] // Make sure static constructor is called during startup.
#endif
[InputControlLayout(stateType = typeof(KeyboardState), isGenericTypeOfDevice = true)]
[UnityEngine.Scripting.Preserve]
public class KeyboardEx : Keyboard
{
    /// <summary>
    /// A Button control that returns the opposite of the pressed state.
    /// If the button is pressed, returns 0.0 and if not pressed, returns 1.0.
    /// </remarks>
    [UnityEngine.Scripting.Preserve]
    public class InvertButtonControl : ButtonControl
    {
        ////TODO: wasPressedThisFrame and wasReleasedThisFrame

        public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
        {
            return base.ReadUnprocessedValueFromState(statePtr) > 0f ? 0f : 1f;
        }
    }

    /// <summary>
    /// An artificial inverted combination of <see cref="leftShiftKey"/>, <see cref="rightShiftKey"/>,
    /// <see cref="leftCtrlKey"/>, <see cref="rightCtrlKey"/>,
    /// <see cref="leftAltKey"/>, and <see cref="rightAltKey"/> into one control.
    /// </summary>
    /// <value>Control representing a combined left and right shift, control, and alt key.</value>
    /// <remarks>
    /// This is a <see cref="InputControl.synthetic"/> button which is considered pressed whenever the left and
    /// right shift, control, and alt keys are not pressed.
    /// </remarks>
    public InvertButtonControl noModifier { get; private set; }

    protected override void FinishSetup()
    {
        base.FinishSetup();

        noModifier = GetChildControl<InvertButtonControl>("noModifier");
    }

    static KeyboardEx()
    {
        const string json = @"
    {
        ""name"" : ""KeyboardEx"",
        ""extend"" : ""Keyboard"",
        ""controls"" : [
            {
                ""name"" : ""noModifier"",
                ""layout"" : ""DiscreteButton"",
                ""usage"" : ""Modifier"",
                ""offset"" : 0,
                ""bit"" : 51,
                ""sizeInBits"" : 8,
                ""synthetic"" : true
            }
        ]
    }";

        InputSystem.RegisterLayoutOverride(json);
    }

    // In the Player, trigger the calling of our static constructor
    // by having an empty method annotated with RuntimeInitializeOnLoadMethod.
    [RuntimeInitializeOnLoadMethod]
    private static void Init()
    {
    }
}
1 Like

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.

3 Likes

Any news on that issue ?

Nothing in the way of implementation yet but it’ll get worked on soon.

1 Like

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.

2 Likes

Is this fixed? I’m facing this issue atm

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).

I also could not get the “/noModifier” example to work. Added the example script into a .cs file in my project, but the “noModifier” keyboard mapping does not appear.

3 Likes

+1

2 Likes

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.

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?

High priority over a year now… @[Rene-Damm]( Using ButtonWithOneModifier without triggering another action using the same binding? members/rene-damm.132762/) any news?

2 Likes

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!

3 Likes

Hi, Could you help me please? I know it has nothing to do with this, but do you know why this error happens?

6785972--785876--upload_2020-8-25_19-23-8.png

@josenajarqs Is the PlayerInput script enabled?

1 Like

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

[SerializeField] private InputActionReference jumpAction = null;
        [SerializeField] private PlayerController playerController = null;
        [SerializeField] private TMP_Text bindingDisplayNameText = null;
        [SerializeField] private GameObject startRebindObject = null;
        [SerializeField] private GameObject waitingForInputObject = null;

        private InputActionRebindingExtensions.RebindingOperation rebindingOperation;

        private const string RebindsKey = "rebinds";


        public void StartRebinding()
        {
            startRebindObject.SetActive(false);
            waitingForInputObject.SetActive(true);

            playerController.PlayerInput.SwitchCurrentActionMap("Menu");

            rebindingOperation = jumpAction.action.PerformInteractiveRebinding()
                .WithControlsExcluding("Mouse")
                .OnMatchWaitForAnother(0.1f)
                .OnComplete(operation => RebindComplete())
                .Start();
        }

        private void RebindComplete()
        {
            int bindingIndex = jumpAction.action.GetBindingIndexForControl(jumpAction.action.controls[0]);

            bindingDisplayNameText.text = InputControlPath.ToHumanReadableString(
                jumpAction.action.bindings[bindingIndex].effectivePath,
                InputControlPath.HumanReadableStringOptions.OmitDevice);

            rebindingOperation.Dispose();

            startRebindObject.SetActive(true);
            waitingForInputObject.SetActive(false);

            playerController.PlayerInput.SwitchCurrentActionMap("Gameplay");
        }
    }
}

And this is in the PlayerController

  [SerializeField] private PlayerInput playerInput = null;
        [SerializeField] private CharacterController controller = null;

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

=))))))))))

Maybe you need one more year to work on it…

1 Like