Search Unity

Fake mouse position in 4.6 UI (ANSWERED)

Discussion in 'Unity UI (uGUI) & TextMesh Pro' started by OpticalOverride, Dec 4, 2014.

  1. OpticalOverride

    OpticalOverride

    Joined:
    Jan 13, 2013
    Posts:
    156
    I've been attempting to figure out how to fabricate mouse input on the new UI all day, and feel like I'm stuck in the mud. As the UI is new, it seems there is close to zero in the way of documentation on the scripting end of the EventSystem. :confused:

    Problem:
    For testing purposes, what I am trying to do is get a scroll bar on a canvas to react to 2 key presses. I have the the scroll bar getting selected, and even firing it's OnDrag event callback (as well as other various callbacks), yet no reaction on the UI end... The goal is to have the 'T' key select the scrollbar, and then while holding the 'S' key, have it scroll downwards. The latter part (dragging the scrollbar) being the what's not working. If anyone has any knowledge of how to accomplish this, or a resource (youtube video, blog post, garbage pale outside Unity HQ for scraps of know-how, anything...) I would greatly appreciate it.

    Apologies in advance for the following mess of code... (been tinkering all day, haven't had time to clean up)

    Some of what I have (explained below):
    Code (CSharp):
    1. // TESTING
    2.         if( Input.GetKeyDown( KeyCode.T ) )
    3.         {
    4.             //PointerEventData data = new PointerEventData( EventSystem.current );
    5.             //ExecuteEvents.Execute( m_scrollBar, data, ExecuteEvents.beginDragHandler );
    6.  
    7.             PointerEventData data = new PointerEventData( EventSystem.current );
    8.             data.selectedObject = m_scrollBar;
    9.             //data.pressPosition = new Vector2( Input.mousePosition.x, Input.mousePosition.y );
    10.             m_pressPos = new Vector2( Input.mousePosition.x, Input.mousePosition.y );
    11.             ExecuteEvents.Execute( m_scrollBar, data, ExecuteEvents.selectHandler );
    12.             ExecuteEvents.Execute( m_scrollBar, data, ExecuteEvents.pointerEnterHandler );
    13.             ExecuteEvents.Execute( m_scrollBar, data, ExecuteEvents.initializePotentialDrag );
    14.             ExecuteEvents.Execute( m_scrollBar, data, ExecuteEvents.pointerDownHandler );
    15.         }
    16.  
    17. if( Input.GetKey( KeyCode.S ) )
    18.         {
    19.             PointerEventData data = new PointerEventData( EventSystem.current );
    20.             //data.position = new Vector2( Input.mousePosition.x, Input.mousePosition.y );
    21.             //data.scrollDelta = new Vector2( Input.mouseScrollDelta.x, Input.mouseScrollDelta.y );
    22.             data.pointerDrag = m_scrollBar;
    23.             data.scrollDelta = new Vector2( 0, -0.1f );
    24.             ExecuteEvents.Execute( m_scrollBar, data, ExecuteEvents.dragHandler );
    25.         }
    26.  
    Both of the above if statements are in an overridden function of the BaseInputModule function, Process(). The script itself inherits from BaseInputModule, and is a component of the EventSystem game object in my scene. I have a second script on the scrollbar itself that simply prints information to the console to determine that the events are being properly sent, callbacks like OnDrag, etc.

    Thanks so much, any help is greatly appreciated!
     
  2. OpticalOverride

    OpticalOverride

    Joined:
    Jan 13, 2013
    Posts:
    156
    Answered my own question today.

    Wow, I was tired last night... There was a much simpler question I could have asked: How to give a fake position for the mouse cursor in the new UI in 4.6.

    For anyone who is looking to simulate mouse movement in the new UI (give the mouse a fake position), here's the code:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class VRInputModule : StandaloneInputModule {
    5.  
    6.     private Vector2 m_cursorPos;
    7.  
    8.     // Hand this function your fake mouse position (in screen coords)
    9.     public void UpdateCursorPosition( Vector2 a_pos )
    10.     {
    11.         m_cursorPos = a_pos;
    12.     }
    13.  
    14.     // NEXT TWO FUNCTIONS (GetPointerData/CopyFromTo)
    15.     // are copied from PointerInputModule source, next update for Unity this likely won't be necessary (As Tim C has made them protected as of this morning (my current version is 4.6.0f3), thank you!), if you have a later version, remove these two functions to receive updates!
    16.     protected bool GetPointerData(int id, out PointerEventData data, bool create)
    17.     {
    18.         if (!m_PointerData.TryGetValue(id, out data) && create)
    19.         {
    20.             data = new PointerEventData(eventSystem)
    21.             {
    22.                 pointerId = id,
    23.             };
    24.             m_PointerData.Add(id, data);
    25.             return true;
    26.         }
    27.         return false;
    28.     }
    29.    
    30.     private void CopyFromTo(PointerEventData @from, PointerEventData @to)
    31.     {
    32.         @to.position = @from.position;
    33.         @to.delta = @from.delta;
    34.         @to.scrollDelta = @from.scrollDelta;
    35.         @to.pointerCurrentRaycast = @from.pointerCurrentRaycast;
    36.     }
    37.  
    38.     // This is the real function we want, the two commented out lines (Input.mousePosition) are replaced with m_cursorPos (our fake mouse position, set with the public function, UpdateCursorPosition)
    39.     private readonly MouseState m_MouseState = new MouseState();
    40.     protected override MouseState GetMousePointerEventData()
    41.     {
    42.         MouseState m = new MouseState();
    43.  
    44.         // Populate the left button...
    45.         PointerEventData leftData;
    46.         var created = GetPointerData( kMouseLeftId, out leftData, true );
    47.  
    48.         leftData.Reset();
    49.  
    50.         if (created)
    51.             leftData.position = m_cursorPos;
    52.             //leftData.position = Input.mousePosition;
    53.  
    54.         //Vector2 pos = Input.mousePosition;
    55.         Vector2 pos = m_cursorPos;
    56.         leftData.delta = pos - leftData.position;
    57.         leftData.position = pos;
    58.         leftData.scrollDelta = Input.mouseScrollDelta;
    59.         leftData.button = PointerEventData.InputButton.Left;
    60.         eventSystem.RaycastAll(leftData, m_RaycastResultCache);
    61.         var raycast = FindFirstRaycast(m_RaycastResultCache);
    62.         leftData.pointerCurrentRaycast = raycast;
    63.         m_RaycastResultCache.Clear();
    64.  
    65.         // copy the apropriate data into right and middle slots
    66.         PointerEventData rightData;
    67.         GetPointerData(kMouseRightId, out rightData, true);
    68.         CopyFromTo(leftData, rightData);
    69.         rightData.button = PointerEventData.InputButton.Right;
    70.  
    71.         PointerEventData middleData;
    72.         GetPointerData(kMouseMiddleId, out middleData, true);
    73.         CopyFromTo(leftData, middleData);
    74.         middleData.button = PointerEventData.InputButton.Middle;
    75.  
    76.         m_MouseState.SetButtonState(PointerEventData.InputButton.Left, StateForMouseButton(0), leftData);
    77.         m_MouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
    78.         m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);
    79.  
    80.         return m_MouseState;
    81.     }
    82.  
    83. }
    Simply attach this script to the EventSystem game object in your scene, and deactivate the StandardInputModule.

    Hope this helps someone else!
     
  3. Dinosaur

    Dinosaur

    Joined:
    Dec 13, 2012
    Posts:
    4
    Thank you for posting!! I wanted to fake a mouse click with another mouse button and tried setting up event triggers on all my buttons, but that solution did not work with scrollbars. I came across your script, swapped middleData for leftData, and success!
     
  4. dagon

    dagon

    Joined:
    Jan 4, 2013
    Posts:
    18
    Great ,But not working on unity 5.2 above.
     
  5. dagon

    dagon

    Joined:
    Jan 4, 2013
    Posts:
    18
    for anyone looking for unity 5.2 above
    fix "protected override MouseState GetMousePointerEventData()"
    to "protected override MouseState GetMousePointerEventData( int id =0)"
     
    Dinosaur and OpticalOverride like this.
  6. TomNCatz

    TomNCatz

    Joined:
    Jan 6, 2015
    Posts:
    12
    also, if you change the first function to the following you don't need to turn off the other input systems for this to work.


    public void UpdateCursorPosition( Vector2 a_pos )
    {
    ActivateModule();
    m_cursorPos = a_pos;
    Process();
    }
     
  7. OpticalOverride

    OpticalOverride

    Joined:
    Jan 13, 2013
    Posts:
    156
    Remembered this post and came back to use my old solution for a new problem (so glad I posted it, never would have found it in my old projects!)

    For anyone who also wants to change the button used to select UI elements (instead of the left mouse button), use the following code as an example (I'm using Joystick1Button0, or the A button on an xbox controller, but you can use any input button you want :) ). Just use this function in place of the original solution's GetMousePointerEventData.

    Code (csharp):
    1.  
    2. // This is the real function we want, the two commented out lines (Input.mousePosition) are replaced with m_cursorPos (our fake mouse position, set with the public function, UpdateCursorPosition)
    3.     private readonly MouseState m_MouseState = new MouseState();
    4.     protected override MouseState GetMousePointerEventData( int id = 0 )
    5.     {
    6.         MouseState m = new MouseState();
    7.  
    8.         // Populate the left button...
    9.         PointerEventData leftData;
    10.         var created = GetPointerData( kMouseLeftId, out leftData, true );
    11.  
    12.         leftData.Reset();
    13.  
    14.         if (created)
    15.             leftData.position = m_cursorPos;
    16.             //leftData.position = Input.mousePosition;
    17.  
    18.         //Vector2 pos = Input.mousePosition;
    19.         Vector2 pos = m_cursorPos;
    20.         leftData.delta = pos - leftData.position;
    21.         leftData.position = pos;
    22.         leftData.scrollDelta = Input.mouseScrollDelta;
    23.         //leftData.button = PointerEventData.InputButton.Left;
    24.         eventSystem.RaycastAll(leftData, m_RaycastResultCache);
    25.         var raycast = FindFirstRaycast(m_RaycastResultCache);
    26.         leftData.pointerCurrentRaycast = raycast;
    27.         m_RaycastResultCache.Clear();
    28.  
    29.         // copy the apropriate data into right and middle slots
    30.         PointerEventData rightData;
    31.         GetPointerData(kMouseRightId, out rightData, true);
    32.         CopyFromTo(leftData, rightData);
    33.         rightData.button = PointerEventData.InputButton.Right;
    34.  
    35.         PointerEventData middleData;
    36.         GetPointerData(kMouseMiddleId, out middleData, true);
    37.         CopyFromTo(leftData, middleData);
    38.         middleData.button = PointerEventData.InputButton.Middle;
    39.  
    40.         // Use the "A" button on the xbox controller for selection instead of the left mouse button
    41.         PointerEventData.FramePressState selectState = PointerEventData.FramePressState.NotChanged;
    42.         if( Input.GetKeyDown( KeyCode.Joystick1Button0 ) )
    43.             selectState = PointerEventData.FramePressState.Pressed;
    44.         else if( Input.GetKeyUp( KeyCode.Joystick1Button0 ) )
    45.             selectState = PointerEventData.FramePressState.Released;
    46.  
    47.         m_MouseState.SetButtonState(PointerEventData.InputButton.Left, selectState, leftData);
    48.         m_MouseState.SetButtonState(PointerEventData.InputButton.Right, StateForMouseButton(1), rightData);
    49.         m_MouseState.SetButtonState(PointerEventData.InputButton.Middle, StateForMouseButton(2), middleData);
    50.  
    51.         return m_MouseState;
    52.     }
    53.  
    Hope this helps someone!
     
    SheCallsMeSloth, Dinosaur and dagon like this.
  8. Viji82

    Viji82

    Joined:
    Aug 16, 2016
    Posts:
    1
    Hi,

    The coordinates passed on to the function should be the coordinates within the Unity editor or the system ?
    I am trying to use this code for performing mouse key press within Unity Editor while running integration tests.
     
  9. lodendsg

    lodendsg

    Joined:
    Sep 1, 2012
    Posts:
    181
    Okay so I am using this bit of code to get around the odd behaviour of Cursor Locked not setting the cursor to centre screen ... this is giving me the click event now like I need however I need to sort out how to simulate the pointer enter and exit events.

    Any ideas?
     
  10. PaulStanos

    PaulStanos

    Joined:
    Jan 28, 2014
    Posts:
    1
    @lodendsg, you may have already found an answer to your question of enter and exit events, but if you haven't here is what I did.

    I found that using the protected method called HandlePointerExitAndEnter I would get the enter and exit events (e.g. button changing color on hover, etc.). The two parameters required are: PointerEventData (I used leftData from the example script that AnimalMother posted), and a GameObject (I used raycast.gameObject, where raycast is what is returned from the FindFirstRaycast method).

    After that everything just seemed to work. I hope this helps!
     
  11. tbr

    tbr

    Joined:
    Oct 23, 2015
    Posts:
    10
    I'm probably going to come across as a little dim here but I'm not sure how to
    I thought perhaps that I create a script and use this is the update,
    Code (CSharp):
    1. CenterMouseInputModule.UpdateCursorPosition(Vector2(Screen.width/2,Screen.height/2);
    But that doesn't seem to work, can anyone point me in the right direction?
     
    Artyom-Abgaryan likes this.
  12. kauaiBlake

    kauaiBlake

    Joined:
    Feb 28, 2013
    Posts:
    34
    To get this code working under 5.6.1 i had to replace the UpdateCursorPosition function with this one

    public override void UpdateModule()
    {
    m_cursorPos = Input.mousePosition;
    }
     
  13. EmmaEwert

    EmmaEwert

    Joined:
    Mar 13, 2014
    Posts:
    25
    As of the UI code in 2017.x, it seems to be enough to just momentarily unlock the cursor, process the input, and then return the cursor to its original lock state.

    Since this thread seems to be where you end up through google, I'll leave the entirety of my FirstPersonInputModule here:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3.  
    4. public class FirstPersonInputModule : StandaloneInputModule {
    5.     protected override MouseState GetMousePointerEventData(int id) {
    6.         var lockState = Cursor.lockState;
    7.         Cursor.lockState = CursorLockMode.None;
    8.         var mouseState = base.GetMousePointerEventData(id);
    9.         Cursor.lockState = lockState;
    10.         return mouseState;
    11.     }
    12.  
    13.     protected override void ProcessMove(PointerEventData pointerEvent) {
    14.         var lockState = Cursor.lockState;
    15.         Cursor.lockState = CursorLockMode.None;
    16.         base.ProcessMove(pointerEvent);
    17.         Cursor.lockState = lockState;
    18.     }
    19.  
    20.     protected override void ProcessDrag(PointerEventData pointerEvent) {
    21.         var lockState = Cursor.lockState;
    22.         Cursor.lockState = CursorLockMode.None;
    23.         base.ProcessDrag(pointerEvent);
    24.         Cursor.lockState = lockState;
    25.     }
    26. }
    Attaching this script instead of the StandaloneInputModule effectively makes the center of the screen work as a UI-interacting cursor, while keeping the actual cursor locked and hidden.

    Enter/Exit (hover) events, as well as everything else, seems to work as expected. I haven't tested the drag functionality, but those three methods above are the only ones that check (and return early) if the cursor is locked, so I imagine the entire UI "just works".

    While this approach seems a little hacky, it utilises the existing PointerInputModule instead of overriding it. Hopefully this means any beneficial changes to the built in Unity UI code won't break the script above (as seems to have happened a couple of times with the other scripts and suggestions in this thread).
     
    k-ina, ChronoWS, nicloay and 3 others like this.
  14. k-ina

    k-ina

    Joined:
    May 17, 2017
    Posts:
    2
    You helped me A LOT!!! Thank you