Search Unity

Fake mouse position in 4.6 UI (ANSWERED)

Discussion in 'UGUI & TextMesh Pro' started by OpticalOverride, Dec 4, 2014.

  1. OpticalOverride

    OpticalOverride

    Joined:
    Jan 13, 2013
    Posts:
    161
    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:
    161
    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:
    20
    Great ,But not working on unity 5.2 above.
     
  5. dagon

    dagon

    Joined:
    Jan 4, 2013
    Posts:
    20
    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:
    24
    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:
    161
    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. Deleted User

    Deleted User

    Guest

    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. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    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?
     
    AbgaryanFX likes this.
  12. kauaiBlake

    kauaiBlake

    Joined:
    Feb 28, 2013
    Posts:
    36
    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:
    30
    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).
     
  14. k-ina

    k-ina

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

    thornebrandt

    Joined:
    Mar 22, 2015
    Posts:
    15

    I'm trying to use this in 2019 version and it's incredibly jerky. Both in an edit view and the build. Is anyone else having this problem ?
     
  16. Daahrien

    Daahrien

    Joined:
    Dec 5, 2016
    Posts:
    100
    Did you get it to work?
     
    AlexYoung1469 likes this.
  17. aw85

    aw85

    Joined:
    Jul 26, 2021
    Posts:
    3
    I am too, having this problem. I am still new around Unity and I am using the free Modular First Person Controller asset. Any news or update to this?
     
  18. Guillaume-atVolumiq

    Guillaume-atVolumiq

    Joined:
    Sep 14, 2018
    Posts:
    36
    Same, and the StandaloneInputModule trick is not possible anymore with the new Input System
     
  19. Recluse

    Recluse

    Joined:
    May 16, 2010
    Posts:
    485
    Thanks, this helped me greatly - I added an in-world computer terminal to my game and was struggling to get the buttons to respond to the hidden locked cursor. I didn't want the ordinary mouse pointer to be used, like in the pause Menu, but for the player to select via looking at items. This worked perfectly in 2018 using the old input system.
     
  20. Guillaume-atVolumiq

    Guillaume-atVolumiq

    Joined:
    Sep 14, 2018
    Posts:
    36
    nzeman1337 and Recluse like this.
  21. ark_ryl

    ark_ryl

    Joined:
    Jun 26, 2021
    Posts:
    1
    I've tried this and I still can't seem to get it to work. Unless I'm missing something obvious.
    My world space UIs work fine when my cursor is unlocked, but still can't be interacted with when the cursor is locked.

    EDIT: Nevermind, I just realised that my screenspace overlay canvas was blocking the raycasts. If you're having a similar issue and you have a screenspace overlay canvas, make sure you disable the graphics raycaster on that canvas when you need to interact with worldspace UI!
     
    Last edited: Apr 13, 2022
    Guillaume-atVolumiq likes this.
  22. Guillaume-atVolumiq

    Guillaume-atVolumiq

    Joined:
    Sep 14, 2018
    Posts:
    36
    Did you get it to work on the current Input System package or neededd to download the develop one from Github ?
     
  23. chilton

    chilton

    Joined:
    May 6, 2008
    Posts:
    564
    Can't get this to work in 2021. I am able to simulate the click, but the click up event doesn't reset the color of the object.