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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

button trigger behaviour: "release only" triggers twice!?

Discussion in 'Input System' started by FeastSC2, Jul 8, 2019.

  1. Natzely

    Natzely

    Joined:
    Sep 9, 2017
    Posts:
    3
    Debug pictures of a Release Only event. The value zeros at when it's performed which is when the release event happens. So you would have to read the event at started event and use it during the performed event if you want the action to trigger when the user releases the button instead of when they press it.
     

    Attached Files:

  2. KwahuNashoba

    KwahuNashoba

    Joined:
    Mar 30, 2015
    Posts:
    110
    Let me join the party, this might be useful to someone :)

    I've also had this kind of issue, but with value-typed action. When I hit key on keyboard for brief period it triggers single time but if I hold it a bit longer, it triggers twice. What worked for me was to set Action Type to Pass Through but on Action itself, not the underlying bindings/composites. I left all the Interactions blank.

    Then I check for the input like this:

    Code (CSharp):
    1. public void OnMove(InputAction.CallbackContext context)
    2. {
    3.     if (context.action.phase == InputActionPhase.Performed)
    4.     {
    5.         ProcessMoveInput(context.action.ReadValue<Vector2>());
    6.     }
    7. }
     
  3. DemTsag

    DemTsag

    Joined:
    Nov 21, 2017
    Posts:
    1

    This worked for me. I have been trying to make this work 3 days now. Thank you so much!
     
  4. latrodektus

    latrodektus

    Joined:
    Nov 1, 2016
    Posts:
    13
    I've done this and it's working, switching off Started and Canceled events should be an option in the Input System Package settings in the Project Settings.
     
  5. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    83
    Same problem still persisting in 2021.1.20f1.

    I just want to know when a button is pressed and when its being released within my handler function, not that hard of a task but its incredibly combersome to setup and tough to debug. If it wasn't for this thread, no way I would have found out why my events get fired twice, with the exact same action phase and triggered/started/performed values.

    If this is intended behaviour, its not at all well documented. It seems like when spawning PlayerInput prefabs through the PlayerInputManager, the Inputs get fired twice, once in the prefab instance, and once in the prefab - hence the valid scene check working, or alternatively you can check if the gameobject is acitve in the hierarchy (in case it will be always active this check seems like a bit less overhead, no idea what the performance of the valid scene check is):

    Code (CSharp):
    1.         public void HandleMyInput(CallbackContext context)
    2.         {
    3.             if (!IsInputAllowed) return;
    4.             // do your thing here
    5.         }
    6.  
    7.         private bool IsInputAllowed
    8.         {
    9.             get
    10.             {
    11.                 if (!gameObject.activeInHierarchy)
    12.                     return false;
    13.                 return true;
    14.             }
    15.         }

    For reference, this problem was discussed on stackoverflow too.
     
    Last edited: Dec 17, 2021
  6. LDMONTY007

    LDMONTY007

    Joined:
    Sep 7, 2019
    Posts:
    3
    Ok, if you read the unity manual on the input system it says if you just want to check if the button has been pressed only when it has been pressed (as in it won't continuously repeat like inputActionPhase.started does) all you have to do is use InputAction.Triggered. This gives you a boolean value, if the action (say a button) has been pressed it will output true, when it is released it switches back to false, this way it doesn't constantly trigger like InputAction.started it only triggers once when the button is pressed.
    Code (CSharp):
    1. if (InputAction.triggered)
    2.             {
    3.                 print("TRIGGERED");
    4.             }
     
  7. BillShackleton

    BillShackleton

    Joined:
    Mar 18, 2020
    Posts:
    1
    Thank You!
     
    Michal_Stangel likes this.
  8. ShokWayve

    ShokWayve

    Joined:
    Jan 16, 2013
    Posts:
    130
    I have also found a solution. Use the context to check the control.ispressed() function as below:

    Code (CSharp):
    1. if(context.control.IsPressed() == true)
    2.         {
    3.             Debug.Log("Left Button Clicked Baby!!");
    4.             return;
    5.         }
    I hope this helps someone.
     
  9. abitowhit

    abitowhit

    Joined:
    Aug 9, 2020
    Posts:
    13
    I was able to fix using a boolean if it helps anyone.
    I have a "Challenge Help" canvas (book) with fwd/bkwd TMP buttons which would fire a fwd and bkwd script to change text and an image.
    My problem was that onClick would fire and advance to the next page then the release would advance yet one more page (i.e. p1 to p3).

    I created a local bool in the script called allowPress in the script and a public void AllowPress which toggled allowPress to true.
    On the button "onClick" I first call AllowPress followed by the actual fwd/bkwd methods.
    onclickfix.png

    In both the fwd and bkwd methods I then added an initial check of the local allowPress being true and immediately set it to false. The second onRelease does not fire now because local allowPress is now false.

    e.g.
    Code (CSharp):
    1. public void forward()
    2. {
    3.     if(allowPress)
    4.     {
    5.         allowPress=false;
    6.         if(currentPage < pageText.Length-1)
    7.         {
    8.             currentPage++;
    9.          }
    10.         else{
    11.             currentPage=0;
    12.         }
    13.         updatePage();
    14.     }
    15. }
     
  10. davidnibi

    davidnibi

    Joined:
    Dec 19, 2012
    Posts:
    424
    I got this working by setting the interact button (the one that usually is on off on off etc) to Press and Release in Interactions, and used this:

    Code (CSharp):
    1.     public void OnInteract(InputAction.CallbackContext context)
    2.     {
    3.         if (context.canceled)
    4.         {
    5.             interactPressed = false;
    6.         }
    7.         else
    8.         {
    9.             interactPressed = context.action.triggered;
    10.         }
    11.     }
    It works 100% for me.
     
  11. Hypertectonic

    Hypertectonic

    Joined:
    Dec 16, 2016
    Posts:
    75
    I discovered that even if trying to check for phase, the reason it still fired twice for me appears to be because I had two InputMaps with the exact same action name and binding, and PlayerInput.cs just throws you the event twice, once for each InputMap.

    Since I did want both input maps to be on, and was still using SendMessages instead of UnityEvents, I managed to prevent the double execution like this:
    Code (CSharp):
    1. public void OnYourAction(InputValue value)
    2. {
    3.     if (PlayerInput.actions.FindAction("YourAction").inProgress) return;
    4.  
    5.     // code here only executes once
    6. }
     
    Last edited: Aug 27, 2022
  12. AbraTabia

    AbraTabia

    Joined:
    Jul 21, 2019
    Posts:
    11
    Hey everyone. I'm not sure if this problem being described here has been fixed, I just was stuck on a similar issue but was able to fix it and in doing so understand how and why my trigger buttons were firing 2-4 times on press and then one more time on release. I was using an xbox controller and it was only doing it for the triggers, bumpers were fine using InputActionName.performed but the triggers were firing multiple times.

    If I used InputActionName.canceled instead it fires only once, like a "on Button Up", and InputActionName.started also fired only once as a "on button down" effect, so if you have this repeated firing happening to you unintentionally, to get only one action taken on the button I suggest using one of those 2 options and not performed. Performed will fire constantly, until it reaches maximum button down, and then one more time on release. I believe this is the intended effect for controller trigger buttons, like a machine gun shooting kind of thing,

    But basically the variation in people's reports of how many times it is firing instead of once, is in my opinion how fast they are clicking the button and if you hold a trigger button half way pressed you will see the performed event firing constantly. So that explains the triggers, and my thinking about the problem happening on other buttons (mine were all set to the defaults) is that your action might be acting like my xbox trigger buttons were today. I wish I knew how to explain that part better, "I think" that is a press interaction you would need to adjust but I haven't touched those (yet).

    If you still find trouble with this and need to code around it, you would set a bool to true on the started/performed method and only run that method when that is set to false (if (!flag) return;) and then in your canceled method, set that flag to true again. That would insure a 1 to 1 ratio of performed to canceled calls at least and ignore rapid fire.
     
  13. brian-nielsen

    brian-nielsen

    Joined:
    Apr 18, 2018
    Posts:
    15
    I suppose I'll throw in my workaround as well. Using SendMessages() my OnClick method is firing four times. Below fires when the button is released.

    My "solution"

    Code (CSharp):
    1.     public void OnClick(InputValue value)
    2.     {
    3.         //Value wrapper for New Input System. This will fire 4 times on a single click without this wrapper.        
    4.         Debug.Log("On click input value = " + value.Get<float>());
    5.         if (value.Get<float>() >= 0f && value.Get<float>() < 1f)
    6.         {
    7.             //do stuff
    8.         }
     
  14. Malorke

    Malorke

    Joined:
    May 28, 2019
    Posts:
    1
    So I just had the issue and it appears that for my PS4 controller there is two set of input events, one for the controller DualShock4GamepadHID then another one for XInputControllerWindows.

    That explain why even with an if (context.performed) it still runs two times in one frame.

    I don't know how to solve that yet.
     
  15. KnightWhoSaysNi

    KnightWhoSaysNi

    Joined:
    Jan 21, 2017
    Posts:
    10
    Using Unity 2022.2.01f and Quest 2 Activate action (triggerPressed)
    action.performed was firing just once for a while. Then it started firing twice. Then it went back to firing once for no reason at all.

    I debugged the code and saw that it was firing twice. Restarted Unity and my PC twice. The problem persisted for a while and I didn't even bother fixing it as I was working on some other areas.

    Then a day later, and after the last debugging in Visual Studio the event started firing just once. -.-
    I didn't do anything. The problem fixed itself...for now.

    [Update]
    It started happening again and this time the action.performed wasn't firing just twice but it starts off with 2 event calls, and then every time I stop and play again it increases the number of calls.

    The only thing that seems to fix this is disabling "Enter Play Mode Options" in Project Settings/Editor. Initially I had this turned on and had no problems, then the event started firing twice. Then it stopped firing twice for a short while. And now it fires 5 or 6 or more times with every button press.

    Since it is painful to work in Unity without EnterPlayModeOptions turned on, for now I'll have to just manually check for the extra event calls.

    [Second Update]
    On action.performed I was doing something with a game object in the scene. If disable Enter Play Mode Options that game object's reference gets lost somehow. In Awake of the script that has the action the reference is alive, but when I press the button the reference is missing.

    No idea what the hell is going on. Unity just randomly started acting out it seems (everything was working fine for a while, initially).

    [Final Update] :D
    I wasn't unsubscribing from the event + EnterPlayModeOptions = multiple calls to the event handler. So, no bugs, totally my fault. The only thing that still makes no sense is why it worked correctly initially.
     
    Last edited: Dec 29, 2022
    fegabe likes this.
  16. jawsome290

    jawsome290

    Joined:
    Jan 20, 2023
    Posts:
    3
    FIX: Setup your action like this
    upload_2023-2-9_18-56-24.png
    Then, within the function where your action is called add an if statement to check if Control == 1
    1==Fully pressed
    0==Not Pressed
    Like this
    upload_2023-2-9_18-58-50.png
     
  17. bagelbaker

    bagelbaker

    Joined:
    Jun 5, 2017
    Posts:
    67
    We have the same issue, the ps4 gamepad acts as 2 separate devices that provides input. The extra XInputControllerWindows doesn't appear with a ps5 (dualsense) gamepad. And only once device (XInputControllerWindows) when using an xbox gamepad. This bug occurs on Unity 2022.2.6f1 and input system 1.3.0 through 1.5.0. The bug is not there on Unity 2021.3.4f1 with input system 1.3.0 through 1.5.0.

    I created a bug report: IN-32056
     
  18. DecayedBomb

    DecayedBomb

    Joined:
    Feb 7, 2013
    Posts:
    1
    Set the Player Input Behavior to "Send Messages"
    Name function in your code "On+("ActionName")"
    Example: Action name Jump, name your function OnJump();
    and everything will work how it should, no extra linking required.
     
  19. LionelLyyn

    LionelLyyn

    Joined:
    Aug 3, 2022
    Posts:
    3
    Just want to add my two cents to the conversation.

    Because the fix was quite obvious.

    I found out that the action (started) was triggering twice. The issue is that I use the SendMessage behavior in my Player Input and the method name that subscribe to the player input action was the same name :

    For instance, if you have an action in your action map called ''Fire" and you subscribe to using this :

    Code (CSharp):
    1. playerInput.actions["Player/Fire"].started += _ => OnFire();
    2.  
    3. private void OnFire()
    4. {
    5.     PressOnFire?.Invoke();
    6. }
    7.  
    This will trigger twice because the player input will send message directly to the method OnFire() and also trigger the event from the event subscribtion. I don't know if what I said is clear enough.

    The solution is to use a different method name from the name of the action when you subscribe to your player input actions
     
  20. Deleted User

    Deleted User

    Guest

    To those who are dealing with multiple triggers and you have it set to react to context.performed and it still fires twice, check and see if your awake method or whatever is being triggered twice. In my case it was, because i was annoyed with having to unload certain scenes, I set it up to check if a scene was loaded and unload it in the editor using #if (UNITY_EDITOR) etc and then unload those scenes. Well it didn't unload them fast enough because it was firing on the unloading scene (then throwing a null exception on that scene) and then when I did load the scene because it was meant to load, it fired as normal. This ends up with two calls, one that's null ref and the other normal. Removing the call to pre-unload scenes fixed the duplicate calls for me. Just something to check if you have either a function you wrote or maybe a package that's causing the issue.
     
  21. gtaogle

    gtaogle

    Joined:
    Jan 6, 2022
    Posts:
    18
    This also happens with UI Elements for click events on buttons. I think this is not a bug in the input system but in the events system - somehow there are cases where the event gets attached to multiple objects and somehow these other events do not get removed properly, still sending messages to objects that are no longer in the scene but, perhaps paradoxically, still not garbage-collected because they are associated with an event? I think the argument might be that this just how C# events work, but in the case of input system events you're not directly subscribing so you would have to go out of your way to check to make sure things you don't want are in the list of subscribers to an event. I can't say for sure, but it's also possible there is currently a memory leak associated with this.

    I thought the solution might be to always bind your events to singleton objects in the scene, such as a player controller object with a script, or a scene control object with a script. But I still get double events sometimes. If I'm not wrong (and I could be;) my solution isn't a solution because on scene switch you would accrue extra event subscribers anyway.

    The solution of checking the game object for being valid works UNLESS you're using anonymous functions to handle the events like so:


    Code (CSharp):
    1.     public EventCallback<ClickEvent> getCallbackFor(ButtonType t) {
    2.         switch(t) {
    3.             case ButtonType.Continue:
    4.                 return a => { WorldUI.Dialog.callbackWithEvent(this.gameObject.name, "Continue", this, a); };
    5.         }
    6.         return null;
    7.     }
    In this case, this.gameObject isn't the object receiving the event, but this object setting up the event, which would be valid when the event was being subscribed to (in theory!)

    I am partly skeptical of my above analysis however, because I do this

    Code (CSharp):
    1.         void applyDialogButton(DialogButtons d, DialogTemplate t) {
    2.             switch(d) {
    3.                 case DialogButtons.Continue:
    4.                     if(t.getCallbackFor(d) is EventCallback<ClickEvent> e) {      
    5.                         if(lastContinueEvent!=null)                 {
    6.                             continueButton.UnregisterCallback<ClickEvent>(lastContinueEvent);
    7.                         }
    8.                         lastContinueEvent = e;
    9.                         continueButton.RegisterCallback<ClickEvent>(e);                      
    10.                     }
    11.                     break;
    12.             }
    13.         }
    Which should remove the old subscriber - if I don't do this, the first dialog I create is the only subscriber delegate that gets called - this thing only calls one function! So perhaps it is somewhere else, on the layer above where input events get moved into C# events that is holding onto invalid objects? Unsure. But today I have to resolve this double input issue, and I want to avoid doing it without requiring the use of a semaphore; as mentioned above, this workaround is effective, but rather verbose.

    Code (CSharp):
    1.         if(context.control.IsPressed() && InputControl.limitAction("togglePlayer",true)) {
    2.             // switch controlled entity          
    3.             if (entityControls.nextControllable() is GameEntity e)
    4.             {
    5.                 controlEntity(e);
    6.             }
    7.         } else {
    8.             InputControl.limitAction("togglePlayer", false);
    9.         }
    limitAction just keeps a dictionary of semaphores and returns true only if the semaphore (it's a boolean, folks) is false or not set. If true, it returns false. Turning it off allows the action to be done again. There's always a risk here that the semaphore gets turned on and can't be turned off normally because some event comes between pressing the button here and releasing it(!) -- I'd rather not have to tangle with figuring out what semaphores to clear.

    EDIT:

    It appears that the click events are not responsible for this, but an event I set up based on an input system event prior to the dialog (something to happen after it closes.) Because it's triggered by input system events, it triggers twice and puts two events on the stack. I can confirm the issue is with the input system double-firing because if I lock the behavior in the function the input system calls behind a semaphore, it only succeeds once. To me this is basically double-managing the input system, I prefer to use the action locking system I built for more abstract cases (i.e. preventing you from jumping while already jumping etc)

    Anyway, even checking to see if the game object is valid / in the active hierarchy didn't work in this case. It's disappointing but it doesn't seem to affect people universally; perhaps the switching of action maps (for example) induces this unexpected behavior.
     
    Last edited: Jul 12, 2023