Check if UI elements/RectTransform are overlapping
Asked Answered
P

3

5

I'd like to know how I check whether two UI Panels on my Unity Canvas are overlapping each other.

Currently I am doing this by comparing the canvas elements Rects

Canvas Settings

  • Render Mode: Screen Space - Camera
  • Pixel Perfect: [Yes]
  • Render Camera: Main Camera
  • Plane Distance: 100
  • Sorting Layer: Default
  • Order In Layer: 0

Canvas Scaler Settings

  • UI Scale Mode: Constant Pixel Size
  • Scale Factor: 1
  • Reference Pixels Per Unit: 100

Code I am using to check

[Header("Check For Overlap")]
public RectTransform PlayerBar;
public RectTransform LeftBar;
public Rect RectOne;
public Rect RectTwo;
public bool overlapping;

//Check if the two canvas element Rects overlap each other

public void CheckForOverlap()
{
    overlapping = false;
    // Convert Canvas RectTransforms to World Rects
    RectOne = GetWorldRect(LeftBar);
    RectTwo = GetWorldRect(PlayerBar);

    if (RectOne.Overlaps(RectTwo))
    {
        overlapping = true;
    }
}


public Rect GetWorldRect(RectTransform rt)
{
    //  Get World corners, take top left
    Vector3[] corners = new Vector3[4];
    rt.GetWorldCorners(corners);
    Vector3 topLeft = corners[0];

    // Rect Size ... I'm not sure if this is working correctly?
    Vector2 size = new Vector2(rt.rect.size.x, rt.rect.size.y);
    return new Rect(topLeft, size);
}

What happens

'Overlapping' bool instantly changes to true.

The Rect One returns as (example)

X -7.5, Y 2.5 W 98.5, H 164.1667

Playtime answered 4/2, 2017 at 16:58 Comment(0)
S
8

Convert the RectTransform to Rect then check if it overlaps.

Here is a simple function that can do that:

bool rectOverlaps(RectTransform rectTrans1, RectTransform rectTrans2)
{
    Rect rect1 = new Rect(rectTrans1.localPosition.x, rectTrans1.localPosition.y, rectTrans1.rect.width, rectTrans1.rect.height);
    Rect rect2 = new Rect(rectTrans2.localPosition.x, rectTrans2.localPosition.y, rectTrans2.rect.width, rectTrans2.rect.height);

    return rect1.Overlaps(rect2);
}

Usage:

public RectTransform uiRect1;
public RectTransform uiRect2;

void Update()
{
    if (rectOverlaps(uiRect1, uiRect2))
    {
        Debug.Log("Overlaps");
    }else
    {
        Debug.Log("Does not Overlap");
    }
}

Even better, make it an extension method:

public static class ExtensionMethod
{
    public static bool rectOverlaps(this RectTransform rectTrans1, RectTransform rectTrans2)
    {
        Rect rect1 = new Rect(rectTrans1.localPosition.x, rectTrans1.localPosition.y, rectTrans1.rect.width, rectTrans1.rect.height);
        Rect rect2 = new Rect(rectTrans2.localPosition.x, rectTrans2.localPosition.y, rectTrans2.rect.width, rectTrans2.rect.height);

        return rect1.Overlaps(rect2);
    }
}

Now, you can do

public RectTransform uiRect1;
public RectTransform uiRect2;

void Update()
{
    if (uiRect1.rectOverlaps(uiRect2))
    {

    }

    //OR

    if (uiRect2.rectOverlaps(uiRect1))
    {

    }
}
Sterling answered 4/2, 2017 at 18:59 Comment(5)
Thanks a lot, this was really useful. Especially the added tip for an extension methodPlaytime
RectTransform already has rect object, wouldn't using it be less memory expensive?Warehouse
This only works if both objects have the same parent. How would you do this if this is not a guarantee?Narcotism
I don't see any projection in screen coordinates - you are only ignoring the z coordinate. This should projected first to screen coordinates.Mb
does not work if parent is anchored elswhereAdamo
B
9

Updated version considering scale of the rectTransform.

public static class RectTransformExtensions
{

    public static bool Overlaps(this RectTransform a, RectTransform b) {
        return a.WorldRect().Overlaps(b.WorldRect());
    }
    public static bool Overlaps(this RectTransform a, RectTransform b, bool allowInverse) {
        return a.WorldRect().Overlaps(b.WorldRect(), allowInverse);
    }

    public static Rect WorldRect(this RectTransform rectTransform) {
        Vector2 sizeDelta = rectTransform.sizeDelta;
        float rectTransformWidth = sizeDelta.x * rectTransform.lossyScale.x;
        float rectTransformHeight = sizeDelta.y * rectTransform.lossyScale.y;

        Vector3 position = rectTransform.position;
        return new Rect(position.x - rectTransformWidth / 2f, position.y - rectTransformHeight / 2f, rectTransformWidth, rectTransformHeight);
    }
}
Beaufort answered 19/9, 2018 at 15:56 Comment(2)
this script is so much better in my testing. it detects very well. thank you.Boredom
This doesn't work when anchors are not in the same position because sizeDelta will be zero.Fellini
S
8

Convert the RectTransform to Rect then check if it overlaps.

Here is a simple function that can do that:

bool rectOverlaps(RectTransform rectTrans1, RectTransform rectTrans2)
{
    Rect rect1 = new Rect(rectTrans1.localPosition.x, rectTrans1.localPosition.y, rectTrans1.rect.width, rectTrans1.rect.height);
    Rect rect2 = new Rect(rectTrans2.localPosition.x, rectTrans2.localPosition.y, rectTrans2.rect.width, rectTrans2.rect.height);

    return rect1.Overlaps(rect2);
}

Usage:

public RectTransform uiRect1;
public RectTransform uiRect2;

void Update()
{
    if (rectOverlaps(uiRect1, uiRect2))
    {
        Debug.Log("Overlaps");
    }else
    {
        Debug.Log("Does not Overlap");
    }
}

Even better, make it an extension method:

public static class ExtensionMethod
{
    public static bool rectOverlaps(this RectTransform rectTrans1, RectTransform rectTrans2)
    {
        Rect rect1 = new Rect(rectTrans1.localPosition.x, rectTrans1.localPosition.y, rectTrans1.rect.width, rectTrans1.rect.height);
        Rect rect2 = new Rect(rectTrans2.localPosition.x, rectTrans2.localPosition.y, rectTrans2.rect.width, rectTrans2.rect.height);

        return rect1.Overlaps(rect2);
    }
}

Now, you can do

public RectTransform uiRect1;
public RectTransform uiRect2;

void Update()
{
    if (uiRect1.rectOverlaps(uiRect2))
    {

    }

    //OR

    if (uiRect2.rectOverlaps(uiRect1))
    {

    }
}
Sterling answered 4/2, 2017 at 18:59 Comment(5)
Thanks a lot, this was really useful. Especially the added tip for an extension methodPlaytime
RectTransform already has rect object, wouldn't using it be less memory expensive?Warehouse
This only works if both objects have the same parent. How would you do this if this is not a guarantee?Narcotism
I don't see any projection in screen coordinates - you are only ignoring the z coordinate. This should projected first to screen coordinates.Mb
does not work if parent is anchored elswhereAdamo
S
0

This from the first answer (thank you!) but I updated so that it works regardless of where the pivot is.

public static class RectTransformExtensions
{
    
        public static bool Overlaps(this RectTransform a, RectTransform b) {
            return a.WorldRect().Overlaps(b.WorldRect());
        }
        public static bool Overlaps(this RectTransform a, RectTransform b, bool allowInverse) {
            return a.WorldRect().Overlaps(b.WorldRect(), allowInverse);
        }

        public static Rect WorldRect(this RectTransform rectTransform)
        {
            Vector2 sizeDelta = rectTransform.sizeDelta;
            Vector2 pivot = rectTransform.pivot;
            
            float rectTransformWidth = sizeDelta.x * rectTransform.lossyScale.x;
            float rectTransformHeight = sizeDelta.y * rectTransform.lossyScale.y;

            //With this it works even if the pivot is not at the center
            Vector3 position =rectTransform.TransformPoint(rectTransform.rect.center);
            float x = position.x - rectTransformWidth * 0.5f;
            float y = position.y - rectTransformHeight * 0.5f;
            
            return new Rect(x,y, rectTransformWidth, rectTransformHeight);
        }
    
}
Sidneysidoma answered 2/6, 2022 at 2:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.