How to execute Button once (when pressed only) using New Input System?

i just learn New Input System and i found some trouble.
Here when i try press and hold " I " the input executed more than once and make my inventory UI open repeatedly… how to handle this ? i have try some ways but still not working.

void Update()
{
        if (inputManager.InventoryInput)
        {
            if (isInventoryOpen)
            {
                isInventoryOpen = false;
            }
            else
            {
                isInventoryOpen = true;
            }
        }
        Debug.Log("Inventory status" + isInventoryOpen);
}

private void FixedUpdate() 
    {
        //When isInventoryOpen = true
        if (isInventoryOpen)
        {
            inventoryInterface.SetActive(true);
            Debug.Log("Open Inventory");
            return;
        }

        //When All Status False
        inventoryInterface.SetActive(false);
    }

You will need to show us the code that actually handles the input.

this one ?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class InputManager : MonoBehaviour
{
    [Header("Input Action Asset")]
    [SerializeField] private InputActionAsset playerControls;

    [Header("Action Map Name Preference")]
    [SerializeField] private string actionMapName = "Player";

    [Header("Action Name Preference")]
    [SerializeField] private string move = "Move";
    [SerializeField] private string run = "Run";
    [SerializeField] private string croch = "Croch";
    [SerializeField] private string interact = "Interact";
    [SerializeField] private string map = "Map";
    [SerializeField] private string inventory = "Inventory";
    [SerializeField] private string questLog = "QuestLog";
    [SerializeField] private string cameraChange = "CameraChange";
    [SerializeField] private string pause = "Pause";

    private InputAction moveAction;
    private InputAction runAction;
    private InputAction crochAction;
    private InputAction interactAction;
    private InputAction mapAction;
    private InputAction inventoryAction;
    private InputAction questLogAction;
    private InputAction cameraChangeAction;
    private InputAction pauseAction;

    public Vector2 MoveInput { get; private set; }
    public bool RunInput { get; private set; }
    public bool CrochInput { get; private set; }
    public bool InteractInput { get; private set; }
    public bool MapInput { get; private set; }
    public bool InventoryInput { get; private set; }
    public bool QuestLogInput { get; private set; }
    public float CameraChangeInput { get; private set; }
    public bool PauseInput { get; private set; }

    public static InputManager Instance { get; private set; }

    private void Awake() {

        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }

        moveAction = playerControls.FindActionMap(actionMapName).FindAction(move);
        runAction = playerControls.FindActionMap(actionMapName).FindAction(run);
        crochAction = playerControls.FindActionMap(actionMapName).FindAction(croch);
        interactAction = playerControls.FindActionMap(actionMapName).FindAction(interact);
        mapAction = playerControls.FindActionMap(actionMapName).FindAction(map);
        inventoryAction = playerControls.FindActionMap(actionMapName).FindAction(inventory);
        questLogAction = playerControls.FindActionMap(actionMapName).FindAction(questLog);
        cameraChangeAction = playerControls.FindActionMap(actionMapName).FindAction(cameraChange);
        pauseAction = playerControls.FindActionMap(actionMapName).FindAction(pause);

        RegisterInputAction();
    }

    void RegisterInputAction()
    {
        moveAction.performed += context => MoveInput = context.ReadValue<Vector2>();
        moveAction.canceled += context => MoveInput = Vector2.zero; 

        runAction.performed += context => RunInput = true;
        runAction.canceled += context => RunInput = false;

        crochAction.performed += context => CrochInput = true;
        crochAction.canceled += context => CrochInput = false;

        interactAction.performed += context => InteractInput = true;
        interactAction.canceled += context => InteractInput = false;

        mapAction.performed += context => MapInput = true;
        mapAction.canceled += context => MapInput = false;

        inventoryAction.performed += context => InventoryInput = true;
        inventoryAction.canceled += context => InventoryInput = false;

        questLogAction.performed += context => QuestLogInput = true;
        questLogAction.canceled += context => QuestLogInput = false;

        cameraChangeAction.performed += context => CameraChangeInput = context.ReadValue<float>();
        cameraChangeAction.canceled += context => CameraChangeInput = context.ReadValue<float>();

        pauseAction.performed += context => PauseInput = true;
        pauseAction.canceled += context => PauseInput = false;
    }

    private void OnEnable() {
        moveAction.Enable();
        runAction.Enable();
        crochAction.Enable();
        interactAction.Enable();
        mapAction.Enable();
        inventoryAction.Enable();
        questLogAction.Enable();
        cameraChangeAction.Enable();
        pauseAction.Enable();
    }

    private void OnDisable() {
        moveAction.Disable();
        runAction.Disable();
        crochAction.Disable();
        interactAction.Disable();
        mapAction.Disable();
        inventoryAction.Disable();
        questLogAction.Disable();
        cameraChangeAction.Disable();
        pauseAction.Disable();
    }
}

InputActions have a .WasPressedThisFrame() method: Class InputAction | Input System | 1.10.0

You can perhaps have boolean properties to expose this method, or just expose the InputAction’s for simplicity.

im sorry, where should i put that method ?
i put it on my if-else statment and it became error, i try put it on my input handler then it also error

mind if i get some example ?
Thanks

I’m saying expose a way to check for that in your InputManager class.

Such as just having a property in it:

public bool InventoryInputWasPressedThisFrame => inventoryAction?.WasPressedThisFrame() ?? false;

You’ve already got a central place for inputs. You can just add more functionality as needed.

1 Like

oh i see… thanks, i got it now

1 Like

Your performed/cancelled callbacks all just turn Booleans true/false, so it is clear that the Booleans like InputManager.InventoryInput will remain true while the button is held down. Using booleans and if/else for one-off actions is not really a good idea because of that. Instead, either directly hook up new callbacks to performed and/or cancelled for button actions that must be one-offs from a button press, or have the InputManager provide public events like public event Action InventoryPressed; as proxies for the buttons being pressed or released. These created events can be registered to trigger once with private void PressInventory(InputAction.CallbackContext ctx) => InventoryPressed?.Invoke(); and inventoryAction.performed += PressInventory; in Awake or Start in InputManager (and unsubscribe in OnDestroy). Code that needs to do something based on the inventory button being pressed would then be subscribed to in Awake/Start, like inputManager.InventoryPressed += ToggleInventory; (unsubscribe in OnDestroy). In this case, judging by the code, you could most easily write this:

private void ToggleInventory()
{
    isInventoryOpen ^= true;
    // if you wanted to, you could also set the active state of the inventory display here
    // inventoryInterface.SetActive(isInventoryOpen);
}
1 Like

Look simple, i will try this to, thanks for your suggestions