Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Mouse Delta Input

Discussion in 'New Input System' started by caseyc, Mar 18, 2019.

  1. caseyc

    caseyc

    Joined:
    Jul 2, 2012
    Posts:
    15
    I was checking out the new input system over the weekend, I'm a big fan so far of the features but I was wondering what the correct way to process the callback for mouse delta is.

    I've tried this:

    Code (CSharp):
    1.     private void Update()
    2.     {
    3.         transform.rotation = Quaternion.Euler(_rotation);
    4.     }
    5.  
    6.  
    7.     private void OnLook(InputAction.CallbackContext context)
    8.     {
    9.         Vector2 lookValue = context.ReadValue<Vector2>();
    10.  
    11.         _rotation.x += -lookValue.y * 0.022f * _sensitivity;
    12.         _rotation.y += lookValue.x * 0.022f * _sensitivity;
    13.     }
    And also this:

    Code (CSharp):
    1.     private void Update()
    2.     {
    3.         _rotation.x += -_lookValue.y * 0.022f * _sensitivity;
    4.         _rotation.y += _lookValue.x * 0.022f * _sensitivity;
    5.  
    6.         transform.rotation = Quaternion.Euler(_rotation);
    7.     }
    8.  
    9.  
    10.     private void OnLook(InputAction.CallbackContext context)
    11.     {
    12.         _lookValue = context.ReadValue<Vector2>();
    13.     }
    I have noticed that I receive several updates to the mouse delta per frame, so it seems like the first way is the correct manner to process the input. However, I was expecting this function to create a similar look speed to cs:go (0.022 is the constant m_yaw value in cs:go) and the second function seems to get closer to the expected result (although it is still a little faster). What units is the mouse delta returned in and what is the correct way to process it if I would want to create a similar sensitivity to source engine games? Thanks in advance for any help!
     
  2. Rene-Damm

    Rene-Damm

    Unity Technologies

    Joined:
    Sep 15, 2012
    Posts:
    686
    "delta" is in pixels, i.e. the amount of change on "position".

    Applying in Update() without taking time into account will essentially make rotation speeds dependent on framerate. To get constant speeds, scale by Time.deltaTime.
     
  3. caseyc

    caseyc

    Joined:
    Jul 2, 2012
    Posts:
    15
    For mouse movement wouldn't you want to take the raw change in pixels each frame though? I understand for a joystick you would want to get the direction the joystick is pointing and apply that by a rotation speed * deltaTime each frame but for a mouse you are measuring how far the user actually moved the mouse since the last update (or in this case the last time OnLook was called). For instance if the user was moving the mouse at at 10 inches/second on their mousepad and the mouse had a pixels per inch of 400 it would move 4000 pixels/second. If the framerate were 60 frames/sec it would be 4000/60 = 66.66 pixels per frame. If the framerate was 120 frames/sec it would be 4000/120 = 33.33 pixels per frame. In either case the overall speed of 4000 pixels/second is the same and not affected by the frame deltaTime. Unless I'm completely misunderstanding how this works lol. Thanks for you help!
     
    JimmyCushnie likes this.
  4. caseyc

    caseyc

    Joined:
    Jul 2, 2012
    Posts:
    15
    So I set up a quick test to see what was going on with the mouse delta being passed and now I'm even more confused than before.

    Code (CSharp):
    1.     private void Update()
    2.     {
    3.         Vector2 calculatedDelta = _mousePosition - _previousPosition;
    4.         _previousPosition = _mousePosition;
    5.  
    6.         if(_passedDelta != calculatedDelta)
    7.         {
    8.             Debug.Log("Passed Delta Mismatch: " + _passedDelta + ": " + calculatedDelta);
    9.         }
    10.  
    11.         if(_cumulativePassedDelta != calculatedDelta)
    12.         {
    13.             Debug.Log("Cumulative Passed Delta Mismatch: " + _cumulativePassedDelta + ": " + calculatedDelta);
    14.         }
    15.  
    16.         _cumulativePassedDelta = Vector2.zero;
    17.     }
    18.  
    19.  
    20.     private void OnLook(InputAction.CallbackContext context)
    21.     {
    22.         Vector2 delta = context.ReadValue<Vector2>();
    23.  
    24.         _passedDelta = delta;
    25.         _cumulativePassedDelta += delta;
    26.     }
    27.  
    28.     private void OnMousePositionChanged(InputAction.CallbackContext context)
    29.     {
    30.         _mousePosition = context.ReadValue<Vector2>();
    31.     }
    I basically set up a test where I calculate my own mouse delta manually using the mouse position, and compare it to the cumulative passed delta during a frame as well as the most recent passed delta in a frame. Neither the cumulative nor the raw passed delta seems to be accurate. The cumulative is way off, the passed one is closer but still off. What exactly is being passed with mouse delta?
     
  5. Rene-Damm

    Rene-Damm

    Unity Technologies

    Joined:
    Sep 15, 2012
    Posts:
    686
    There's some magic going on under the hood that makes things confusing for actions. Something that's still on the list to be looked at.

    In short, deltas will accumulate and reset automatically.

    Bear with me here... :)

    Let's say we get three deltas in a given frame. (1,1), (2,2), and (3,3). At the event level, this means that we get three events that have those exact deltas in them. At the state level (i.e. the memory that keeps the state of the mouse), however, we see accumulation. So after the *second* event the state is (3,3) and after the *third* event the state is (6,6). I.e. you get the accumulated mouse motion in the frame. And on the beginning of the next frame, it automatically resets to (0,0).

    This is done so that if you do Mouse.current.delta.ReadValue() in Update(), you don't just get the last delta of the last mouse event -- which could be (0,0) even though the frame actually did have mouse motion.

    However... for actions, this isn't what you'd expect. Actions observe every single state change. So what you expect is to get (1,1), (2,2) and (3,3) one after the other in the frame. What you actually get right now instead is (1,1),(3,3),(6,6). AND the action will observe the reset. I.e. you will *also* see (0,0) at the beginning of the next frame.

    This same problem crops up in other places, too, where something that does make sense for querying state in Update() or FixedUpdate() does not make sense for querying state in actions.

    What should probably happen is that Update() and FixedUpdate() see the accumulation happening but the actions do not. Something that still needs looking at.
     
  6. caseyc

    caseyc

    Joined:
    Jul 2, 2012
    Posts:
    15
    Thanks for the reply! I tried a similar test where I used Mouse.current.delta.ReadValue() in Update() but it was giving me the correct value with some framerates and not the correct one with others (i turned off vsync and was testing at about 2,000 fps vs capping to 30 to see if there was a difference). I tried playing with the input system update mode to correct this but it didn't seem to affect it at all. I suspect that the mouse delta isn't getting reset each frame but is being reset at some other point in time but I can't quite tell when. I'm on 2018.3.9 if that makes any difference. Thanks again!
     
  7. Elhimp

    Elhimp

    Joined:
    Jan 6, 2013
    Posts:
    20
    I'm not mathematician, but this look quite opposite to definition of delta. Anyway...

    So, is it gonna be benefitial to have some InputAction("name", "<Mouse>/delta")* with multiple callbacks per frame, which I should ignore completely? Or it vould be better to just "manually" calculate difference between previous and current cursor positions in scope of preffered update rate, or just read lates value from Mouse.current.delta?

    *considering I don't need to make composite action of multiple Vector2 sources.
     
  8. Wokky

    Wokky

    Joined:
    Apr 3, 2014
    Posts:
    5
    This seems similar to an issue I encountered after experimenting with converting a first-person character controller over to the new input system. Mouse input became jittery and felt out-of-sync with my mouse movements. After finding this thread I tried changing from using an action callback to
    Mouse.current.delta.ReadValue()
    but it doesn't appear to have resolved the problem.



    This toy example roughly illustrates what I'm observing - the position of the orange square is being offset in Update() via the old input system with
    Input.GetAxisRaw()
    while the blue is offset by
    Mouse.current.delta.ReadValue()


    Where the acceleration of the orange square generally matches that of the cursor, the blue square seems to unpredictably lag or jump ahead. The second half of the video also demonstrates its movement becoming out of sync: dragging the cursor from a point and eventually returning to that same point means that the orange square also returns to its initial position as expected, but the blue square comes to rest in a different location.
     
    Last edited: Jul 18, 2019
  9. Sonorpearl

    Sonorpearl

    Joined:
    Sep 8, 2017
    Posts:
    32
    @Rene-Damm It would be nice to get an Update for the problem, which is shown in the video from @Wokky.
    It´s an essential feature, which shouldn't be broken for long. ^^
     
    JimmyCushnie likes this.
  10. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,207
    I just ran into this myself. Is there intent to make frame by frame delta input more usable in actions?
     
    Last edited: Sep 28, 2019
  11. Laumania

    Laumania

    Joined:
    Jun 27, 2012
    Posts:
    121
    I'm listening here too...and my mouse is now behaving a little wierd I think in my FPS controller.
     
  12. Laumania

    Laumania

    Joined:
    Jun 27, 2012
    Posts:
    121
    @Rene-Damm Any update to this issue? Or work arounds? Seems like a pretty big thing not working properly, as "mouse" is kind of a populate controller for games :)
     
    JimmyCushnie likes this.
  13. Laumania

    Laumania

    Joined:
    Jun 27, 2012
    Posts:
    121
    By the way, I'm not using mouse input as events, I'm polling and still it works very wierd and not at all like the old input. It's clearly depending on the DPI/Resolution or something like that, which is not ideal.

    Code (CSharp):
    1. private void Update()
    2. {
    3. LookDelta  = _defaultControls.Player.Look.ReadValue<Vector2>() * Time.deltaTime;
    4. }
     
  14. Rene-Damm

    Rene-Damm

    Unity Technologies

    Joined:
    Sep 15, 2012
    Posts:
    686
    Hey guys, sorry for the huge lag here.

    @Wokky Could you file a ticket with the Unity bug reporter using that setup you have there? Would like to take a closer look at that.

    I did some quick some testing and even trying to account for the various scaling/alterations that the old input manager applies (even if you do GetAxisRaw), while I see a difference in magnitude, I don't see grave differences in behavior. Still, we've seen enough reports about wonky behavior with mouse delta input to warrant a close examination.

    upload_2019-10-8_18-20-54.png

    As far as actions bound to deltas are concerned, I'm not sure yet how to most elegantly solve that. Problem is most often it *does* make most sense to tap the accumulated delta (such as when you do a polled ReadValue in Update or when you just store the latest value in an action callback) but sometimes it doesn't (such as when you really just want motion updates one by one). One potential way of solving it is having two controls for each delta-style control (i.e. position and scroll deltas): one that accumulates and one that doesn't. Then in bindings, one can decide which to tap.

    Just to note that, in the old input system, you *always* get an accumulated value. I.e. "Mouse X" and "Mouse Y" are always accumulated frame to frame. (at least on Windows; haven't checked the myriads of other platforms)

    ////EDIT: Just for reference, this is the test script I used above. (relying on Squiggle for graphing)

    Code (CSharp):
    1. public class MouseDeltaTest : MonoBehaviour
    2. {
    3.     private InputAction m_Action;
    4.  
    5.     void Start()
    6.     {
    7.         m_Action = new InputAction(type: InputActionType.Value, binding: "<Mouse>/delta");
    8.         m_Action.Enable();
    9.     }
    10.  
    11.     void Update()
    12.     {
    13.         var vertical = Input.GetAxis("Mouse Y");
    14.         var horizontal = Input.GetAxis("Mouse X");
    15.         var delta = m_Action.ReadValue<Vector2>();
    16.  
    17.         // Account for scaling applied directly in Windows code by old input system.
    18.         delta *= 0.5f;
    19.  
    20.         // Account for sensitivity setting on old Mouse X and Y axes.
    21.         delta *= 0.1f;
    22.  
    23.         DebugGraph.Log("Old", new Vector2(horizontal, vertical));
    24.         DebugGraph.Log("New", delta);
    25.     }
    26. }
    27.  
     
    JimmyCushnie likes this.
  15. Laumania

    Laumania

    Joined:
    Jun 27, 2012
    Posts:
    121
    @Rene-Damm Well, the reason you might not see an issue is because you have multiplying with these magic numbers? :D

    Code (CSharp):
    1. // Account for scaling applied directly in Windows code by old input system.
    2.         delta *= 0.5f;
    3.         // Account for sensitivity setting on old Mouse X and Y axes.
    4.         delta *= 0.1f;

    I was just creating a demo project, when I discovered that my problem was a combination of two things:

    1. I were missing your magic values
    2. I were, for some reason, multiplying by Time.deltaTime.

    Multiplying by magic values and removing the Time.deltaTime solved my issue it seems.


    Code (CSharp):
    1.  var lookDeltaRaw = _defaultControls.Player.Look.ReadValue<Vector2>();
    2.                
    3.                 // Account for scaling applied directly in Windows code by old input system.
    4.                 lookDeltaRaw *= 0.5f;
    5.                
    6.                 // Account for sensitivity setting on old Mouse X and Y axes.
    7.                 lookDeltaRaw *= 0.1f;
    8.  
    9.                 return lookDeltaRaw;
     
  16. Laumania

    Laumania

    Joined:
    Jun 27, 2012
    Posts:
    121
    ...and after that I actually ended up removing the "magic values" and just adjust my sensitivity on my FirstPersonController to fit.

    Seems to work...so right now it looks like my mistake was to * Time.deltaTime...just if others get here too with same wierd behavior.
     
  17. Rene-Damm

    Rene-Damm

    Unity Technologies

    Joined:
    Sep 15, 2012
    Posts:
    686
    @Laumania If the problem is scaling of values, that'll be easy to correct e.g. by applying processors. We can certainly tweak that on the managed side. ATM the deltas are served pretty much raw from the platform.

    However, problems like the one illustrated by @Wokky seem to go deeper and hint at differences of deltas not just in magnitude but in distribution over time. If that is indeed the case, scaling alone unfortunately won't do the trick.
     
  18. Laumania

    Laumania

    Joined:
    Jun 27, 2012
    Posts:
    121
    Yeah I actually know about the scale, but didn't added that, just adjusted sensitivity. But as it's down to 0,2 for me right now...I might want to apply some scale, to have a better range of sensitivity ingame.

    At least I don't have an issue anymore with the mouse input in the new input system. I'll let you know if I run into problems :)