Search Unity

Event-Driven Input

Discussion in 'Input System' started by orionburcham, Apr 16, 2016.

  1. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Hi folks! Really excited about the new input direction.

    There's one thing that's jumping out in your code examples- your scripts are still detecting the input by polling for it in the Update loop.

    A superior approach might be allowing scripts to register event listeners with action maps, to receive a callback when the input occurs.

    I'm imagining this would look something like:
    Code (CSharp):
    1.  
    2. private void Awake()
    3. {
    4.     myActionMap.myAction.AddListener(OnMyAction);
    5. }
    6.  
    7. private void OnMyAction(MyAction action)
    8. {
    9.     // do stuff with the MyAction instance here, like...
    10.     if(action.IsUp)
    11.     {
    12.         StopAThingIWasDoing();
    13.     }
    14. }
    All action maps would be updated at the start of the frame, before any listeners were called. This would preserve Unity's current benefit that "all input is the same for the entire frame".

    So you could do things like this if you wanted:
    Code (CSharp):
    1.  
    2. private void OnMyAction(MyAction action)
    3. {
    4.     // here, we're checking the state of a totally different action from the action map
    5.     If(myActionMap.someOtherAction.IsDown)
    6.     {
    7.         DoAThing();
    8.     }
    9. }
    10.  
    And polling stuff in Update would also be intrinsically supported:

    Code (CSharp):
    1. private void Update()
    2. {
    3.     // this would be a polling approach to the first code example in this post.
    4.     if(myActionMap.myAction.IsUp)
    5.     {
    6.         StopAThingIWasDoing();
    7.     }
    8. }
    In my experience, registering listeners is often more performant and convenient than polling. Does the new input system allow for this type of thing?

    Thanks! Looking forward to more.
     
    Last edited: Apr 18, 2016
    Plasmajam likes this.
  2. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Just giving this a friendly bump. :) It'd be great to hear thoughts on this from any Unity folks, pro or con.

    Cheers!
     
  3. ScottyB

    ScottyB

    Joined:
    Apr 4, 2011
    Posts:
    146
    Below is a comment made by René Damm on the blog post here: http://blogs.unity3d.com/2016/04/12/developing-the-new-input-system-together-with-you/


    In the prototype you can hook yourself into the event tree (basically a tree of subscribers) and then you’ll get callbacks. The details of event distribution are still a bit up in the air as we’re trying to make something happens that works in a bit of a wider context than just input but I don’t think the fact that you can get notifications is going to change.

    Event processing, yes. Event gathering, not necessarily. We completely agree that frame-rate dependence in input is bad. And the old system was inherently tied to frame rate.

    What we want is to have event gathering (e.g. when we poll gamepads) to happen off the main thread where possible and where it makes sense. Where we already have properly timestamped events we can pick up from the OS instead of having to poll, that doesn’t make sense but where we can’t it definitely does. Doing it off the main thread will allow it to be run at higher frequency than frame rate and pick up events from polled devices with better granularity.
     
  4. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Yes, listeners for actions is in our plans!

    There is a question of whether to have individual delegates to subscribe to for isHeld, wasJustPressed, wasJustReleased (maybe even isNotHeld?). Probably if you just want callbacks for when a button is just pressed or released you wouldn't want to also get callbacks for every frame (or fixed frame) where it's down. But at other times you do want a callback for every frame where it's down (e.g. for applying force or similar as long as a button is held). So I'm leaning towards separate delegates rather than just one per action.
     
  5. CodeBison

    CodeBison

    Joined:
    Feb 1, 2014
    Posts:
    289
    If you're talking purely either/or - as in, you get either a single delegate per action or one for each thing that can happen related to the action - than I'd prefer separate delegates. But is there any reason we couldn't have both, so we can choose the model that works best for our use case?
     
  6. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Well, I guess the question for me is, when do you expect callbacks for the "everything" style delegate? If you get a callback every time a button is pressed, every time it's released, and also every frame where it's held down and every frame where it's not held down, then it's just equivalent to getting a callback every frame.

    If you exclude the callbacks for when the button is not held down from the "everything" callback, so it's just pressed + held down + just released, then it's more or less the same as just a callback for when it's held down (maybe you'd get it one frame longer when released but that's it).

    So I wonder if the combined delegate you're after is specifically for getting calls when the button has been just pressed or just released?
     
  7. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    488
    Sounds like a good solution might be to have the multiple callbacks per action, but to let users poll for the other states from inside those listeners, like:
    Code (CSharp):
    1. private void Awake()
    2. {
    3.     myActionMap.myAction.isUp += OnIsUp;
    4. }
    5.  
    6. private void OnIsUp()
    7. {
    8.     // checking different info about the same action
    9.     if(myActionMap.myAction.clickCount > 1)
    10.     {
    11.         DoRadThings();
    12.     }
    13. }
    This could give both side what they want, but it would mean that all of an action's data would need to be updated before any of the delegates were called that frame.

    (Btw- great news about the general answer!)
     
    Last edited: Apr 20, 2016
    runevision likes this.
  8. CodeBison

    CodeBison

    Joined:
    Feb 1, 2014
    Posts:
    289
    Yes, that sounds like the most sensible option to me. Any combined delegate would make the most sense if it alerted you of state changes only. Continuing to send you updates every frame would have limited value, and would be little different from polling.
     
  9. Zach-pocalypse

    Zach-pocalypse

    Joined:
    May 22, 2016
    Posts:
    1
    Why not just do it more like C# standard library events? Have a delegate for the specific kind of event handler, and then just pass all of the rest of the information (how long the button is held, etc), in the second parameter as a derivative of EventArgs.

    such as:
    Code (CSharp):
    1.     private void Awake()
    2.     {
    3.         Input.MouseDown += new MouseEventHandler(OnMouseDown(object sender, MouseEventArgs args));
    4.     }
    5.  
    6.     private void OnMouseDown(object sender, MouseEventArgs args)
    7.     {
    8.         if(args.button == "left")
    9.         {
    10.             print("Left button pressed!");
    11.         }
    12.  
    13.         if(args.held == true)
    14.         {
    15.             print("Being held down!");
    16.         }
    17.     }
    I don't understand why you would want to diverge from an already well-established event system.