Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

New Input System : How to use the "Hold" interaction.

Discussion in 'Input System' started by Xynhay, Dec 31, 2018.

  1. Xynhay

    Xynhay

    Joined:
    Jul 8, 2017
    Posts:
    21
    Hello ,

    I'm trying to use the new input system for the first time. It looks really nice.
    I'm trying to create a simple input system to control the movement of the player.
    I used a "composite dpad" to get a Vector2 that gives me a movement direction according to the pressed buttons (up down left right arrow).
    I works, but I need to spam the buttons to make my player move... My move function is called only when a press an arrow, and not continuously when I hold it.
    Here's my Awake function.
    Code (CSharp):
    1.     void Awake()
    2.     {
    3.         controls.Player.Movement.performed += mov => Move(mov.ReadValue<Vector2>());
    4.     }
    So,logically, I tried to switch the Interaction from "Press" to "Hold"....
    But it doesn't work at all.
    The function is called only once after a random duration, or not at all...

    How I am supposed to use this Hold interaction ? Or how can I make my player move when the keys are holded, without writing a long code to do that with the current "Press" interaction that is a one-call ?

    Thanks you.
     
  2. AmirLevi

    AmirLevi

    Joined:
    Dec 5, 2012
    Posts:
    12
    I have the same problem.
    for my understanding the performed action is called when the key press is 'done',
    but there is a repeat check mark that should make the action work again and again until the key is up,
    and it doesn't seem to work.
    it look likes a bug to me....
     
  3. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    I hope it wasn't random :) If it was, there's a bug.

    A "hold" is currently defined as a control moving out of its default state (e.g. a button being pressed) for at least a minimum amount of time. The time can be set on each "Hold" that's been added (there should be "Duration" field showing up under the interaction list). If it's set to 0 (the default), then the global default value is used. This can be configured on a per-project basis in the input settings ("Default Hold Time" in "Edit >> Project Settings... >> Input (NEW)").

    Put simply, you should see "started" getting called as soon as a button goes down, "performed" being called when the button is held for longer than the set duration, and "cancelled" being called if it's released before.

    BTW the set of interactions we have is up for a consistency pass. What we have ATM has grown quite organically and it needs a good pruning and cleaning.

    Sorry about that. The "repeat" property on Hold isn't actually implemented ATM. Should have been commented out.
     
    Rouddem, dog_funtom, AlexDuff and 5 others like this.
  4. PixelLifetime

    PixelLifetime

    Joined:
    Mar 30, 2017
    Posts:
    90
    So basically right now the only way is to use `bool` or coroutine to move something continuously. Or is there a more elegant way? And I if I get it right - the `repeat` property on Hold should be doing the job in the future, correct?

    I really like the new system, keep up the good work ;)
     
  5. PixelLifetime

    PixelLifetime

    Joined:
    Mar 30, 2017
    Posts:
    90
    What I have ended up with:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3.  
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6. using UnityEngine.Events;
    7. using UnityEngine.Experimental.Input;
    8.  
    9. #if UNITY_EDITOR
    10. using UnityEditor;
    11. #endif
    12.  
    13. using TMPro;
    14.  
    15. public class PointerMovementController : MonoBehaviour
    16. {
    17.     [SerializeField] private MainControls _mainControls;
    18.  
    19.     [SerializeField] private float _movementSpeed = 2f;
    20.  
    21.     private RectTransform _rectTransform;
    22.  
    23.     public void Move(Vector2 delta)
    24.     {
    25.         this._rectTransform.anchoredPosition += delta * this._movementSpeed * Time.deltaTime;
    26.     }
    27.  
    28.     private IEnumerator MovementProcess(InputAction.CallbackContext callbackContext)
    29.     {
    30.         Vector2 delta = callbackContext.ReadValue<Vector2>();
    31.  
    32.         while (delta.sqrMagnitude > 0.01f)
    33.         {
    34.             this.Move(delta);
    35.  
    36.             yield return null;
    37.  
    38.             delta = callbackContext.ReadValue<Vector2>();
    39.         }
    40.     }
    41.  
    42.     private Coroutine _movementProcess;
    43.     public void StartMovement(InputAction.CallbackContext callbackContext)
    44.     {
    45.         if (this._movementProcess != null)
    46.             this.StopCoroutine(this._movementProcess);
    47.  
    48.         this._movementProcess = this.StartCoroutine(this.MovementProcess(callbackContext));
    49.     }
    50.  
    51.     //public void StopMovement(InputAction.CallbackContext callbackContext)
    52.     //{
    53.     //    if (this._movementProcess != null)
    54.     //        this.StopCoroutine(this._movementProcess);
    55.     //}
    56.  
    57.     private void Awake()
    58.     {
    59.         this._rectTransform = this.transform as RectTransform;
    60.  
    61.         this._mainControls.Pointer.Movement.performed += this.StartMovement;
    62.     }
    63.  
    64.     private void OnEnable()
    65.     {
    66.         this._mainControls.Enable();
    67.     }
    68.  
    69.     private void OnDisable()
    70.     {
    71.         this._mainControls.Disable();
    72.     }
    73.  
    74. #if UNITY_EDITOR
    75.     //protected override void OnDrawGizmos()
    76.     //{
    77.     //}
    78. #endif
    79. }
    80.  
    81. #if UNITY_EDITOR
    82. [CustomEditor(typeof(PointerMovementController))]
    83. [CanEditMultipleObjects]
    84. public class PointerMovementControllerEditor : Editor
    85. {
    86. #pragma warning disable 0219, 414
    87.     private PointerMovementController _sPointerMovementController;
    88. #pragma warning restore 0219, 414
    89.  
    90.     private void OnEnable()
    91.     {
    92.         this._sPointerMovementController = this.target as PointerMovementController;
    93.     }
    94.  
    95.     public override void OnInspectorGUI()
    96.     {
    97.         this.DrawDefaultInspector();
    98.     }
    99. }
    100. #endif
     
    soheil0082 likes this.
  6. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Yup, at the moment there's no support for running logic continuously over time while a control is actuated so yup, it'd be up to OnFixedUpdate/OnUpdate and coroutines.

    Not sure TBH. Need to take another look.

    One thing I've been thinking about is it could be great to build interval-style triggering into all the various interactions. Or at least wherever it makes sense. So, for example, you could put a "Press" on a button and then set it to trigger at a set interval for as long as the button is pressed. I think that could be quite useful and the API the interactions use already has all the functionality needed to do that.

    Passing the CallbackContext directly into a coroutine is problematic. It's meant to only be valid for the duration of the callback. The way you're using it, you should generally be fine but if anything about the actions changes (e.g. the devices they are using), you may end up getting invalid array indexes or reading out the wrong data. ATM CallbackContext doesn't catch this and warn about it. Probably should.

    You generally shouldn't need the coroutine, though, for what you're doing there. The action will trigger for every single value change so should be fine to just call Move() on every single callback.

    Glad to hear. And thanks :)

    I think we're missing lots of polish and a stack of fixes but overall, I think we're starting to have something that could work out well.
     
    emredesu, GrayedFox and PixelLifetime like this.
  7. Deleted User

    Deleted User

    Guest

    Code (CSharp):
    1.     public InputMaster input;
    2.     private Vector2 controls;
    3.  
    4.     private void OnEnable()
    5.     {
    6.         input.Enable();
    7.  
    8.         input.Player.Movement.started += UpdateMovement;
    9.         input.Player.Movement.performed += UpdateMovement;
    10.         input.Player.Movement.cancelled += UpdateMovement;
    11.     }
    12.  
    13.     private void UpdateMovement(InputAction.CallbackContext obj)
    14.     {
    15.         controls = obj.ReadValue<Vector2>();
    16.     }
    This works if interaction is set to Stick, but an attempt to use 2 Dpads at the same time results in an error
    "InvalidOperationException: Cannot go from 'Waiting' to 'Cancelled'; must be 'Started'
     
    Last edited by a moderator: Jan 18, 2019
  8. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    @uriel798 Could you post the .inputactions file that leads to the error?

    @MakabreGaming The coroutine / keep-updating thing kept bugging me since it's a very common problem to want to have an input that just keeps triggering for as long as a control is held regardless of whether the control changes value or not. So I've started experimenting with having a "continuous" mode for actions that can be enabled with a setting on the action. If enabled, the action will trigger every update regardless of whether there is input coming in or not.



    It's still entirely WIP but I think if this works out, it could make a number of setups a lot easier.
     
    LLZ_T, FireJojoBoy, synthc and 4 others like this.
  9. Deleted User

    Deleted User

    Guest

    @Rene-Damm drive.google.com/file/d/1fKQljRBF7ZxMr78zYMy_7-Q-eMyCe5xY/view?usp=sharing
    or
    Code (CSharp):
    1. {
    2.     "name": "InputMaster",
    3.     "maps": [
    4.         {
    5.             "name": "Player",
    6.             "id": "628cebea-eb93-4a4e-9b5e-71a64d519dbf",
    7.             "actions": [
    8.                 {
    9.                     "name": "Movement",
    10.                     "id": "da956973-b32b-4b1e-a327-8ffc58483ee4",
    11.                     "expectedControlLayout": "Dpad",
    12.                     "bindings": []
    13.                 },
    14.                 {
    15.                     "name": "Left Hand",
    16.                     "id": "67fda0e4-caaf-4ddc-91a3-a478d16cb236",
    17.                     "expectedControlLayout": "",
    18.                     "bindings": []
    19.                 },
    20.                 {
    21.                     "name": "Right Hand",
    22.                     "id": "147f0271-30ce-4255-a254-3930f94c8f35",
    23.                     "expectedControlLayout": "",
    24.                     "bindings": []
    25.                 },
    26.                 {
    27.                     "name": "HoldE2",
    28.                     "id": "c746c1a7-0b1b-4c7b-9803-0b04003ddabf",
    29.                     "expectedControlLayout": "",
    30.                     "bindings": []
    31.                 },
    32.                 {
    33.                     "name": "Tap",
    34.                     "id": "0b689adb-fb09-42f5-81c8-04aca339f1ca",
    35.                     "expectedControlLayout": "",
    36.                     "bindings": []
    37.                 }
    38.             ],
    39.             "bindings": [
    40.                 {
    41.                     "name": "WASD",
    42.                     "path": "Dpad",
    43.                     "interactions": "Stick",
    44.                     "processors": "",
    45.                     "groups": "",
    46.                     "action": "Movement",
    47.                     "chainWithPrevious": false,
    48.                     "isComposite": true,
    49.                     "isPartOfComposite": false,
    50.                     "modifiers": ""
    51.                 },
    52.                 {
    53.                     "name": "up",
    54.                     "path": "<Keyboard>/w",
    55.                     "interactions": "",
    56.                     "processors": "",
    57.                     "groups": "Keyboard and Mouse",
    58.                     "action": "Movement",
    59.                     "chainWithPrevious": false,
    60.                     "isComposite": false,
    61.                     "isPartOfComposite": true,
    62.                     "modifiers": ""
    63.                 },
    64.                 {
    65.                     "name": "down",
    66.                     "path": "<Keyboard>/s",
    67.                     "interactions": "",
    68.                     "processors": "",
    69.                     "groups": "Keyboard and Mouse",
    70.                     "action": "Movement",
    71.                     "chainWithPrevious": false,
    72.                     "isComposite": false,
    73.                     "isPartOfComposite": true,
    74.                     "modifiers": ""
    75.                 },
    76.                 {
    77.                     "name": "left",
    78.                     "path": "<Keyboard>/a",
    79.                     "interactions": "",
    80.                     "processors": "",
    81.                     "groups": "Keyboard and Mouse",
    82.                     "action": "Movement",
    83.                     "chainWithPrevious": false,
    84.                     "isComposite": false,
    85.                     "isPartOfComposite": true,
    86.                     "modifiers": ""
    87.                 },
    88.                 {
    89.                     "name": "right",
    90.                     "path": "<Keyboard>/d",
    91.                     "interactions": "",
    92.                     "processors": "",
    93.                     "groups": "Keyboard and Mouse",
    94.                     "action": "Movement",
    95.                     "chainWithPrevious": false,
    96.                     "isComposite": false,
    97.                     "isPartOfComposite": true,
    98.                     "modifiers": ""
    99.                 },
    100.                 {
    101.                     "name": "Arrows",
    102.                     "path": "Dpad",
    103.                     "interactions": "Stick",
    104.                     "processors": "",
    105.                     "groups": "",
    106.                     "action": "Movement",
    107.                     "chainWithPrevious": false,
    108.                     "isComposite": true,
    109.                     "isPartOfComposite": false,
    110.                     "modifiers": ""
    111.                 },
    112.                 {
    113.                     "name": "up",
    114.                     "path": "<Keyboard>/upArrow",
    115.                     "interactions": "",
    116.                     "processors": "",
    117.                     "groups": "Test;Keyboard and Mouse",
    118.                     "action": "Movement",
    119.                     "chainWithPrevious": false,
    120.                     "isComposite": false,
    121.                     "isPartOfComposite": true,
    122.                     "modifiers": ""
    123.                 },
    124.                 {
    125.                     "name": "down",
    126.                     "path": "<Keyboard>/downArrow",
    127.                     "interactions": "",
    128.                     "processors": "",
    129.                     "groups": "Test;Keyboard and Mouse",
    130.                     "action": "Movement",
    131.                     "chainWithPrevious": false,
    132.                     "isComposite": false,
    133.                     "isPartOfComposite": true,
    134.                     "modifiers": ""
    135.                 },
    136.                 {
    137.                     "name": "left",
    138.                     "path": "<Keyboard>/leftArrow",
    139.                     "interactions": "",
    140.                     "processors": "",
    141.                     "groups": "Test;Keyboard and Mouse",
    142.                     "action": "Movement",
    143.                     "chainWithPrevious": false,
    144.                     "isComposite": false,
    145.                     "isPartOfComposite": true,
    146.                     "modifiers": ""
    147.                 },
    148.                 {
    149.                     "name": "right",
    150.                     "path": "<Keyboard>/rightArrow",
    151.                     "interactions": "",
    152.                     "processors": "",
    153.                     "groups": "Test;Keyboard and Mouse",
    154.                     "action": "Movement",
    155.                     "chainWithPrevious": false,
    156.                     "isComposite": false,
    157.                     "isPartOfComposite": true,
    158.                     "modifiers": ""
    159.                 },
    160.                 {
    161.                     "name": "right",
    162.                     "path": "<Mouse>/leftButton",
    163.                     "interactions": "",
    164.                     "processors": "",
    165.                     "groups": "Keyboard and Mouse",
    166.                     "action": "Left Hand",
    167.                     "chainWithPrevious": false,
    168.                     "isComposite": false,
    169.                     "isPartOfComposite": false,
    170.                     "modifiers": ""
    171.                 },
    172.                 {
    173.                     "name": "right",
    174.                     "path": "<Keyboard>/e",
    175.                     "interactions": "Hold(duration=2)",
    176.                     "processors": "",
    177.                     "groups": "Keyboard and Mouse",
    178.                     "action": "HoldE2",
    179.                     "chainWithPrevious": false,
    180.                     "isComposite": false,
    181.                     "isPartOfComposite": false,
    182.                     "modifiers": ""
    183.                 },
    184.                 {
    185.                     "name": "right",
    186.                     "path": "<Mouse>/rightButton",
    187.                     "interactions": "",
    188.                     "processors": "",
    189.                     "groups": "Keyboard and Mouse",
    190.                     "action": "Right Hand",
    191.                     "chainWithPrevious": false,
    192.                     "isComposite": false,
    193.                     "isPartOfComposite": false,
    194.                     "modifiers": ""
    195.                 },
    196.                 {
    197.                     "name": "right",
    198.                     "path": "<Keyboard>/f",
    199.                     "interactions": "Tap",
    200.                     "processors": "",
    201.                     "groups": ";Keyboard and Mouse",
    202.                     "action": "Tap",
    203.                     "chainWithPrevious": false,
    204.                     "isComposite": false,
    205.                     "isPartOfComposite": false,
    206.                     "modifiers": ""
    207.                 }
    208.             ]
    209.         },
    210.         {
    211.             "name": "Menu",
    212.             "id": "a3819298-f2ff-43f9-9bf3-3eb27911a12f",
    213.             "actions": [],
    214.             "bindings": []
    215.         }
    216.     ],
    217.     "controlSchemes": [
    218.         {
    219.             "name": "Keyboard and Mouse",
    220.             "basedOn": "",
    221.             "bindingGroup": "Keyboard and Mouse",
    222.             "devices": [
    223.                 {
    224.                     "devicePath": "<Keyboard>",
    225.                     "isOptional": false,
    226.                     "isOR": false
    227.                 },
    228.                 {
    229.                     "devicePath": "<Mouse>",
    230.                     "isOptional": false,
    231.                     "isOR": false
    232.                 }
    233.             ]
    234.         }
    235.     ]
    236. }
     
    Last edited by a moderator: Jan 19, 2019
  10. douglassophies

    douglassophies

    Joined:
    Jun 17, 2012
    Posts:
    141
    There is not continuous button now so i guess there is a new way of doing this? Had a search but could not find anything. Thanks
     
  11. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    Any date on preview release of feature?
     
    GilbertoBitt likes this.
  12. GilbertoBitt

    GilbertoBitt

    Joined:
    May 27, 2013
    Posts:
    111
    this feature was removed on the last updated 0.9
     
  13. douglassophies

    douglassophies

    Joined:
    Jun 17, 2012
    Posts:
    141
    Thanks @GilbertoBitt. What is the alternative? We can have a toggle bool in Update() on button down/up but is there a neater way with the new system?
     
    GilbertoBitt likes this.
  14. GilbertoBitt

    GilbertoBitt

    Joined:
    May 27, 2013
    Posts:
    111
    i think so, i'm trying to understand the correct way after the update too
     
  15. Korindian

    Korindian

    Joined:
    Jun 25, 2013
    Posts:
    584
    With 0.9, there seem to be two ways of doing what used to be "Continuous" so we could detect if a button is held:

    1. As you mentioned, subscribing to events of Started and Canceled, and toggling a bool. For this to work, we can set the ActionType for the InputAction as Button with no interactions.

    2. Doing the input action as a Button with no interactions, casting the control as a ButtonControl, and then using the polling bools wasPressedThisFrame, isPressed, and wasReleasedThisFrame by doing like the following:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.InputSystem;
    3. using UnityEngine.InputSystem.Controls;
    4.  
    5. public class InputTest : MonoBehaviour
    6. {
    7.     // Assign input action asset here.
    8.     public InputActionAsset asset;
    9.  
    10.     private InputAction inputAction;
    11.     private ButtonControl buttonControl;
    12.  
    13.  
    14.     private void Awake()
    15.     {
    16.         inputAction = asset.FindAction("InputActionNameHere");
    17.  
    18.         // Getting the first binding of the input action using index of 0. If we had more bindings, we would use different indices.
    19.         buttonControl = (ButtonControl)inputAction.controls[0];
    20.  
    21.         inputAction.Enable();
    22.     }
    23.  
    24.  
    25.     private void Update()
    26.     {
    27.         if (buttonControl.wasPressedThisFrame)
    28.             Debug.Log("Was Just Pressed");
    29.  
    30.         if (buttonControl.isPressed)
    31.             Debug.Log("Is Currently Pressed");
    32.  
    33.         if (buttonControl.wasReleasedThisFrame)
    34.             Debug.Log("Was Just Released");
    35.     }
    36. }
     
    douglassophies likes this.
  16. douglassophies

    douglassophies

    Joined:
    Jun 17, 2012
    Posts:
    141
    @Rene-Damm What was the reason for removing continuous? The solutions are simple enough but so much less elegant for a feature that is presumably used often in games.
     
  17. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Yup, it definitely was a convenient thing. In the end, however, there were two reasons that spelled the end of "continuous".
    1. The cost in complexity and code footprint ended up being much more than initially anticipated. What seemed like just a little add-on behavior, turned out to have quirky side-effects on interactions and even just the core feature ended up having a much bigger footprint than expected.
    2. The new and much more versatile polling API for actions (InputAction.triggered and InputAction.ReadValue<T>) came along and made this kind of scenario even simpler to support.
    So given now you can instead simply do

    Code (CSharp):
    1. public void Update()
    2. {
    3.     var move = moveAction.ReadValue<Vector2>();
    4.     //...
    5. }
    6.  
    "continuous" turned out to no longer really carry its weight when we looked at it.
     
  18. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    There's actually a simpler way now.

    Code (CSharp):
    1. public void Update()
    2. {
    3.     if (inputAction.triggered)
    4.         /* ... */;
    5. }
    6.  
    The "triggered" property will only be true for the frame in which the action was performed and it'll be true regardless of how many times it was performed or even if it was cancelled after. It's basically the equivalent of "wasPressedThisFrame" except there's no need to grab individual controls and check them.
     
  19. Korindian

    Korindian

    Joined:
    Jun 25, 2013
    Posts:
    584
    I tried this with the various combinations of type and interactions for an InputAction, and could not get 'triggered' to return true for an equivalent of isPressed, or in the old system, GetButton (was not looking for wasPressedThisFrame or GetButtonDown).

    This is the reason I posted the #2 solution. Other than casting the control to a ButtonControl and using isPressed, is there an equivalent to polling for holding a key down every frame for an InputAction? "Continuous" served this purpose previously.

    In the old system, we can do Input.GetButton("Something") and this will return true for as long as the button bound to "Something" is held down.

    You have Keyboard.current.spaceKey.isPressed, but no equivalent isPressed for rebindable InputActions with a binding of spacebar unless cast to a ButtonControl, unless I'm mistaken?
     
  20. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Yup, that'd be expected. And yup, realize now that is actually different from what you were going for in your script.

    For an equivalent to isPressed, you can do

    Code (CSharp):
    1. public void Update()
    2. {
    3.     if (inputAction.ReadValue<float>() > 0)
    4.         /* ... */
    Or in case you want to explicitly check against the button press threshold

    Code (CSharp):
    1. public void Update()
    2. {
    3.     if (inputAction.ReadValue<float>() >= InputSystem.settings.defaultButtonPressPoint)
    4.         /* ... */
    We should probably have a method on InputAction that does this implicitly.
     
  21. Korindian

    Korindian

    Joined:
    Jun 25, 2013
    Posts:
    584
    Thanks, that's good to know. You may want to put all of this in the docs, especially in the "Migrate From Old Input System" under Input.GetButton.

    In the docs, can you explain what "the raw control without using bindings" means in the following statement of the Input.GetButton entry:
    "You can also use ButtonControl.isPressed to detect if a button was pressed on the raw control without using bindings."

    Are there any drawbacks to using the InputAction control as a ButtonControl? Is it better/faster to use inputAction.ReadValue<float>() > 0 rather than buttonControl.IsPressed?
     
    angrypenguin likes this.
  22. Graveyard-Studios

    Graveyard-Studios

    Joined:
    Apr 12, 2017
    Posts:
    1
    I'm having problems with "GetKeyDown" using the InputSystem since "performed" is not triggering functions in my code. The paleative solution that i found was:

    When you read the "phase" of the button, it returns "waiting" when the button is not pressed and "started" while it's pressed. I just compare
    Code (CSharp):
    1. if (action.phase == InputActionPhase.Started) {
    2.     // Do Something
    3. }
     
  23. PixelLifetime

    PixelLifetime

    Joined:
    Mar 30, 2017
    Posts:
    90
    So what is the point of different `interactions` and `processors` now if we don't have continuous? How can we use them with our own continuous implementation? Is it decided for sure and can we start writing wrappers? Is a wrapper based on a coroutine that starts and ends on `action.started` and `action.canceled` sufficient for handling this situation?
     
  24. PixelLifetime

    PixelLifetime

    Joined:
    Mar 30, 2017
    Posts:
    90
    Code (CSharp):
    1. [SerializeField] private CharacterController _characterController;
    2.  
    3. [SerializeField] private InputActionReference _inputActionMove;
    4.  
    5. private IEnumerator TestProcess<TValue>(InputAction inputAction, Action<TValue> action)
    6.     where TValue : struct
    7. {
    8.     while (true)
    9.     {
    10.         action.Invoke(inputAction.ReadValue<TValue>());
    11.  
    12.         yield return null;
    13.     }
    14. }
    15.  
    16. private Coroutine _testCoroutine;
    17.  
    18. public void Bind()
    19. {
    20.     this._inputActionMove.action.started += (callbackContext) =>
    21.     {
    22.         this._testCoroutine = this.StartCoroutine(
    23.             this.TestProcess<Vector2>(this._inputActionMove, (value) => this._characterController.Move(value.x))
    24.         );
    25.     };
    26.  
    27.     this._inputActionMove.action.performed += (callbackContext) =>
    28.     {
    29.     };
    30.  
    31.     this._inputActionMove.action.canceled += (callbackContext) =>
    32.     {
    33.         this.StopCoroutine(this._testCoroutine);
    34.     };
    35. }
    This is what I got in the end. It's working from the looks of it in the game. But I would like to move it out of coroutines to Update, thus
    1. Is there a possibility that we would be able to access internal update of Input System and subscribe to it? - because in the editor we can choose `Update Mode` when this update mode changes we would have to manually sync our update etc.
    upload_2019-9-24_20-10-28.png
    2. How can I make sure that value that I read using `ReadValue<>` meets all interactions requirements and goes through all the processing set in inspector? Are/Will there be methods that will allow us to do this `InputAction.MeetsInteractions(ReadValue<>) - returns a bool that indicates that all conditions are met` and `InputAction.Process(ReadValue<>) - returns a processed value.`.
    3. Somehow get a `Binding` that has invoked this action, to process stuff with these methods that are specific for `Binding`.
     
    Pilltech101 likes this.
  25. Pilltech101

    Pilltech101

    Joined:
    Jul 25, 2014
    Posts:
    35


    Bump
     
  26. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    I recommend using the polling API for this. I.e. in your Update (or FixedUpdate) method, doing this:

    Code (CSharp):
    1. _characterController.Move(_inputActionMove.action.ReadValue<Vector2>())
    There's InputSystem.onBeforeUpdate and InputSystem.onAfterUpdate which bracket each input system update.

    Interactions have no impact on values but rather only influence the phase progression (started, performed, canceled) of an action.

    Processors will get invoked automatically as part of reading a value.

    ATM the API does not expose the binding that triggered but rather only the control. This is probably something we should add. Internally the information is there. Just needs a translation into a proper public API.
     
  27. Dreamback

    Dreamback

    Joined:
    Jul 29, 2016
    Posts:
    220
    Are there any plans to bring it back or some equivalent? I thought not having to constantly poll for input was one of the advantages of the new Input system. For us, it means the rest of our game's input can all be done with PlayerInput/SendMessages, but for player movement, we have to do things differently to detect if the player is still holding a direction since OnMove(InputVal) is called just once per press.
     
    sand_lantern and Rallix like this.
  28. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Not ATM. We did find that quite a few users are missing the functionality so could be there's some revisiting after 1.0. But not sure.

    True. Point taken :)
     
    ModLunar, sarynth, synthc and 2 others like this.
  29. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    On the subject of the hold system: is it possible to read how much progress the action has to completion? I want the user to hold a button for 0.5s before an action is executed, but I also want to display the progress of this hold on screen. Is there a 0-1 float value that I can read that tells me this?
     
    Yecats likes this.
  30. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    There's no percentage value ATM. There's InputAction.CallbackContext.duration but to know how far along it is, you'll have to know the percentage value.

    Certainly seems like something that would make sense to have. I'll put it on the list to consider when revisiting parameters in general. ATM parameters are still pretty restricted. You can't dynamically change or even just access them. Which should definitely be possible. It's on the list of things to look at after 1.0 has shipped.
     
  31. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Do I understand correctly that there is no way to get a callback for key being held down? Continuous actions are really needed. I'm really surprised there is no way to do it that is consistent with the rest of interaction, specifically callbacks. I want to have a callback that keeps getting triggered while button is down.
     
    Tony_Max and DaneGillburry like this.
  32. Synpse

    Synpse

    Joined:
    Mar 19, 2018
    Posts:
    20
    I have spent 12 hours today looking for an answer to this... It's crazy that there's no way to check if a key is pressed continuously, just like Input.GetKey() does...
    If anyone finds a way to do this, please let me know!
     
  33. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    There is multiple ways actually :

    You can use a bool to track your key state. In the action map, set the interaction to Press/Press and release. When you get press, set your bool to true, when you get release, set it back to false. And now instead of doing
    Code (CSharp):
    1. if(Input.GetKey)
    simply do
    Code (CSharp):
    1. if(yourBool)
    Or if you can poll the action, using :
    Code (CSharp):
    1. [serializedfield] private InputActionReference myaction
    2.  
    3. myaction.action.ReadValue<yourType>()
     
  34. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Correct.

    There was support for continuous actions at some point. They may make a comeback in some form.

    Docs

    For actions, there's InputAction.ReadValue as described by @Jichaels.
     
    illinar likes this.
  35. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    If they were the same .performed callback before, I think it would make more sense to have a separate callback. inProgress or sth.

    How do I poll "GetKey" for a simple key action? Read value as well? What type? I tried bool, it's not working. (that would be a bad API anyway)

    How do I directly get "KeyUp" from an action??? I'm getting pretty frustrated with this API
     
    Last edited: Oct 18, 2019
  36. Synpse

    Synpse

    Joined:
    Mar 19, 2018
    Posts:
    20
    But the question here is how do you get press and release? How do you get them in code? It does not mention that in the documentation nor it mentions how the interaction system works...
     
    TP3 likes this.
  37. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    What I do is :


    myBool = Mathf.Approximately(ctx.ReadValue<float>(), 1);
     
  38. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    I thought getting a bool through ReadValue<bool> would be bad, now look at this beauty:
    Mathf.Approximately(_inputActions.BuildMode.CameraRotationMode.ReadValue<float>(), 1)


    (To be fair
    ReadValue<float>() > 0
    would do the trick but its still very messy especially if bound to analog input at some point)

    Having a float value on binary input..

    Is there a reason for not having sth like this, apart of it being difficult to come up with good property names?
    _inputActions.BuildMode.CameraRotationMode.inProgress;
     
    Last edited: Oct 18, 2019
  39. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    In my opinion a good consistent API would have 3 properties in action:

    triggered, continued, cancelled


    and 3 callbacks:

    onTriggered, onContinued, onCancelled


    ?right?

    Edit: continued is not a great naming because it doesn't sound like it returns true when the action is just triggered, and it should be doing that (like GetKey()).
     
    Last edited: Oct 18, 2019
    MassimoFrancesco, keanine and Synpse like this.
  40. I guess because it's not high priority. With your current set of tools you can easily simulate a key held down. You get a bool variable set to true on press down and set to false on release.
    This will do while they get to the point where they can iron out.a proper API for this.

    BTW, any "issue" can be seen "simple" from one use-case point of view, and it exponentially be more complex when you account for every single controller and button type and whatnot in existence.
     
  41. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    I think you can start with API and quick implementation of these basic features, and later make it more robust on the implementation side. As long as you now what API you want. I personally have no doubt about those three state properties and those three callbacks, the naming is the only issue.
     
  42. Yeah I know. By definition "basic features" always are the ones you care about.
     
  43. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Have the key been just pressed? Is a key still down? Have the key been released? Can I get callbacks when it happens?

    I can't imagine any scenario where those would be useful. Oh wait, I can. Every game ever. Besides callbacks, in what universe is this not the most basic input system functionality?

    Sorry for sarcastic tone.
     
    Last edited: Oct 19, 2019
    Dmitresso, monoganog, ThaiCat and 3 others like this.
  44. Synpse

    Synpse

    Joined:
    Mar 19, 2018
    Posts:
    20
    After a lot of trial and error and actually trying to understand the documentation I can say that it's actually not that hard to do what you're saying. Those basic things are still there they're just done a bit differently.

    You can refer to the InputAction you're trying to do and then doing:

    (Warning 1: Pseudo-Code)
    (Warning 2: This should be put on OnEnable(), Awake() or Start(). Don't ask me why, but if I had to guess I'd say they're using an Observer pattern. You can find more about it here)

    Code (CSharp):
    1. yourInputAction.started += ctx =>
    2. {
    3. // This is a GetKeyDown()
    4. // Your operations go here
    5. };
    6.  
    7. yourInputAction.canceled += ctx =>
    8. {
    9. // This is a GetKeyUp()
    10. // Your operations go here
    11. };
    If you want to simulate a GetKey(), you can put a bool as true inside of:
    Code (CSharp):
    1. yourInputAction.started
    and then changing that same bool to false inside of:
    Code (CSharp):
    1. yourInputAction.canceled
    You can then check if that bool is true on your Update() method.
     
    Last edited: Oct 19, 2019
    Pk0n likes this.
  45. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    Thanks, I know how to do all those thing, my problem is inconsistency and incompleteness of each of those approaches.

    Three basic things: GetKeyDown GetKey GetKeyUp

    I can't do all three of them with any of those approaches.

    Callbacks: There is no callback for GetKey. Simulating it requires 3-5 extra lines depending on approach. This also rules out the approach I was going to use where Input classes just hook up the input. Since I can't do that and my input classes must have an Update(), there is no reason to use callbacks at all.

    Properties. There is no property for GetKey. I can read a FLOAT value to see if my "W" key is down, and its rather messy. Wait there is another reason why I can't use just this approach there is no way to get GetKeyUp. So I actually have to use callbacks for those. So I have to have an Awake method and use callbacks just for that one thing.

    performed - callback
    triggered - property with the same meaning. So is action triggered or performed?
    cancelled - callback, no analogous property..

    Naming GetKey analog for actions is the only issue I see. Like..:

    triggered - GetKeyDown property
    onTriggered - GetKeyDown callback
    triggeredContinuously - GetKey property
    onTriggeredContinuously - GetKey callback
    cancelled - GetKeyUp property
    onCancelled - GetKeyUp callback

    That's pretty bad. I do think unfortunately that the same action must be able to act as continuous and as normal.
     
    Last edited: Oct 19, 2019
    monoganog, nglasl, NotaNaN and 6 others like this.
  46. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Er yes you can. I use new input and do similar stuff to Getkey. Read the docs!

    using UnityEngine.InputSystem;

    var kb = Keyboard.current;
    bool space = kb.spaceKey.isPressed;

    Pretty annoying TBH to be repeatedly told we can't when I already am, and it's clearly spelled out in the docs. I made an ultra trivial wrapper for it, less than half a page of code to do tap, up, down, hold etc...

    You aren't even trying really.
     
    illinar and Lurking-Ninja like this.
  47. Because you're confused about things. And you just confused me too above. Up until I read your new post I thought we were talking about _actions_. Since you were talking about performed and callbacks and whatnot.
    (I was ignoring the GetKey parts because I thought you're talking about the old Input mechanisms), my bad, sorry about that.

    If you really only need the key-presses, see Hippo's post. If you need the ongoing callback on actions, simulate it with the bool variable I was talking about until the proper continuous callback will be introduced.
     
  48. Korindian

    Korindian

    Joined:
    Jun 25, 2013
    Posts:
    584
    Also, if you're using Input Actions (instead of Keyboard.current), you can cast one to ButtonControl and have the same bool functionality in an Update as for GetKey:

    GetKeyDown/GetButtonDown becomes wasPressedThisFrame
    GetKey/GetButton becomes isPressed
    GetKeyUp/GetButtonUp becomes wasReleasedThisFrame

    See post #15 in this thread for example code.

    I kind of understand the frustration with it not being as simple as the old system in that it would be nice to have a "held" or "down" callback in addition to .started, .performed, and .canceled. GetKey was there previously as .performed with a "hold" interaction enabled until that was removed some versions ago.

    At least the functionality exists.
     
    ben4d85 likes this.
  49. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    I'm sorry I didn't specify in every post that I'm talking about actions. Edit: actually I reread my posts, I was obviously talking about actions.

    I'm seeing Actions as pretty much the only legit way to use the new input system, action maps specifically for anything except the simplest games.

    Because why would I do this:
    var kb = Keyboard.current;
    bool space = kb.spaceKey.isPressed;

    instead of this:
    Input.GetKey(KeyCode.Space);

    ?

    There might be reasons, but that's not the point. Let's not go on another tangent.
     
    Last edited: Oct 19, 2019
  50. illinar

    illinar

    Joined:
    Apr 6, 2011
    Posts:
    863
    So.. It doesn't. Talking about actions.
    ((ButtonControl)inputAction.controls[0]).isPressed
    Is of course not a valid API for action maps and there are better temporary solutions.

    I feel bad for what this thread have became but, let's see how the holy war have began:

    How did everyone get so confused and offended? I never said I don't know how to work around those things currently. I never said it's impossible to work around those limitations. I just pointed out the inconsistencies in API, shared my frustration and showed what I expected API to look like.
     
    Last edited: Oct 19, 2019