Search Unity

Hold Interaction Only Triggers One Time

Discussion in 'Input System' started by Tiernan98, Jan 13, 2021.

  1. Tiernan98

    Tiernan98

    Joined:
    Jul 11, 2017
    Posts:
    42
    Hi,

    I am using the new Unity input system and my character has the ability to climb up on a ledge, and if the climb button is held they will do a cool move, but if it is cancelled they perform a normal climb up (just explaining why I am using a hold interaction).

    The problem is that it only fires one time and I can't work out why. Here is what my code looks like (irrelevant code omitted):
    Code (CSharp):
    1.  
    2. public partial class PlayerController : MonoBehaviour
    3. {
    4.     public class ClimbState : PlayerState
    5.     {
    6.         public LedgePoint Ledge => _ledge;
    7.  
    8.         private bool _isClimbingUp;
    9.         private bool _handstandRequested;
    10.         private bool _climbUpRequested;
    11.         private LedgePoint _ledge;
    12.        
    13.         public override void OnEnter()
    14.         {
    15.             _isClimbingUp = false;
    16.             _handstandRequested = false;
    17.             _climbUpRequested = false;
    18.  
    19.              _owner.UInput.OnClimbUp.performed += Handstand;
    20.              _owner.UInput.OnClimbUp.canceled += ClimbUp;
    21.  
    22.             // Stop character jerking out of walls
    23.             Physics.IgnoreLayerCollision(8, 9);
    24.         }
    25.  
    26.         public override void OnExit()
    27.         {
    28.             _owner.UInput.OnClimbUp.performed -= Handstand;
    29.             _owner.UInput.OnClimbUp.canceled -= ClimbUp;
    30.  
    31.             Physics.IgnoreLayerCollision(8, 9, false);
    32.         }
    33.  
    34.         public override void Update()
    35.         {
    36.             if (_isClimbingUp)
    37.             {
    38.                 if (_owner.AnimControl.IsIn("Idle"))
    39.                 {
    40.                     _owner.StateMachine.ChangeState(_owner.BaseStates.Locomotion);
    41.                 }
    42.                 else if (_owner.AnimControl.IsIn("CrouchIdle"))
    43.                 {
    44.                     _owner.StateMachine.ChangeState(_owner.BaseStates.Crouch);
    45.                 }
    46.             }
    47.             else if (_climbUpRequested)
    48.             {
    49.                 if (_owner.AnimControl.IsIn("HangIdle") && _ledge.CanClimbUp)
    50.                 {
    51.                     _owner.AnimControl.ClimbUp();
    52.                     _isClimbingUp = true;
    53.                 }
    54.                 else
    55.                 {
    56.                     _climbUpRequested = false;
    57.                 }
    58.             }
    59.             else if (_handstandRequested)
    60.             {
    61.                 if (_owner.AnimControl.IsIn("HangIdle") && _ledge.CanClimbUp)
    62.                 {
    63.                     _owner.AnimControl.ClimbUpCool();
    64.                     _isClimbingUp = true;
    65.                 }
    66.                 else
    67.                 {
    68.                     _handstandRequested = false;
    69.                 }
    70.             }
    71.             else if (_owner.UInput.OnCrouch.triggered)
    72.             {
    73.                 // Stop character getting stuck in wall
    74.                 _owner.SetPosition(_owner.transform.position - _owner.transform.forward * _owner.CharControl.radius);
    75.                 _owner.AnimControl.Fall();
    76.                 _owner.StateMachine.ChangeState(_owner.BaseStates.Air);
    77.             }
    78.         }
    79.  
    80.         private void ClimbUp(InputAction.CallbackContext ctx)
    81.         {
    82.             _climbUpRequested = true;
    83.         }
    84.  
    85.         private void Handstand(InputAction.CallbackContext ctx)
    86.         {
    87.             _handstandRequested = true;
    88.         }
    89.     }
    90. }
    91.  
    And the constructor of the UInput class looks like this, with other actions of course:
    Code (CSharp):
    1.  
    2. public UInput(PlayerInput input)
    3. {
    4.     OnClimbUp = input.actions.FindAction("ClimbUp", true);
    5. }
    6.  
    And here is my input asset:


    It always works the first time, but if I climb another ledge or the same ledge, it is impossible to climb up. Something is happening whenever I exit and re-enter the state. I tried commenting out setting _climbUpRequested to true and instead printed a debug statement. It fires no problem for as many times as I press the button without causing the character to climb.

    I thought maybe there might be an issue with unsubscribing and resubscribing to the action events. However, I tried commenting out the unsubscribe and only allowed it to subscribe once (with a temp bool) and again it only fires the first time.

    Anyone any ideas or is this a bug of some kind?
     
  2. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    I'll bump for you
     
    Tiernan98 likes this.
  3. Devil_Inside

    Devil_Inside

    Joined:
    Nov 19, 2012
    Posts:
    1,119
    Bumped into this issue as well.
    For me it works fine only when only one binding is available for the action, and that binding is either a mouse or a keyboard.
    If I add 2 bindings like in your screenshot (a keyboard and a gamepad), the action remains stuck at "performed" state and won't cancel if you release the button.
    This also happens for me if the only binding is a VR device button.

    Have you been able to find a solution?
     
  4. https://issuetracker.unity3d.com/is...binding-are-attached-to-the-same-input-action

    In the mean time, I have a workaround.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3.  
    4. public class Test : MonoBehaviour
    5. {
    6.     [SerializeField] private InputActionAsset inputActionAsset;
    7.     private InputAction _holdStuff;
    8.     private int _heldThisTimes;
    9.     private bool _hasMoreThanOneBindings = false;
    10.  
    11.     private void Awake()
    12.     {
    13.         _holdStuff = inputActionAsset.FindAction("HoldInteractionTest");
    14.         _hasMoreThanOneBindings = _holdStuff.bindings.Count > 1;
    15.         _holdStuff.started += Started;
    16.         _holdStuff.performed += Performed;
    17.         _holdStuff.canceled += Canceled;
    18.     }
    19.  
    20.     private void Started(InputAction.CallbackContext obj) => Debug.Log($"Pressed. ({_heldThisTimes++})");
    21.  
    22.     private void Performed(InputAction.CallbackContext obj)
    23.     {
    24.         Debug.Log($"SUCCESS! ({_heldThisTimes})");
    25.         if (_hasMoreThanOneBindings) Invoke(nameof(DoResetAction), 1f);
    26.     }
    27.  
    28.     private void Canceled(InputAction.CallbackContext obj) => Debug.Log($"Released. ({_heldThisTimes})");
    29.  
    30.     private void DoResetAction()
    31.     {
    32.         _holdStuff.Disable();
    33.         _holdStuff.Enable();
    34.     }
    35.  
    36.     private void OnEnable() => inputActionAsset.Enable();
    37. }
    38.  
    screenshot1.png
     
    Last edited by a moderator: May 11, 2021
    laurentlavigne likes this.