Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Detect when MousePosition in a RectTransform

Discussion in 'UGUI & TextMesh Pro' started by Tetsubo, Oct 15, 2017.

  1. Tetsubo

    Tetsubo

    Joined:
    Jun 26, 2014
    Posts:
    40
    Hello Unity Community,

    in my 2d game I am trying to find out when is Input.mousePosition on a UI Image. I want to do that by checking out if Input.mousePosition coordinates are inside the borders given by RectTransform.rect.xMin, .xMax, .yMin, .yMax:

    if (Input.mousePosition.x < (imgRectTransform.position.x + imgRectTransform.rect.xMax)
    && Input.mousePosition.x > (imgRectTransform.position.x + imgRectTransform.rect.xMin)
    && Input.mousePosition.y <(imgRectTransform.position.y + imgRectTransform.rect.yMax)
    && Input.mousePosition.y > (imgRectTransform.position.y + imgRectTransform.rect.yMin))
    return true;


    This boolean expression is true when I click over the image, but it is also true when i click "quite a bit" outside of the image. "Quite a bit" is, as testing shows me, depending on the screen size - from a couple of pixels for larger screen to double size of the Image for smaller screens.. And I don't know why exactly?..

    What I figured just out - it has to do with with the canvasScaler - which is set to "Scale With Screen Size" with Full HD as reference resolution and "Match Width Or Height" = 1. Canvas.scaleFactor gives me 1.. So what am I missing in the calculation?

    Any opinions and suggestions are very welcome!
     
    Last edited: Oct 15, 2017
  2. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    816
  3. Tetsubo

    Tetsubo

    Joined:
    Jun 26, 2014
    Posts:
    40
    First of all - thank you for the answer, @Johannski

    For this to work (which really works), the event system and Graphic Raycaster must be enabled. For performance reasons I don't want the Raycaster to be enabled. In other words, checking if a click is on button only when a touch / mouse click is made with a simple boolean comparison of floats is more performant then letting a raycaster spawn a ray every frame to all object that are raycast targets (in my UI I have more buttons than the mentioned one). Also I do cache all the values to be checked in temp variables -> no new objects created internally -> GC called rarely.
     
  4. jeffweber

    jeffweber

    Joined:
    Dec 17, 2009
    Posts:
    616
  5. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    816
    I don't think that is what Tetsubo wants to use either if performance is important. Have a look at the decompiled version: https://github.com/MattRix/UnityDec...ityEngine/UnityEngine/RectTransformUtility.cs

    It creates rays (garbage) and tests those rays, thus it is not that different if not worse than using a graphics raycaster.

    Maybe try this instead:
    Code (CSharp):
    1. var mousePosition = Input.mousePosition;
    2.             var normalizedMousePosition = new Vector2(mousePosition.x / Screen.width, mousePosition.y / Screen.height);
    3.             if (normalizedMousePosition.x > rectTransform.anchorMin.x &&
    4.                 normalizedMousePosition.x < rectTransform.anchorMax.x &&
    5.                 normalizedMousePosition.y > rectTransform.anchorMin.y &&
    6.                 normalizedMousePosition.y < rectTransform.anchorMax.y)
    7.             {
    8.                 // Hit the rect transform
    9.             }
     
    XYHC and Tetsubo like this.
  6. Tetsubo

    Tetsubo

    Joined:
    Jun 26, 2014
    Posts:
    40
    @jeffweber , thanks mate! That works pretty well and is the shortest way to that kind of checks.. as @Johannski pointer out though, this is not that performant as it creates quite a bit of garbage..

    I will test tomorrow the suggested solution by @Johannski , it looks really very promising! Thanks a lot!
     
  7. The_Mean_Fiddler

    The_Mean_Fiddler

    Joined:
    Nov 27, 2014
    Posts:
    17
    This works for me:


    Code (CSharp):
    1. Vector2 localMousePosition = imgRectTransform.InverseTransformPoint(Input.mousePosition);
    2.         if (imgRectTransform.rect.Contains(localMousePosition))
    3.         {
    4.             return true;
    5.         }
     
  8. naviln

    naviln

    Joined:
    Oct 18, 2016
    Posts:
    32
    THANKYOU!! This works with Input.GetTouch(0).position as well, exactly what I needed.
    Legend mate, thanks.
     
    NinjaISV and roointan like this.
  9. abbabon

    abbabon

    Joined:
    Mar 19, 2018
    Posts:
    5
    Legendary tip, even 3 years into the future. Thank mate
     
    dean0 likes this.
  10. gili_unity

    gili_unity

    Joined:
    Jan 2, 2019
    Posts:
    9
    You can also use the following, which is helpful in case you change the camera used:
    Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, Camera.main.farClipPlane));
     
  11. aaonurdemir

    aaonurdemir

    Joined:
    May 27, 2016
    Posts:
    3
    For Screen Space - Overlay:
    RectTransformUtility.RectangleContainsScreenPoint(wordRect, Input.mousePosition);

    For Screen Space - Camera:
    RectTransformUtility.RectangleContainsScreenPoint(wordRect, Input.mousePosition, Camera.main);
    or the camera that you used.
     
  12. teh1archon

    teh1archon

    Joined:
    Nov 28, 2014
    Posts:
    12
    It didn't quite work for me but my situation is maybe a bit different. I need to check if the mouse is over a button (thus having a RectTransform) but due to all sorts of changes and filtering events on the canvas the button itself is not clickable with OnClick since it's Image component is turned off (but still on the object). Because of that, I couldn't use all the IsPointerEnter and his friends and had to go more basic.

    Also, I don't use Overlay or World camera so this is probably why the method above doesn't work. This is how to do it with Screen Space Camera canvas:

    Code (CSharp):
    1.            
    2. Vector2 localPoint;
    3. RectTransformUtility.ScreenPointToLocalPointInRectangle(_rectTransfiorm, Input.mousePosition, Camera.main, out localPoint);
    4.             return _rectTransfiorm.rect.Contains(localPoint);
     
    XYHC, efge, itsnottme and 1 other person like this.
  13. UDN_5c806b49-d8a0-4f67-a296-c12c91aa7396

    UDN_5c806b49-d8a0-4f67-a296-c12c91aa7396

    Joined:
    Jan 9, 2017
    Posts:
    151
    This code works beautifully but is there a way to give some "padding" to the recttransfrom in this code? Meaning something like: if mouse is within rectangle bounds + 100px padding
     
  14. jlauener

    jlauener

    Joined:
    Apr 24, 2019
    Posts:
    1
    FYI rays are not "garbage" that need to be GCed. As they are struct they are copied on the stack instead of being allocated in the heap. Like Vector3, Vector3, Rectangle, etc..
     
  15. brian-nielsen

    brian-nielsen

    Joined:
    Apr 18, 2018
    Posts:
    15
    So clean. So smooth. Thank you :)