Search Unity

Question: How do I detect if the mouse is over any UI element? [UI Elements]

Discussion in 'UI Toolkit' started by erlapso, Feb 16, 2020.

  1. erlapso

    erlapso

    Joined:
    Apr 7, 2019
    Posts:
    6
    In the old Unity UI I would have
    `EventSystem.current.IsPointerOverGameObject()`
    and that would work in stuff like (pseudo code follows)
    `getbuttondown("fire1") if ( EventSystem.current.IsPointerOverGameObject() ) } do nothing { else } `
    Now im trying to switch to UIElements - which looks awesome - but I really need to be able to tell if the user is clicking on the UI or somewhere else. How do I do that? I could not find it in the docs
     
    OMGOMGXAXA likes this.
  2. VOTRUBEC

    VOTRUBEC

    Joined:
    Dec 17, 2014
    Posts:
    76
    Do you want to know when the mouse enters and leaves the VisualElement, or when the MouseUpEvent/MouseDownEvents occur?

    Depending on your need, one of these should do the trick:

    Code (CSharp):
    1. yourElement.RegisterCallback<MouseEnterEvent> ( x => OnMouseEnter ( x ) );
    2. yourElement.RegisterCallback<MouseLeaveEvent> ( x => OnMouseLeave ( x ) );
    3. yourElement.RegisterCallback<MouseUpEvent> ( x => OnMouseUp ( x ) );
     
    jGate99 likes this.
  3. PMacDev

    PMacDev

    Joined:
    Sep 8, 2020
    Posts:
    7
    This would work for a one-off, but what if you would want to do this for ALL of the elements, so essentially checking if the player is clicking a UI Element or trying to interact with the game?
     
  4. Arkenhammer

    Arkenhammer

    Joined:
    Nov 10, 2019
    Posts:
    27
    What I did was create my own custom background element for each UI panel which goes over my game; those panels register/unregister themselves in a list of UI areas (via an interface). Then when my game gets a mouse click it runs through each of the areas to see if the mouse is in a UI area and, if so, it rejects the click and lets the UI Event system handle it. It's not pretty, particularly for panels which move or animate but, for my game, it got me the enough to replace the old "IsMouseOverUI" functionality.

    Note: MouseEnterEvent and MouseLeaveEvent do not solve this problem because the mouse will "Leave" the background when it enters an interactable child element. Adding mouse enter/leave to 100s of elements in the UI is not really an option so rectangle testing it is.
     
  5. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,065
    For the record, we will have proper Panel layering and event handling between UI Toolkit Panels, UGUI Panels, and the game view in 2021.1. Should hopefully reduce the need for the creative but legitimate solutions in this thread.
     
    craftsmanbeck likes this.
  6. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    202
    I am at this point right now with 2021.1. Here you cannot click through from one ui document to another anymore, and also not on to UGUI.
    The Sort order also works now with UGUI.

    But I still don't see an option to detect if the mouse is over the UI without a detection-Panel which reacts to MouseEnter and MouseLeave.
    And doing that with uitoolkit only can detect if the mouse is on that particular uidocument. If there are more layered this does not work.
    What works is having a UGUI Canvas behind sorted behind all the UIToolkit-documents and detect there if the mouse is over the ui or not.
    But this method has it's own problems because now, the detection-Panel blocks events.

    Systems I knew solved this by raycasting and detecting if the raycast was blocked by the "UI" layer. This seems not possible with UIToolkit.

    Or do I miss something important?
     
  7. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    202
    And it does not work in builds! My canvas UI detection mechanism works in the editor but not in build... :(

    Edit:
    Hm I have some other UGUI panels in front of that ui detector panel. It seams one of those panels blocks the detector in build but not in editor
     
    Last edited: May 18, 2021
  8. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    475
    Hi, on 2021 you should simply be able to use the EventSystem.IsPointerOverGameObject method to detect if the mouse is over any UI (both UI Toolkit and UGUI). This requires having an EventSystem in the scene.
     
    manuelgoellnitz likes this.
  9. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    202
    Oh yes that works! Many thanks
    I tried it before, but probably something else in my code broke it :)
     
  10. whiteclouds2016

    whiteclouds2016

    Joined:
    Sep 22, 2017
    Posts:
    18
    @manuelgoellnitz Hi, bro, When I use "EventSystem.current.IsPointerOverGameObject()",it always return false.
    It is any trick for it?
     
    Kazko likes this.
  11. Kazko

    Kazko

    Joined:
    Apr 2, 2014
    Posts:
    55
    EventSystem.current.IsPointerOverGameObject() return True for me, most of the time. UITK panel seems to not block the pointer being over GO, and also it seems it needs the EventSystem component, so when I use InputSystemEventSystem(UITookkit) component, there is a warning that only one event system can be active.

    I found a solution in another thread yesterday. https://forum.unity.com/threads/using-new-input-system-with-ui-toolkit.1026847/

    It's a hacky one, and one that probably requires you to use a single UI Document. I transitioned from using multiple UIDocs to a single one couple of weeks ago because of another problem, and I suggest this is the way you do it. If you do, then you can create a VisualElement master in your main Document and then place all your other UI inside the master. So then if you do (somewhere on Start or Awake) the following:

    Code (CSharp):
    1. MasterVisualElement.RegisterCallback<PointerOverEvent>((evt) =>
    2.             {
    3.                 OverUI = true;
    4.             });
    5.            
    6.             MasterVisualElement.RegisterCallback<PointerOutEvent>((evt) =>
    7.             {
    8.                 OverUI = false;
    9.             });
    Where OverUI is some static bool, you are now able to check this bool and if it's true, exit from your other input operations. This would work the best if your UI is static on screen. Mine wasn't, so there were couple of problems with showing/hiding UI panels (for example an options menu).

    I used to show/hide panels using style.opacity, because using Flex/None or transform.scale Vector3.Zero/One was causing the UI to lose some of the layout. But when using opacity, the UI is still there, so this hack won't work. I tried modifying the picking mode to Ignore along with hiding the panel, but that did not work either. I had to reposition the panel outside of screen (for example style.top = Screen.height * 2) for this to work.

    It works, but it all seems so hacky and buggy for such a trivial thing and I can't wait for this to be fixed. Someone if reading this, please perhaps suggest some improvements.
     
  12. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    202
    Dion't use the EventSystem of UIToolkit but the Unity Standard one. And only have one EventSystem in the scene.
     
  13. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    202
    I also tried this solution. But it has one flaw: I didn't try "PointeroutEvent" but "PointerLeaveEvent". And That Event is not fired when the user has the app in Window mode and moves the mouse from a UI element outside of the Window.
     
  14. whiteclouds2016

    whiteclouds2016

    Joined:
    Sep 22, 2017
    Posts:
    18
    OK, I have fixed my problem and shared why it always return false
    1.You must add the Standard EventSystem as what @manuelgoellnitz suggested
    2.Set the "PickingMode.Ignore" even thought the panel include nothing but itself. It will broke the mouse event.

    FYI
     
    Leslie-Young likes this.
  15. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,109
    Unity, is there an official solution to this?

    Using Component/Event/EventSystem result on not being able to use Component/UIToolkit/InputSysteEventSystem (UI Toolkit) (Unity throw warning about there being two event systems). The controls does not work correctly if I do not use the UI Toolkit specific component. I could for example not drag a slider, only click to change its position. But not using EventSystem (non UI Toolkit) causes that one can not use EventSystem.current.IsPointerOverGameObject().

    [Edit] I see that EventSystem.current.IsPointerOverGameObject(), and my Slider, works despite the warning of having two event systems in the scene. This feels all so messy but I guess it is what we have to live with while UIToolkit is in "preview".

    Now to figure out why TwoPaneSplitView is causing pickingmode=ignored to be ... ignored.
     
    Last edited: May 30, 2021
  16. uBenoitA

    uBenoitA

    Unity Technologies

    Joined:
    Apr 15, 2020
    Posts:
    107
    Leslie, I'm very sorry for the situation of [EventSystem + UI Toolkit + new Input] not working fully at the moment. We have specific feature cutoff dates and one of the pieces to the puzzle got in while a second piece got left out, in the last major feature cutoff. This is no excuse, but I do feel bad about the current situation. You have the right idea though: this situation is temporary, a fix should be our shortly in the Input System package for UI Toolkit compatibility with the EventSystem component.
     
  17. bloodthirst69

    bloodthirst69

    Joined:
    Oct 1, 2017
    Posts:
    14
    I dunno if this is what ur looking for , but this one worked for me :

    Code (CSharp):
    1.         /// <summary>
    2.         /// Is mouse inside the rect of a visual element ?
    3.         /// </summary>
    4.         /// <param name="pos">The world space position of the mouse (evt.position)</param>
    5.         /// <returns></returns>
    6.         private bool IsInsideElement(VisualElement v , Vector2 pos)
    7.         {
    8.                 return v.worldBound.Contains(pos);
    9.         }
    10.  
     
    OMGOMGXAXA and Kazko like this.
  18. CoryMaklin

    CoryMaklin

    Joined:
    Oct 10, 2020
    Posts:
    28
    How can we check whether the mouse is over a specific element? I have an inventory and equipment UI, and I've been using worldBound.Contains for drag and drop functionality. However, when one UI is behind the other, I need a way of determining which is in front prior to checking whether the item being dragged overlaps with the slots.
     
  19. bloodthirst69

    bloodthirst69

    Joined:
    Oct 1, 2017
    Posts:
    14
    This depends on your uxml structure , for example if u have something like
    <parent>
    <inventory>
    <equipement>
    </parent>

    then u can just use inventoryVisualElement.parent.IndexOf(inventoryVisualElement) or equipementVisualElement.parent.IndexOf(equipementVisualElement) to get who's in front , if u want to change the order of who's in front you can use the BringToFront() and SendToBack() methods.

    if your hierarchy is more complex then you'll need to find their common ancestor in the hiararchy (aka their first parent element) and under that -let's call it "A" , then you find out the closest distinct parent of each under "A" element.
    then you use the index of those two to figure out who's in front

    Hope this helps
     
  20. CoryMaklin

    CoryMaklin

    Joined:
    Oct 10, 2020
    Posts:
    28
    Hey! That does help! However, how would you use that method if you had multiple UI Documents?
     
  21. bloodthirst69

    bloodthirst69

    Joined:
    Oct 1, 2017
    Posts:
    14
    By UI Documents you mean mutliple EditorWindows ? cuz multiple UI Documents != multiple EditorWindowsv.
    if you mean multiple editor windows open , then i would start with this (i am just suggesting here , this is just where i would start and not tested)
    - first i would register them in a static list to keep a list of the opens windows.
    - Use the "OnFocus" unity special method to shuffle the list according to their order on screen
    - if that doesn't work , i would look into "GUI.depth" which shows the current window's depth (careful , this used IMGUI and no UI Toolkit , so make sure to use the IMGUIContainer thingy)
    - every editorWindow has a "position" property , it's the window's top-left corner postion in screenSpace , the rest of the elements in the window are in "local space" of the window.
    - so if i wanted to test if elements (A and B for example) are on different windows are overlapping , i would basically
    do something like :
    Vec2 A_pos_In_screenSpace = A_position_in_its_own_window_local_space => A_pos_in_screen_space (based on the window's pos)

    Vec2 B_pos_In_screenSpace = B_position_in_its_own_window_local_space => B_pos_in_screen_space (based on the window's pos)

    do the comparison now since they are in the same space.

    some like that
     
unityunity